haohaiHuang/Design-Agent--plugins-design-router ↗★ 0
@local/dsh-design-router
DSH 设计 Agent 完整可复现包:design-references 路由技能(DSH 适配)+ design-router 确定性工具插件 + my-agent 预设 适合需要在DSH中复现和运行特定设计辅助Agent的用户。
インストール
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:haohaiHuang/Design-Agent#5dec33a44d22cfa95d59d19467276db451b8aa37&path:plugins/design-routerドキュメント
README 全文を読む ↗Design-Agent — DSH Design Agent workspace
A fully reproducible package for a design agent on DeepSeek Harness (DSH): the my-agent preset, the design-references routing skill (DSH-adapted), and the design-router deterministic-tool plugin. Built by upgrading an existing HTML-based design agent with the design-references methodology.
⚠️ What this package reproduces: the machinery, not the content. The routing, discipline, and tools are fully self-contained (clone →
cp -RL→ run). But the reference library is personal — several primary-level resources point at~/Desktop/Design/...and~/resources/design-references.md, private assets that do not ship with the repo. On a fresh machine those degrade to the fallback chain (web_search+dembrandt+ the bundled hallmark discipline). If you want the full personal reference set, copy those directories yourself; without them the package still works, as a "hallmark discipline + web research" design agent. See One-shot reproduction for what ships vs. what you bring.
🇨🇳 中文版见 README.zh.md
What's inside
| Component | Role |
|---|---|
plugins/design-router/ | Deterministic-tool Cordis plugin (6 read-only tools + 1 local-log writer, zero external runtime deps) |
presets/my-agent/ | DSH agent preset (agent.cordis.yml + preset.yml): three-layer routing persona (stage → branch → phase) with per-phase confirmation gates |
skills/design-references/ | Stage/branch routing skill (stage triage → A product / B content / C general → five phases), DSH-adapted |
skills/hallmark/ | Anti-AI-slop execution skill (MIT upstream copy from nutlope/hallmark; site/ theme tokens & examples bundled in-skill, self-contained) |
DSH version requirement:
0.1.5-rc.1or later. The persona plugin schema changed in 0.1.5 (text→prefix+suffix); a preset still usingtext:fails to mount with$.prefix missing required value. This repo's preset is on the 0.1.5 schema and also uses two rows introduced in 0.1.5:present(dsh-tool-present, deliverable declaration) andcommand-goal(/goalcommand).
⚠️ DSH 0.2.0+ (desktop app): presets are no longer scanned from a directory
0.2.0 replaced the preset mechanism. In 0.1.x a preset was a directory
(~/.dsh/.agent-presets//agent.cordis.yml) that @deepseek-ai/dsh-agent-presets scanned and discovered.
In 0.2.0 there is @deepseek-ai/dsh-agent-preset-registry whose config accepts only default /
selectedDefault — no roots, no directory discovery — and every preset is now one row in the profile
composition: @deepseek-ai/dsh-agent-preset with an inline plugins: list (id + plugins are required).
Consequence: after upgrading to the desktop app (0.2.0-rc.2), a custom preset sitting in
~/.dsh/.agent-presets/ silently disappears from the picker — nothing is broken, the directory is simply
no longer read.
Migrating a preset to 0.2.0
docs/migrate-preset-0.2.0.mjs converts the directory format into a root-level insert patch:
# 1. 只生成 patch(检查用)
node docs/migrate-preset-0.2.0.mjs \
--src presets/my-agent/agent.cordis.yml \
--id design-agent --name "设计 Agent" \
--plugin "$PWD/plugins/design-router/index.mjs" \
--out /tmp/design-agent-patch.yml
# 2. 写入某个 profile 的 patch 层(自动备份为 .bak.)
node docs/migrate-preset-0.2.0.mjs --src presets/my-agent/agent.cordis.yml \
--plugin "$PWD/plugins/design-router/index.mjs" \
--append ~/.dsh/profiles/desktop/cordis.patch.yml
Then fully quit and reopen the app; the preset appears in the new-session preset list.
Two things that change with the inline format:
Equivalent route: the bundle layer (what this machine uses)
The recipe above writes the insert row into a profile's user layer (cordis.patch.yml).
The same row can live in a bundle layer instead — see presets/design-agent-preset/:
an installable bundle (Plugins → Add plugin) whose plugin row uses the package name
@local/dsh-design-router, so the bundle contains no machine absolute path and can be copied across
machines (cost: the plugin package is installed separately).
Bundle layer (presets/design-agent-preset/) | Profile user layer (docs/migrate-preset-0.2.0.mjs) | |
|---|---|---|
| Install | Plugins → Add plugin ×2 (preset + plugins/design-router) | script `--append |
| /cordis.patch.yml` | ||
| Plugin reference | package name (no absolute path, portable) | script rewrites it to an absolute path |
| Best for | multi-machine reuse, versioned with the repo | single machine, no bundle install |
Both are the same insert row — do not use both (two rows for the same preset- duplicate the composition).
- The plugin row must use an absolute path. An inline preset has no "preset directory", so
./plugins/design-router/index.mjswould resolve against the profile directory. The migration script rewrites it to the absolute repo path (override with--plugin). - The desktop profile is Electron-managed.
~/.dsh/profiles/desktop/cordis.patch.ymlalso receives app-written settings; if a settings change ever rewrites that file, re-run the--appendcommand (it is idempotent-safe in the sense that it appends — remove the previous block first if duplicated).
The legacy directory
~/.dsh/.agent-presets/my-agent/can be kept as a reference; 0.2.0 ignores it.
Troubleshooting: the preset still does not appear in the picker
A preset whose rows fail to mount is registered as broken and the picker hides it — the usual cause is a row naming a package that the running DSH version no longer ships. That is exactly what happened on the 0.1.5 → 0.2.0 jump:
| 0.1.5 row | 0.2.0 replacement |
|---|---|
@deepseek-ai/dsh-workflow-worker-thread | @deepseek-ai/dsh-workflow-ptc (same provider: spawn role) |
One dead reference is enough to take the whole preset off the list. To check a preset against the installed
app (this build lives in app.asar, so read it through Electron's own runtime):
APP="/Applications/DeepSeek Harness.app/Contents/MacOS/DeepSeek Harness"
ASAR="/Applications/DeepSeek Harness.app/Contents/Resources/app.asar"
# 1) 列出该版本实际带的包
ELECTRON_RUN_AS_NODE=1 "$APP" -e "console.log(require('fs').readdirSync('$ASAR/dsh/node_modules/@deepseek-ai').join('\n'))" \
| sort > /tmp/dsh-pkgs.txt
# 2) 抽出预设引用的包名并比对
grep -oE "name: '@deepseek-ai/[a-z0-9/-]*'" presets/my-agent/agent.cordis.yml \
| grep -oE "@deepseek-ai/[a-z0-9/-]*" | sed 's|@deepseek-ai/||' | sort -u | grep -v / > /tmp/preset-pkgs.txt
comm -23 /tmp/preset-pkgs.txt /tmp/dsh-pkgs.txt # 输出即"本版本没有的包"
Two further checks that catch the other failure modes:
# A) 组合是否真的包含你的预设(把 desktop profile 复制成非保留名就能 dump)
cp -R ~/.dsh/profiles/desktop ~/.dsh/profiles/dtest
dsh --profile dtest --dump-config | grep -n preset-design-agent # 之后记得 rm -rf dtest
# B) 逐行 config 是否符合本版本 schema(捕获 0.1.5 persona text→prefix 那类漂移)
dsh --profile web --dump-config-schema > /tmp/schema.json # 再用 jsonschema 校验 plugins 列表
plugins/design-router — deterministic tools
Ported from my-pi-skills extensions/design-router (pi extension → DSH Cordis plugin). Mounted by the my-agent preset via a relative-path row in agent.cordis.yml (the preset's plugins/ is a relative symlink to the repo-root plugins/, expanded by cp -RL on install — no absolute paths needed):
- id: design-router
name: './plugins/design-router/index.mjs'
Tools
| Tool | Purpose | Phase |
|---|---|---|
| `design_lookup | ||
| ` | Query the design-resource registry (R/C/E/V 3-D index + fallback chain + sources; output tags style buckets) | "What do I consult at this step?" |
design_route | Map need keywords to recommended style-bucket combos (primary must-check + secondary on-demand) + per-bucket representative resources | Phase 1 research (anti-homogeneity routing) |
design_diversity | Machine check of 3 candidates' difference (hue family / font tone / source bucket), PASS/FAIL | Before presenting candidates in Phase 1 (anti-homogeneity check) |
design_quality | Record/query source-quality signals (extraction success / rework rate / reachability — objective, not taste-based); local log, not in git | Record after Phase 4 / consume for downranking in Phase 1 |
design_audit | Machine slop gates (hallmark machine subset) + interfaces CS-* 8 rules + phase-4 scans (incl. DR-4 weight/radius, DR-5 comment self-destruct & undefined vars, DR-6 non-text contrast per WCAG 1.4.11) + inherited-contrast. Strips comments before scanning, resolves one level of var(), reads same-dir linked stylesheets, honours inline slop-ignore: waivers | Phase 4 verification |
design_contrast | WCAG 2.1 + APCA-approx contrast | Phase 4 verification |
Intentional differences from the pi version
- Removed
design_research(DSH uses the ledger-grep + refero probe +web_searchfallback chain) - Removed
hallmark_study_fetch(DSH usesdembrandt/defuddleinstead) - Removed
before_agent_startinjection and the/design-routercommand (DSH's skill-loading mechanism already covers routing) - No dependency on
@deepseek-ai/dsh-tools(a workspace module cannot resolve the dsh install directory); tool definitions are built with plain JSON Schema — zero external runtime deps
Layout
plugins/design-router/
├── index.mjs # Plugin entry: registers 6 tools (5 read-only + 1 local-log writer)
├── checks/ # Ported checkers (TS→JS): typography/layout/a11y/copy/contrast/cheat/kill-slop/assets/types
│ └── kill-slop.test.mjs # KS-* regression test (node checks/kill-slop.test.mjs)
└── data/
└── registry.json # Data form of registry.md (91 resources × 9 branch routes)
Machine-gate coverage
design_audit runs seven checker modules and returns a gate-numbered punch list:
| Family | Gates | Source |
|---|---|---|
| Hallmark slop | 1/2/10/14/19/24/26/27/30/33/34/37/38a/39/40/41/46/47/50/51 | hallmark slop-test.md (machine subset) |
| interfaces CS-* | CS-1…CS-8 | interfaces.dev cheat-sheet |
| Motion EM-* | EM-2/3/5 (EM-1/7/8 map to gates 10/14/27) | emilkowalski/skills |
| kill-ai-slop KS-* | KS-03/04/05/08/14 | kill-ai-slop transcription |
| Asset layer DR-A* | DR-A1/DR-A2 | Anshu asset-layer gates (brand logo/image presence) |
| design-references phase 4 | DR-4 (font-weight / radius off-scale) | design-references workflow.md |
Maintenance
registry.mdis the source of truth (~/.agents/skills/design-references/references/registry.md); after editing it, runnode plugins/design-router/scripts/build-registry.mjsto regeneratedata/registry.json(+data/manifest.jsonversion metadata). Never hand-edit registry.json.- Checker logic follows upstream
extensions/design-router/checks/(TS→MJS port); after upstream updates, runnode plugins/design-router/scripts/check-checks-sync.mjs /path/to/my-pi-skills/extensions/design-router/checksto verify gate coverage — then diff the checker constants by hand: gate-number parity does not catch detail drift (e.g. an expanded default-font list). The KS regression test catches that class of drift. - After any checker change:
node index.test.mjsandnode checks/kill-slop.test.mjs.
One-shot reproduction (fresh machine)
The repo reproduces the machinery: plugin + preset + DSH-adapted skills are all in-repo (reference-library content is personal — see the warning at the top).
# ── Ships with the repo (clone → run) ──
# 1. Skills (design-references is DSH-adapted; hallmark is an MIT upstream copy)
cp -R skills/design-references ~/.agents/skills/
cp -R skills/hallmark ~/.agents/skills/
# 2. Preset (cp -RL expands the relative plugins symlink in presets/my-agent/
# into a self-contained copy — after install the preset no longer depends on
# the repo path, so it can be copied around or migrated freely)
mkdir -p ~/.dsh/.agent-presets
cp -RL presets/my-agent ~/.dsh/.agent-presets/
# 3. Plugin source (keep it under the repo-root plugins/ for git management)
# The preset references it via the RELATIVE path './plugins/design-router/index.mjs':
# presets/my-agent/plugins is a relative symlink to the repo-root plugins/,
# which cp -RL expands to a real directory — no path edits needed on any machine.
# 4. External deps (soft deps — missing ones degrade gracefully)
npm install -g dembrandt # URL → design tokens (phase-1 candidate verification)
# defuddle: npm install -g defuddle
# npm install -g @open-pencil/cli # Optional: read/convert/verify .fig/.pen design files (falls back to Figma-family skills / manual review)
# ── Bring your own (personal picks; absence degrades to the fallback chain) ──
# 5. Machine-local assets (ledger + kami/zine/logo-generator reference libs)
# Without them the package still runs — as a "hallmark discipline + web_search/dembrandt"
# design agent — but the candidate pool loses your personal picks.
Note: the preset's plugin row uses a relative path (./plugins/design-router/index.mjs,
with presets/my-agent/plugins as a relative symlink expanded by cp -RL), so a fresh
machine just copies the preset directory — no path edits required. If you'd rather
avoid symlinks, copy plugins/design-router/ into presets/my-agent/plugins/ and use
plain cp -R (same result, just a second copy).
⚠️ When plugin changes take effect: the preset's persona text is read per session — create a new session after saving, no restart needed. But
plugins/design-router/is a Cordis plugin mounted once per process (ESM imports once), so after changing the plugin you must restart thedsh webprocess for it to be reloaded. Re-install withcp -RL presets/my-agent ~/.dsh/.agent-presets/and then restart; re-installing without restarting keeps running the old checkers.
Prerequisites (from real-machine testing)
The audit checks and the skill's rendering discipline lean on a few local tools. Missing ones only degrade output, but degrade it a lot:
# 1) Visual review (vision skill + the critic sub-agent's direct image reading)
# vision-cli / ego-browser usually live in ~/.local/bin — if that dir is not on PATH the tools report "not found"
ln -sf ~/.local/bin/vision-cli /opt/homebrew/bin/vision-cli
ln -sf ~/.local/bin/ego-browser /opt/homebrew/bin/ego-browser # any dir already on PATH
# 2) Reference-site token extraction
npm install -g dembrandt # real-browser render → exact tokens + DESIGN.md
# 3) Screenshot channel (measured on this machine; try in order)
# chrome-headless-shell (Playwright cache) → iframe preview shell →
# ego-browser Page.printToPDF + pdftoppm
# Note: headless Google Chrome enforces a minimum window width (`--window-size=375` yields
# innerWidth 500), so narrow-viewport shots come out as a wider layout cropped to 375.
Verified (re-runnable verification entry points)
Full verdicts and evidence for 18 real-machine cases, 8 post-fix re-runs and 3 back-port samples live in [`docs