Back to home

JohnXu22786

skill-manager

dsh plugin: multi-zone skill discovery, progressive disclosure, creation wizard, audit and statistics for DeepSeek Harness

Stars
0
Language
Python
Created
Aug 16, 2026
Updated
Aug 16, 2026

Introduction

简体中文

repertoire — Skill Repertoire Library

dsh (DeepSeek harness) plugin: skill discovery, loading, creation and statistics.

  • Multi-level directory auto-discovery — recursively discovers skills from three zones (project zone / user zone / plugin-bundled zone), with explicit priority and visible conflicts
  • Progressive disclosure — directory index (name + purpose) → skill body (loaded on demand) → attached resources (read/run on demand), three tiers of exposure with controlled cost
  • Creation wizard — one command scaffolds a canonical skill skeleton, self-checked on generation, so new skills are clean from birth
  • Validation and statistics — strong validation at discovery time (naming / metadata); full audit on audit / check / forge; a runtime ledger records scanning, loading, running, creation and other activity
  • Zero dependencies — pure Python standard library (≥3.10), the directory itself is the plugin, no third-party packages required
skill catalog
    │  discovered by zone priority: workspace > personal > bundled > extra-*
    ▼
skill card ─────────────► three-tier disclosure
  ├ index: name + description + size (always visible)
  ├ body: full SKILL.md text (loaded on demand, can be forced)
  └ attachments: scripts/ references/ assets/ (read or run on demand)

Directory structure

skill-manager/
├── manifest.json            # plugin manifest (the harness learns about this plugin; repertoire/manifest.json is the bundled copy)
├── repertoire/              # the plugin itself (zero-dependency Python package)
│   ├── plugin.py            # entry: create_plugin() / tools / events
│   ├── forage.py            # discovery engine (zone scanning and conflict resolution)
│   ├── frontmatter.py       # metadata parsing (YAML subset)
│   ├── card.py              # skill card model and state
│   ├── audit.py             # validation rules (fatal / warning)
│   ├── disclosure.py        # three-tier progressive disclosure implementation
│   ├── atelier.py           # creation wizard (minimal / standard templates)
│   ├── ledger.py            # runtime statistics ledger
│   ├── io_bridge.py         # JSON-lines stdio protocol
│   ├── cli.py               # command-line entry
│   ├── config.py            # configuration model
│   ├── manifest.json        # bundled manifest copy (provides system.manifest after pip install)
│   └── skills/              # bundled example skills (bundled zone, shipped with the package)
│       ├── change-log-scribe/ #   example: changelog-writing skill (with scripts)
│       └── skill-smith/       #   example: skill-writing conventions (a meta-skill)
├── examples/                # integration examples (harness demo + protocol session)
└── tests/                   # unit tests (zero-dependency, unittest)

Installation

Installing in DSH

dsh plugin --profile demo add github:JohnXu22786/skill-manager

Zero dependencies, either of two ways:

# Way 1: use directly (no install needed)
python -m repertoire --help          # run from the plugin directory

# Way 2: install as a command (optional)
pip install -e .
repertoire --help

Quick start

# List all skills (index level: name / purpose / source / size)
python -m repertoire list

# Load the full body of a skill
python -m repertoire open change-log-scribe

# Read an attached file inside a skill
python -m repertoire peek change-log-scribe scripts/template.py

# Run a script inside a skill
python -m repertoire run change-log-scribe scripts/template.py fix

# Creation wizard: generate a new skill skeleton (-d required, --yes skips confirmation)
python -m repertoire forge my-new-skill -d "what it does + when to use" --template standard --yes

# Full validation + statistics
python -m repertoire audit
python -m repertoire status

list, audit, check, status support --json for machine-readable output; all commands support --config <file> to specify a configuration file.

Skill format specification

A skill = a directory + a SKILL.md marker file:

my-skill/
├── SKILL.md          # required: YAML metadata + Markdown body
├── scripts/          # optional: deterministic, repeatable scripts
├── references/       # optional: reference material read on demand
└── assets/           # optional: templates, icons and other assets

The SKILL.md header is a metadata block enclosed by --- fences:

---
name: my-skill              # required: lowercase kebab-case, matching the directory name, ≤64 characters
description: one sentence about "what it does + when to use", the sole basis for discovery and triggering
version: 1.0.0              # optional
license: MIT                # optional
tags:                       # optional
  - demo
---
## When to use
...

The metadata parser is implemented as a strict YAML subset (pure standard library): it supports plain / single- and double-quoted scalars, lists, |/> block scalars (including chomping), and comments; it does not support nested mappings, anchors and other advanced constructs — unsupported content is reported with a line number rather than silently parsed incorrectly. Hosts that need full YAML may replace this parser (the interface is in repertoire/frontmatter.py).

Multi-level directory discovery

Zones by priority, highest to lowest:

PriorityzoneDefault locationPurpose
1workspace<project root>/.dsh/skillsproject-private skills
2personal<user home>/.dsh/skillspersonal frequently used skills
3bundled<plugin dir>/skillsskills bundled with the plugin
4extra-*configuredother sources

Scanning rules:

  • Skill directories sit one level below the zone root (<zone>/<skill>/SKILL.md) or two levels (<zone>/<group>/<skill>/SKILL.md, groups supported); deeper directories are ignored
  • Hidden directories (starting with .) are ignored; the marker file name is configurable (default SKILL.md)
  • Name conflicts: the higher-priority zone wins, and the lower-priority copy is recorded as a "shadow" — only one copy appears in the catalog, but the audit clearly reports the suppressed copy and its source zone
  • Broken entries: directories that contain a marker file but whose metadata cannot be parsed (e.g. a name that does not match the directory, illegal naming, missing description) do not enter the catalog but go into the "broken" list, with the specific reason attached

Progressive disclosure

TierContentTriggerCost
Tier 1 · indexname, purpose, source, size, statusauto-injected at session start (catalog.menu event)very low, no body
Tier 2 · bodyfull SKILL.md textloaded on demand via skill.openbounded by max_open_bytes, can be forced
Tier 3 · attachmentsscripts / reference material / assetsread via skill.peek, run via skill.runon demand, never auto-loaded

After a body is loaded the card enters the open state and is cached; skill.drop releases the cache back to listed.

Validation (audit)

The discovery phase performs strong validation: entries whose metadata cannot be parsed, whose naming is illegal, whose name does not match the directory, or which lack a description do not enter the catalog but go into the "broken" list. audit (full), check (single), and forge (self-check after generation) run a full audit, whose findings fall into two tiers:

  • fatal — unusable (missing metadata, illegal naming, name does not match the directory, unreadable file)
  • warning — usable but non-conforming (description too short, description repeats the skill name, body above the line limit, no titled sections, wrong type in optional fields, presence of suppressed shadow copies)

In strict mode all warnings escalate to fatal, suitable for ecosystems that need a hard quality gate. Run python -m repertoire audit to see all findings.

Creation wizard (forge)

python -m repertoire forge <name> -d "<description>" [-t minimal|standard] [-z <zone>] [--version V] [--license L] [--tags a,b] [--yes]
  • The name and description are validated first: illegal kebab naming and empty descriptions are rejected outright
  • Two templates: minimal (SKILL.md only), standard (+ scripts / references / assets directories)
  • The target zone directory is auto-created if missing; an existing same-name skill directory is rejected
  • After generation the artifact is immediately audited and the catalog is re-scanned — the new skill is visible right away; on mid-process failure, already-written files are rolled back automatically

Configuration

JSON configuration file (--config <path>); scalar items (marker, the various limits, strict) can be overridden with environment variables REPERTOIRE_*, e.g. REPERTOIRE_STRICT=1 (zone_paths / extra_zone_paths are structural items, config file only):

{
  "marker": "SKILL.md",
  "max_scan_depth": 2,
  "max_open_bytes": 262144,
  "max_body_lines": 500,
  "peek_cap_bytes": 65536,
  "strict": false,
  "zone_paths": { "workspace": "C:/work/.dsh/skills" },
  "extra_zone_paths": ["D:/shared-skills"]
}
KeyDefaultDescription
markerSKILL.mdskill marker file name
max_scan_depth2max relative depth of skill directories within a zone
max_open_bytes262144body load size limit (force bypasses it)
max_body_lines500body line-count warning threshold
peek_cap_bytes65536per-read limit for attached files
strictfalseescalate warnings to fatal
zone_paths{}override default zone locations; extra keys become named zones
extra_zone_paths[]append lower-priority zones (auto-named extra-1…)

Integration notes (how the harness loads this plugin)

The repo also ships a dsh.bundle (package.json + cordis.patch.yml + index.js). Installing it in a profile — dsh plugin --profile demo add github:JohnXu22786/skill-manager — mounts a Cordis plugin row whose Node bridge spawns python -m repertoire --io and exposes all 8 tools plus system.ping/system.manifest to the agent. Bridge state is kept in one long-lived process, so the catalog cache and open-card state behave like a native session; the bridge is disposed with the plugin. No npm dependencies at load time; Python ≥ 3.10 must be on PATH (override with DSH_REPERTOIRE_PYTHON, request timeout with DSH_REPERTOIRE_TIMEOUT_MS). If Python is missing, the bridge logs a clear error at startup instead of crashing.

This plugin is self-contained; the harness needs only four steps:

import json
from repertoire import create_plugin

# 1. Read the manifest (optional, for capability registration)
manifest = json.loads(open("manifest.json", encoding="utf-8").read())

# 2. Create the instance
plugin = create_plugin()

# 3. Subscribe to events + start
plugin.on("catalog.menu", lambda p: inject_into_context(p["menu"]))
plugin.start()                              # scans once and emits catalog.menu

# 4. Lifecycle and tool calls
plugin.handle("session.boot", {})           # dispatched after receiving harness events
plugin.handle("session.compact", {})        # re-emits the menu after context compaction
result = plugin.call("skill.open", {"name": "change-log-scribe"})

Tool interface (8 tools)

ToolParametersReturns
catalog.lszone? limit?index-level list + zone inventory
catalog.auditfull validation report + runtime statistics
skill.openname force?full body text + byte count
skill.peekname pathtext content of an attached file
skill.runname script args? timeout?exit code + stdout/stderr
skill.dropnamerelease the body cache
skill.forgename description template? zone? version? license? tags?creation result + self-check findings
skill.checknamesingle-skill validation report

Event interface

The plugin subscribes (harness → plugin, invoked via plugin.handle(event, payload)):

EventBehavior on trigger
session.bootre-scan + emit the latest menu
session.compactre-emit the latest menu (restore memory after context compaction)
catalog.rescanre-scan immediately

The plugin emits (plugin → harness, subscribe via plugin.on(event, cb)):

EventPayload highlights
catalog.menucatalog index text + counts (injected into the model context at session start)
catalog.reportfull audit findings + statistics snapshot
catalog.problembroken entries / shadow copies found during scanning
skill.engagea skill body has been loaded
skill.releasea skill body has been released
skill.forgeda new skill has finished being created

Language-agnostic integration (JSON-lines stdio protocol)

Harnesses that cannot import Python (other languages, separate processes) can drive this plugin:

python -m repertoire --io

One request per line, one response per line (id matches; a missing id is treated as a notification and gets no reply):

→ {"id": 1, "method": "catalog.ls", "params": {}}
← {"id": 1, "ok": true, "result": {"count": 2, "items": [...]}}
→ {"id": 2, "method": "skill.open", "params": {"name": "change-log-scribe"}}
← {"id": 2, "ok": true, "result": {"name": "...", "body": "..."}}
→ {"id": 9, "method": "no.such"}
← {"id": 9, "ok": false, "error": {"message": "unknown method 'no.such'"}}

Protocol methods = the 8 tools above + system.ping / system.manifest. A complete session example lives in examples/session.jsonl; a minimal harness demo lives in examples/harness_demo.py (run directly with python examples/harness_demo.py).

Security boundaries

  • Path guard: peek / run relative paths must resolve inside the skill directory, violations are rejected
  • Symbolic links are not followed during discovery, so external content cannot sneak into the catalog via links
  • Binary and over-limit attached files are refused; script runs have a timeout (default 120s)
  • .py subprocesses are forced to a UTF-8 environment; output of other script types (.ps1/.bat/.sh) is decoded as UTF-8 (missing bytes are shown as replacement characters, no crash)
  • Read-only operations never modify skill content; forge refuses to overwrite an existing skill directory and rolls back automatically on failure

Running tests

python -m unittest discover -s tests -p "test_*.py"     # 188 tests, zero dependencies

License

MIT