Skip to content
dsh.fish
Bundle

dsh-skill-router

Pre-step skill router for DeepSeek Harness: rule-first task-to-skill matching that pours matched skill bodies only on high-confidence hits and stays silent otherwise. Companion to skill-bartender.

Source
MJorgin
stars
1 stars
License
MIT
Updated
Updated 14 days ago

Readme

<div align="center">

<img src="docs/social-preview.png" alt="dsh-skill-router โ€” rule-first pre-step skill routing for DeepSeek Harness" width="100%">

<br>

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

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

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Test CI](https://github.com/akqwpeter-prog/dsh-skill-router/actions/workflows/test.yml/badge.svg)](https://github.com/akqwpeter-prog/dsh-skill-router/actions/workflows/test.yml)
[![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-Plugin-4D6BFE)](https://github.com/topics/dsh-plugin)
[![Deterministic](https://img.shields.io/badge/engine-rules%20only%2C%20zero%20LLM-2EA44F)](README.md#-how-it-works)
[![Once per session](https://img.shields.io/badge/pour-once%20per%20session-8B5CF6)](README.md#-how-it-works)
[![Companion](https://img.shields.io/badge/companion-skill--bartender-4D6BFE)](https://github.com/akqwpeter-prog/skill-bartender)
[![Docs](https://img.shields.io/badge/docs-2%20languages-4D6BFE)](docs/lang/README_ZH.md)

<br>

Companion executor to [skill-bartender](https://github.com/akqwpeter-prog/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](#-why) ยท [How it works](#-how-it-works) ยท [What you get](#-what-you-get) ยท [Quick start](#-quick-start) ยท [See it in action](#-see-it-in-action) ยท [Policy](#-policy) ยท [Tested](#-tested) ยท [Scope & non-goals](#-scope--non-goals) ยท [FAQ](#-faq) ยท [Layout](#-layout) ยท [License](#-license)

[**English**](README.md) ยท [**็ฎ€ไฝ“ไธญๆ–‡**](docs/lang/README_ZH.md)

</div>

---

## ๐Ÿค” 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-router | LLM-judge router | Manual loading |
|---|---|---|---|
| Decision maker | **rules** (deterministic) | LLM / embeddings | the model, per step |
| Token cost | zero until a rule pours | every step | every step |
| Latency added | ~0 ms | model round-trip | n/a |
| Reproducible | โœ… same message โ†’ same pour | โŒ varies | โŒ varies |
| User control | edit YAML, done | prompt it | hope 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

| Capability | What it does |
|---|---|
| โšก Pre-step hook | `agent/pre-step` โ€” the pour happens *before* the model starts thinking |
| ๐Ÿงญ YAML policy | User-editable `~/.dsh/skill-router.yaml`; broken YAML falls back to bundled defaults |
| ๐Ÿ”Ž `whenToUse` triggers | Installed skills' `whenToUse` frontmatter acts as a secondary trigger (literal phrase match, appended after YAML rules) |
| ๐Ÿš€ Zero cost | No LLM judge, no embeddings โ€” rules only (fast, free, deterministic) |
| โ™ป๏ธ Once per session | Dedupes pours per session; no skill body floods the context |
| ๐Ÿ”— Companion | Works with skill-bartender's routing table and taste test |

## โšก Quick start

```sh
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.*

<img src="docs/screenshots/how-it-works.png" alt="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)" width="100%">

## ๐Ÿงญ Policy

```yaml
# ~/.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](docs/POLICY.md) ยท bundled defaults: [default-policy.yaml](default-policy.yaml) ยท walkthrough: [docs/EXAMPLES.md](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](DESIGN.md) and the gold-task list in [GOLD-TASKS.md](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.

- [dsh-plugin topic](https://github.com/topics/dsh-plugin)
- [Quickstart](https://deepseek-harness.github.io/deepseek-harness/guide/quickstart)
- [DeepSeek Harness repo](https://github.com/deepseek-ai/deepseek-harness)
- Policy companion: [skill-bartender](https://github.com/akqwpeter-prog/skill-bartender)

> This repo is tagged [`dsh-plugin`](https://github.com/topics/dsh-plugin) and
> listed in the [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
> curated list. PRs, issues and translations are welcome.

## ๐Ÿ“„ License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:MJorgin/dsh-skill-router

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source