๐Ÿธโ†’โš™๏ธ dsh-skill-router

August 17, 2026 ยท View on GitHub

dsh-skill-router โ€” rule-first pre-step skill routing for DeepSeek Harness

๐Ÿธโ†’โš™๏ธ dsh-skill-router

Rule-first pre-step skill routing for DeepSeek Harness: pours matched skills, stays silent when unsure.

License: MIT Test CI DeepSeek Harness Deterministic Once per session Companion Docs


Companion executor to skill-bartender: the skill carries the policy judgment, this plugin carries the execution. Deterministic, zero LLM calls, zero token cost until a rule actually pours:

  • โšก Pre-step hook โ€” reads the latest user message before every step.
  • ๐Ÿงญ Rule-first matching โ€” user-editable YAML policy (~/.dsh/skill-router.yaml, bundled defaults in default-policy.yaml), first match wins.
  • ๐Ÿ”‡ Silent miss โ€” no hit โ†’ zero intervention; the model keeps its normal catalog flow.
  • โ™ป๏ธ Once per session โ€” each skill pours at most once.
  • ๐Ÿ›ก๏ธ Broken YAML never breaks the session โ€” falls back to bundled defaults.

Why ยท How it works ยท What you get ยท Quick start ยท See it in action ยท Policy ยท Tested ยท Scope & non-goals ยท FAQ ยท Layout ยท License

English ยท ็ฎ€ไฝ“ไธญๆ–‡


๐Ÿค” Why

Most skill loading is left to the model's judgment: it sees the catalog every step, re-decides every time, and often loads late, wrong, or not at all. A router that runs before the model answers fixes that:

dsh-skill-routerLLM-judge routerManual loading
Decision makerrules (deterministic)LLM / embeddingsthe model, per step
Token costzero until a rule poursevery stepevery step
Latency added~0 msmodel round-tripn/a
Reproducibleโœ… same message โ†’ same pourโŒ variesโŒ varies
User controledit YAML, doneprompt ithope it remembers

Why rules and not an LLM judge? Speed, cost, and predictability. A URL-path rule routes feishu.cn/x/docx/ to lark-doc in microseconds, for free, every time โ€” and skill-bartender's routing table is where the policy judgment lives. This plugin is the muscle, not the brain.

โš™๏ธ How it works

  • Hooks agent/pre-step, reads the latest user message.
  • Matches it against user-editable rules (~/.dsh/skill-router.yaml, bundled defaults in default-policy.yaml). First match wins.
  • On a hit: pours the matched skill bodies into the step as skill-invocation messages โ€” the catalog's "already loaded, don't re-load" rule applies automatically.
  • No hit: zero intervention. The model keeps its normal catalog flow.
  • Each skill pours at most once per session.

โœจ What you get

CapabilityWhat it does
โšก Pre-step hookagent/pre-step โ€” the pour happens before the model starts thinking
๐Ÿงญ YAML policyUser-editable ~/.dsh/skill-router.yaml; broken YAML falls back to bundled defaults
๐Ÿ”Ž whenToUse triggersInstalled skills' whenToUse frontmatter acts as a secondary trigger (literal phrase match, appended after YAML rules)
๐Ÿš€ Zero costNo LLM judge, no embeddings โ€” rules only (fast, free, deterministic)
โ™ป๏ธ Once per sessionDedupes pours per session; no skill body floods the context
๐Ÿ”— CompanionWorks with skill-bartender's routing table and taste test

โšก Quick start

dsh plugin --profile web add github:akqwpeter-prog/dsh-skill-router

Then restart the running instance (profile bundles load at boot).

Verify: say "็”Ÿๆˆไธ€ๅผ ๆตทๆŠฅ" โ€” media-tools pours automatically; say "่ฟ™ไธชๆˆชๅ›พๅธฎๆˆ‘ๆฃ€ๆŸฅไธ€ไธ‹" โ€” vision-review pours. No rules matched? The model just works as usual.

๐Ÿ“ธ See it in action

One picture: a rule hits โ†’ the skill pours before the model answers; no hit โ†’ total silence.

How the router works: agent/pre-step reads the message โ†’ rule match (YAML first, then whenToUse) โ†’ hit? โ†’ pour skill-invocation (once per session) or stay silent (zero tokens)

๐Ÿงญ Policy

# ~/.dsh/skill-router.yaml
rules:
  - match: "(็”Ÿๆˆ|็”ป).{0,12}(ๅ›พ|ๆตทๆŠฅ|banner)"
    pour: [media-tools]
  • Ordered by precision: URL-path routing first, media, delegation, workflow skills before atomics.
  • First matching rule wins; pour lists the skill names to load.
  • Broken YAML falls back to bundled defaults and never breaks the session.
  • Write it as data: improve matching by editing YAML, not code.
  • Full reference: docs/POLICY.md ยท bundled defaults: default-policy.yaml ยท walkthrough: docs/EXAMPLES.md.

๐Ÿงช Tested

Integration suite (10 cases) run against a live profile: pour, dedupe, zero-touch, reject passthrough, URL routing, mail-vs-IM disambiguation, false-positive guards. See test/ in the repo, plus the design notes in DESIGN.md and the gold-task list in GOLD-TASKS.md.

๐ŸŽฏ Scope & non-goals

  • No LLM judge, no embeddings: rules only (fast, free, deterministic).
  • No auto-install of missing skills: that stays in skill-bartender's quarantine โ†’ SkillSpector โ†’ human-approval flow.
  • Rule table is data: improve matching by editing YAML, not code.
  • whenToUse frontmatter on installed skills acts as a secondary trigger (literal phrase match, appended after YAML rules). Write it as a short trigger phrase; long prose never matches. Today's skill data mostly lacks the field โ€” skill-bartender's taste test can backfill it.

โ“ FAQ

Does it consume tokens when nothing matches? No. No hit โ†’ zero intervention, zero LLM calls. The router only reads text already in the step and runs regex rules โ€” microseconds, free.

How is it different from skill-bartender? skill-bartender is the judgment (which skill fits, when to stay silent, how to install safely). This plugin is the execution (a deterministic pre-step hook that pours). They complement each other; the router works standalone too.

Can I use my own rules? Yes โ€” copy default-policy.yaml to ~/.dsh/skill-router.yaml and edit. First match wins; broken YAML falls back to defaults.

Does it pour the same skill twice in one session? No โ€” each skill pours at most once per session, so context never floods.

๐Ÿ—บ๏ธ Layout

dsh-skill-router/
โ”œโ”€โ”€ index.js               # Cordis plugin: pre-step hook + pour logic
โ”œโ”€โ”€ policy.js              # rule loading / matching (unit-tested)
โ”œโ”€โ”€ default-policy.yaml    # bundled defaults (copy to ~/.dsh/skill-router.yaml)
โ”œโ”€โ”€ test/                  # policy unit tests + integration suite
โ”œโ”€โ”€ DESIGN.md / GOLD-TASKS.md    # design notes + gold tasks
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ screenshots/how-it-works.png
โ”‚   โ”œโ”€โ”€ POLICY.md / EXAMPLES.md
โ”‚   โ”œโ”€โ”€ social-preview.png  # banner (regenerate via scripts/)
โ”‚   โ””โ”€โ”€ lang/README_ZH.md   # ็ฎ€ไฝ“ไธญๆ–‡
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ make-banner.py      # composes docs/social-preview.png
โ”‚   โ”œโ”€โ”€ make-diagram.py     # composes the how-it-works diagram
โ”‚   โ””โ”€โ”€ check-policy.mjs    # policy validation
โ”œโ”€โ”€ cordis.patch.yml / package.json   # DSH bundle manifest
โ””โ”€โ”€ LICENSE (MIT)

๐Ÿค Join the DSH plugin ecosystem

DeepSeek Harness developer preview is still in its testing phase for Harness developers; core plugins and base APIs will keep iterating. We look forward to exploring the upper limits of intelligence together with developers worldwide, on top of open-source, open, reusable, and composable infrastructure.

This repo is tagged dsh-plugin and listed in the awesome-dsh-plugin curated list. PRs, issues and translations are welcome.

๐Ÿ“„ License

MIT