MJorgin / dsh-skill-router

Listed

Rule-first pre-step skill router for DeepSeek Harness — pours matched skills on high-confidence hits, stays silent otherwise. Deterministic: zero LLM calls, zero token cost until a rule pours, one pour per session, broken YAML never breaks the session. 规则路由 · 高置信命中 · 静默放行 · 零 Token 消耗

mainModelSkillSession View source

Installation

npx -y @deepseek-ai/dsh plugin --profile web add github:MJorgin/dsh-skill-router

This installation command is an unverified starting point generated from the GitHub repository address.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit 0ceeb96Synced Aug 18, 2026
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

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected
Security policyDetected
Contributing guideDetected
DocumentationDetected

Repository information

Language
JavaScript
License
MIT
Last updated
Aug 17, 2026, 4:11 AM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.