🧩 Skill Ledger

August 23, 2026 Β· View on GitHub

Skills you can see, switch, and analyze. Per-reply skill badges + a non-blocking alternative-skill hint + a session-wide Skills tab (ledger with capability/quality analysis).

English Β· 繁體中文

npm License: MIT DSH Topics

A DeepSeek Harness (DSH) plugin that turns skills into a first-class observable surface. Three pillars:

✨ Features

A β€” Per-reply badges, on every turn (dual-seat)

Beneath every completed reply, the skills the turn triggered (🧠) and the tools it actually used (βœ“) or failed (βœ—), aggregated with counts:

$ 🧩 \text{Skills} [🧠 \text{ak}-\text{hk}-\text{stock}-\text{dd} βœ“] [🧠 \text{serenity}-\text{skill} βœ—] 🧩 \text{Tools} [\text{read} \times 3 βœ“] [\text{bash} βœ—] $

The badges render through two cooperating seats, because the turn-tail slot is a chain with single-winner semantics β€” the shipped deliverables card claims every turn that produced files, and a chain elects exactly one entry:

  1. conversation.chat.turnTail (chain seat) β€” the full badge block + hint row, rendered directly on turns the chain reaches (no files produced);
  2. conversation.chat.assistant-actions (list seat) β€” a compact 🧩 N skills/M tools chip on EVERY completed turn's action row (list slots render all entries, so it coexists with the deliverables card). Click it to expand a popover with the same full badge block + hint row.

Result: badges are reachable under every reply, file-producing or not, without ever shadowing the shipped deliverables UI.

B β€” Non-blocking alternative-skill hint

When a turn used at least one skill, the hint row appears:

🧩 Not happy? Try   [serenity-skill] [dcf-valuation] [ak-financial-analyst]  [Re-run with selected]

Multi-select candidate chips β†’ the button writes a re-run prompt draft into the composer. Nothing is ever intercepted, popped, or timed out β€” you press Enter, or you don't. (The philosophical opposite of a blocking "skill gate".)

C β€” The Skills tab (conversation.view, id skills)

A session-scoped tab beside chat/trajectory: per-skill ledger stats (calls / ok / err / turns / last-failing turn) backed by the skillLedger session projection (whole session, including out-of-window turns and cold-start backfill), plus two analysis affordances per skill:

  • βš™ capability/quality analysis β€” instant structural report via POST /api/skill-ledger/analyze (folded stats + catalog definition digest);
  • πŸ”¬ deep analysis β€” writes a prompt draft that dispatches a skill-analysis skill for a source-level capability/quality review.

Why the projection is trustworthy

The host half registers skillLedger with the runtime's real contract (stateSchema + wire: { viewSchema, view } β€” the same shape @deepseek-ai/dsh-goal uses). Earlier community plugins registered with a schema/view top-level shape the projection registry never reads; those units silently degrade to host-only (no client value, cache rows that force full-log refolds on every cold read). This package is the reference implementation of the correct wiring.

The fold also settles errors correctly: the committed tool/result event carries isError on its message.content[] block β€” not on data.error β€” and pairing happens through the block's toolCallId. Both facts were verified against real session logs.

Install

Path A β€” Host composition (always-on, every session & preset)

Add to the profile's package.json:

"dsh": { "profile": { "bundles": ["@bryan-cmf/dsh-skill-ledger"] } }

then install (dsh plugin --profile <name> install) and restart DSH.

Path B β€” Agent preset row

- id: skill-ledger
  name: '@bryan-cmf/dsh-skill-ledger'
  inject: [sessionProjections, webServer, skills, sessions]

⚠️ Keep inject in the row (or bundle patch): the DSH loader reads the dependency list from row options, not from the module export. The module-export form is how dsh-skill-gate silently never activated; the missing form is how dsh-ops-view 0.2.0 failed the whole tree.

Requirements

  • DSH >= 0.1.0-rc.6 β€” services sessionProjections, webServer, skills, sessions (all shipped in the dsh-base bundle)
  • Browser UI needs the conversation.chat.turnTail chain, conversation.chat.assistant-actions list and the conversation.view ring (shipped chat view)

Configuration

None in v0.1 β€” knobs are intentionally minimal. Caps are compile-time constants (MAX_TURNS = 500, MAX_CALLS_PER_TURN = 60, candidates served 12 β†’ client narrows to 6).

HTTP surface

RouteMethodPurpose
/api/skill-ledger/candidatesGETCatalog skills (name + 120-char description)
/api/skill-ledger/analyzePOST {sessionId, skill}Folded stats + definition digest

Both are fail-soft: errors return {error} with HTTP 200, never 5xx.

Design document

docs/design.md records the full review that led here β€” including the autopsy of the two predecessors this package replaces (dsh-skill-trail, dsh-skill-gate), the hot-plugin validation round, and the dual-seat fix for the chain/deliverables conflict.

Development

pnpm install
pnpm build        # tsc declarations β†’ lib/types + tsdown β†’ lib/{index,client}.js

License

MIT