dsh-assembler
其他 活跃维护

dsh-assembler

TT-Wang/dsh-assembler

支持通过单句自然语言描述快速组装可运行、可交付的AI智能体,依托Vibe Assembly能力简化构建全流程,无需复杂配置即可获得可直接投入使用的完整智能体实例。

1
Stars 标星
0
Forks 分支
1
Watchers 关注
0
Open Issues
JavaScript
主要语言
BSD-3-Clause
开源协议
10.4 MB
仓库大小
1 个月前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:TT-Wang/dsh-assembler
git clone https://github.com/TT-Wang/dsh-assembler.git
git clone git@github.com:TT-Wang/dsh-assembler.git
README.md main

dsh-assembler — Vibe Assembly for DSH

English | 中文

Assemble a working AI agent from one sentence — and be able to hand it over.

dsh-assembler is a DeepSeek Harness (DSH) plugin. You describe the agent you want in plain language ("build me a support bot that can look up orders, open tickets, and hand off to a human"); the assembler matches parts from a capability catalog, emits an agent preset, and then proves it works by running it in a real session. New sessions pick that preset from the roster and go.

The catalog grows through an induction pipeline: open-source libraries, public APIs, a client's own HTTP interfaces, and a client's documents all enter through one command each — and only past a quality gate. No glue code, only configuration.


Scale today

Measured Where to verify
Catalog 79 MCP servers / 215 registered tools / 237 assemblable entries (229 federated + 8 static) index/catalog.yml
Part mix 61 library-backed + 13 service-backed + 4 first-party same
Assembly wall time median 56s (single-turn 52s / scenario 182s) ledgers in bench/results/

What it does

  • Two entry points — the /assemble <requirement> command (human shortcut) and an assemble tool (agent-native: the whole call renders in the conversation).
  • Assemble-then-verify — after emitting a preset, the assembler derives an acceptance probe, runs it in a real session bound to that preset, and judges the reply against content-bearing marks. A FAIL triggers one re-selection with the failure fed back, then re-probes. find → assemble → **verify**, on by default.
  • Multi-turn scenario probes — the deriver picks the probe shape itself: one turn for pure-compute agents, a 2–4 turn scenario when the requirement implies work that outlives a turn (bookkeeping, filing, tracking). Scenario turns run in one session, and a later turn queries what an earlier turn wrote. Judgement stays black-box: replies only, never the trajectory.
  • Solution packs (the FDE unit of delivery) — solutions/<name>/solution.yml declares an entire engagement: which agents, which catalog, deployment parameters, client knowledge. solution apply assembles them all, each through verification; solution handover grows a delivery report out of the artifacts themselves. Multi-tenant is --param plus a different credential set — never a forked manifest.
  • Knowledge packs (via: knowledge) — a client's manuals, SOPs and product catalogues enter as static teaching material, past a retrieval-hit gate (a probe question whose expected snippet cannot be found is refused), and are copied into the preset's kb/ at assembly time. The handover is one self-contained directory.
  • Credential contract (interface first, key later) — parts declare the environment variable they need and what it is for; the value never enters a preset. Unconfigured, a part still starts, listTools still succeeds, and a call returns an actionable error (which variable, what for, where to get it). On the assembly side: assembly succeeds, the probe degrades to SKIPPED with configuration guidance. Optional credentials (GitHub public reads, polite-pool mailtos) take the anonymous path and do not hold verification back.
  • Client-private catalogs — catalogs/<client>/ carries its own generated/, index/, capabilities.yml and knowledge/. One client's parts cannot surface in another's assembly, because isolation is by separate files, not by a filter someone can forget.
  • Service-backed parts — 13 live data services are wired in (weather, FX, geocoding, holidays, macro data, SEC filings, scholarly search, wiki facts, research graph, package intel, Feishu, Slack, GitHub Issues). A library part pins repo@rev; a service has no bytes to pin, so it pins terms + rate limit + data licence instead — and those travel into the BOM, because a client's compliance desk asks about them first.
  • Parts BOM — every assembly emits parts.lock.yml beside the preset: per part its origin, licence, verified status and the serverName actually mounted, plus knowledge sources with versions and the pending-credential list.
  • Federation cache — each part's tool list is cached under (connection config + adapter file fingerprint): cold ~5s, warm 0.002s.
  • persona lint — mechanical checks on the persona text: every tool it names must be in the mounted surface, step-numbered choreography is rejected, length is bounded.
  • Design notes — DESIGN.md (Chinese) states what the assembler does, what it deliberately does not, and where the boundary sits.

Every assembly ships a web UI

Assembly does not stop at the preset — every agent also gets an operable web frontend, served same-origin by the assembler on the host (/assembler/ui/<preset>). The page speaks the host's public wire (session.create / session.prompt / events.mux) and shares the same agent and the same durable workspace as the DSH conversation view; it pulls current state on open.

Each screenshot below is the real product of a single /assemble command (real assembly, real data, real capture — verdict lines quoted verbatim):

任务管理助手 … 要一个可以拖拽的任务看板页面 → 自动验证:PASS · 前端验收:页面门+环路门 PASS

Kanban (dark)

Approval desk

Data desk

Chat console

How it works:

  • Frontend template parts (via: frontend) — 7 templates: chat console (default for every preset), form desk, data desk, dashboard, drag-and-drop kanban, approval desk, file desk; the matcher picks exactly one by interaction shape
  • Look — built on Franken UI (the no-build HTML port of shadcn/ui, MIT), vendored locally — zero CDN dependency for offline/intranet delivery; auto light/dark
  • Page↔agent contract — pages embed a ```json fence contract in their prompts (no persona coupling); replies render as boards/tables/stat cards
  • Frontends are verified too — assemble-then-verify gains two frontend gates: page gate (HTTP 200 + slots filled) and loop gate (a real session opened with the page's exact parameters must echo a token); carried runs re-run the page gate only
  • One durable ledger — the equipment lane pins the default database (workspace data.db) and pre-builds its schema, so frontend sessions and DSH conversations write the same store

Architecture

┌───────── Induction pipeline (supply chain) ────────────────────────────┐
│ OSS library / public API / client OpenAPI / client documents           │
│   → slice capabilities → MCP adapter → gate (smoke / retrieval) → catalog │
└───────────────────────────────────────────────────────────────────────┘
                              ↓
┌───────── Assembly (capability consumption) ───────────────────────────┐
│ capabilities.yml (public or client) + parallel federation → LLM match  │
│   → emit preset + BOM + copy knowledge into kb/                       │
│   → verify: derive probe (single-turn or scenario) → real session → PASS/FAIL │
└───────────────────────────────────────────────────────────────────────┘
                              ↓
┌───────── Delivery (FDE) ──────────────────────────────────────────────┐
│ solution apply (assemble every agent in the manifest)                 │
│   → solution handover: report (verdicts / params / pending secrets /  │
│     knowledge / BOM / rebuild command)                                │
└───────────────────────────────────────────────────────────────────────┘
                              ↓
┌───────── Runtime (the harness's territory) ───────────────────────────┐
│ session picks the preset → DSH mounts each row → agent calls the parts │
└───────────────────────────────────────────────────────────────────────┘

The assembler is a Cordis plugin and its output is a Cordis composition manifest (one preset row = one plugin instance); parts are external MCP server processes bridged by @deepseek-ai/dsh-mcp-client. The assembler exists only at assembly time — once a session is running its process is gone and nothing changes.


Quick start

1. Install

Add the plugin to a DSH profile's patch layer (e.g. ~/.dsh/profiles/web/cordis.patch.yml):

- insert:
    - id: dsh-assembler
      name: '@dsh-external/dsh-assembler'

Dependency: "@dsh-external/dsh-assembler": "link:/path/to/dsh-assembler".

Some service parts need the operator's contact details (SEC mandates a contactable User-Agent; Crossref/OpenAlex polite pools want a mailto). Copy .env.example to .env and fill in your own — not credentials, but not anyone else's address either.

2. Assemble one agent

/assemble build a support bot that looks up customers, opens tickets, and hands off to a human [--name customer-service-bot] [--param timezone=Asia/Shanghai]

Or simply say it in any session — the agent calls the assemble tool on its own, and reasoning, tool card and result all render inline.

3. Deliver a solution (the FDE path)

npm run solution -- init acme-service --client acme     # manifest skeleton
# edit the agents list in solutions/acme-service/solution.yml
npm run solution -- apply solutions/acme-service/solution.yml --port 3096
npm run solution -- handover solutions/acme-service/solution.yml

HANDOVER.md lists what was delivered with each verdict, the deployment parameters, the pending-credential checklist, knowledge packs with source and version, the supply-chain BOM, and the rebuild command — assembled from the artifacts, with nothing hand-filled. Anything that can be forgotten does not belong in a handover.


Layout

dsh-assembler/
├── src/
│   ├── index.ts            # assembly core: catalog, matching, emission, BOM, params, secrets, knowledge
│   ├── verify.ts           # assemble-then-verify: probe derivation (single/scenario) + real-session driver
│   ├── persona-lint.ts     # mechanical persona checks
│   └── assemble-tool.ts    # the assemble agent tool
├── capabilities.yml        # ★ public catalog: capability entries + mcp-servers + requiredSecrets
├── index/                  # ★ public part index (origin/licence/terms) + smoke reports
├── generated/              # ★ part library: 78 MCP adapter servers, one directory each
├── catalogs/<client>/      # ★ client-private catalog: its own generated/ index/ capabilities.yml knowledge/
├── solutions/<name>/       # ★ solution pack: solution.yml + last-apply.json + HANDOVER.md
├── bench/results/          # assembly-quality ledgers (run-tagged, committed)
├── presets/
│   └── agent-template.yml  # preset template ({{persona}}/{{packageRows}}/{{extraRows}}/{{param:k}})
└── scripts/
    ├── index-add.mjs       # ★ induction pipeline CLI
    ├── solution.mjs        # ★ solution pack CLI
    ├── assembly-bench.mjs  # 45-item assembly benchmark
    └── link-dsh.mjs        # link DSH peer packages

The capability catalog

Four kinds of capability:

via Source Example
package a tool from this repo / an own plugin package crm-query
harness a DSH built-in tool content-search
mcp MCP server tools (federated at assembly time) mcp-weather-forecast-current-weather, 229 of them
knowledge client teaching material (copied into kb/) acme-policies-kb

78 parts / 215 tools — 61 library-backed, 13 service-backed, 4 first-party.

Service-backed parts — live data and external systems

Part Tools Source Licence / terms Credentials
package-registry package-info package-versions check-license npm + PyPI registries registry ToS none
weather-forecast current-weather daily-forecast Open-Meteo CC-BY-4.0 none
currency-rates latest-rates historical-rate convert-amount Frankfurter (ECB data) Public-Domain-ECB none
geocode geocode-address reverse-geocode OpenStreetMap Nominatim ODbL-1.0 none
public-holidays list-holidays is-workday available-countries Nager.Date MIT none
worldbank-data country-indicator common-indicators World Bank Open Data CC-BY-4.0 none
sec-filings lookup-cik company-filings U.S. SEC EDGAR Public-Domain-US-Gov none
scholar-search search-published search-preprints doi-lookup Crossref + arXiv CC0-1.0 / arXiv terms none
wiki-facts page-summary search-entity entity-facts Wikimedia (Wikipedia + Wikidata) CC-BY-SA-4.0 none
research-graph search-works work-citations author-works OpenAlex CC0-1.0 none
feishu-messaging send-message list-chats feishu-capabilities 飞书开放平台 Feishu API ToS FEISHU_APP_ID FEISHU_APP_SECRET
slack-messaging post-message list-channels slack-capabilities Slack API Slack API ToS SLACK_BOT_TOKEN
github-issues list-issues get-issue create-issue github-capabilities GitHub REST API GitHub ToS GITHUB_TOKEN (optional)

First-party parts — thin shells over Node built-ins, zero third-party deps

binary-write(write-binary-file) · crypto-hash(hash-text, hmac-sign, generate-uuid) · compress-gzip(compress, decompress) · dns-lookup(resolve-domain, reverse-lookup)

Library-backed parts, by domain

Domain Parts (tool count)
Documents & office pdf-generate(4) · pdf-extract(3) · pdf-report(1) · docx-generate(3) · docx-extract(2) · pptx-generate(1) · excel-read-write(4) · zip-archive(4)
Data formats csv-parse(3) · yaml-convert(2) · toml-parse(2) · xml-parse(3) · json-query(2) · json-schema-validate(2) · html-parse(4) · html-to-text(4)
Text processing markdown-render(3) · html-to-markdown(1) · readability-extract(2) · text-diff(3) · template-render(4) · fuzzy-search(4) · text-encoding(2)
Chinese language pinyin-convert(2) · chinese-convert(2) · word-segment(2) · num-to-chinese(2)
Computation math-eval(2) · currency-calc(4) · number-format(4) · semver-check(3) · geo-distance(3) · color-convert(2)
Time & calendars date-format(4) · cron-parse(2) · calendar-parse(3) · calendar-generate(4) · rrule-expand(2)
Databases sqlite-query(3) · mysql-query(4) · postgres-query(4)
Network & messaging http-request(4) · email-send(4) · email-fetch(4) · rss-parse(4)
Media & recognition image-process(4) · ocr-parse(3) · qrcode-generate(4) · barcode-generate(2) · exif-read(1) · file-type-detect(1)
Security & validation jwt-decode(2) · ip-utils(2) · string-validate(2) · fake-data(2) · phone-parse(2)
Engineering tools github-api(4) · browser-automate(4) · url-slugify(3) · transliterate(2) · safe-filename(2)
Full tool-level inventory - **email-send** — `send-email`, `verify-smtp-config`, `parse-email-addresses`, `create-test-account`
nodemailer/nodemailer@v6.9.13 · MIT - **email-fetch** — `list-mailboxes`, `search-messages`, `fetch-message`, `list-message-summaries`
postalsys/imapflow@v1.0.162 · MIT - **http-request** — `http-request`, `http-get`, `http-post`, `build-url`
axios/axios@v1.7.2 · MIT - **html-parse** — `extract-text`, `extract-attributes`, `query-elements`, `serialize-html`
cheeriojs/cheerio@v1.0.0-rc.12 · MIT - **csv-parse** — `parse-csv`, `unparse-csv`, `validate-csv`
papaparse/papaparse@5.4.1 · MIT - **pdf-generate** — `create-pdf`, `merge-pdfs`, `extract-pages`, `pdf-info`
Hopding/pdf-lib@v1.17.1 · MIT - **date-format** — `format-date`, `parse-date`, `date-diff`, `date-manipulate`
iamkun/dayjs@v1.11.11 · MIT - **sqlite-query** — `query`, `execute`, `list-tables`
WiseLibs/better-sqlite3@v11.1.2 · MIT - **github-api** — `get-user`, `get-repo`, `list-org-repos`, `search-repositories`
octokit/rest.js@v20.1.1 · MIT - **markdown-render** — `render-markdown`, `render-markdown-inline`, `tokenize-markdown`
markedjs/marked@v12.0.2 · MIT - **pdf-extract** — `get-pdf-text`, `get-pdf-info`, `search-pdf-text`
pdf-parse/pdf-parse@1.1.1 · MIT - **excel-read-write** — `read-xlsx-file`, `write-xlsx-file`, `read-csv-file`, `write-csv-file`
exceljs/exceljs@v4.4.0 · MIT - **docx-generate** — `docx-generate-text`, `docx-generate-table`, `docx-patch-document`
dolanmiu/docx@8.5.0 · MIT - **zip-archive** — `zip-list-entries`, `zip-read-file`, `zip-create-archive`, `zip-update-archive`
cthackers/adm-zip@v0.5.12 · MIT - **fuzzy-search** — `fuzzy-search`, `fuse-create-index`, `fuse-search-with-index`, `fuse-config`
krisk/Fuse@v7.0.0 · Apache-2.0 - **template-render** — `render-template`, `precompile-template`, `render-precompiled`, `validate-template`
handlebars-lang/handlebars.js@v4.7.8 · MIT - **html-to-text** — `html-to-text`, `html-to-text-batch`, `html-to-text-table`, `html-to-text-links`
html-to-text/node-html-to-text@9.0.5 · MIT - **xml-parse** — `xml-validate`, `xml-parse`, `xml-build`
NaturalIntelligence/fast-xml-parser@v4.4.0 · MIT - **image-process** — `image-info`, `image-resize`, `image-convert`, `image-thumbnail`
lovell/sharp@v0.33.4 · Apache-2.0 - **rss-parse** — `parse-rss-string`, `parse-rss-url`, `extract-feed-items`, `parse-feed-metadata`
rbren/rss-parser@v3.13.0 · MIT - **calendar-parse** — `parse-ics`, `parse-ics-file`, `fetch-ics-url`
jens-maus/node-ical@0.19.0 · Apache-2.0 - **calendar-generate** — `create-calendar`, `create-event`, `create-all-day-event`, `create-recurring-event`
sebbo2002/ical-generator@v7.1.0 · MIT - **mysql-query** — `mysql-query`, `mysql-list-tables`, `mysql-describe-table`, `mysql-test-connection`
sidorares/node-mysql2@v3.10.0 · MIT - **number-format** — `format-number`, `unformat-number`, `arithmetic`, `validate-number`
adamwdraper/Numeral-js@2.0.6 · MIT - **qrcode-generate** — `qr-generate-png`, `qr-generate-data-url`, `qr-generate-svg`, `qr-generate-terminal`
soldair/node-qrcode@v1.5.3 · MIT - **postgres-query** — `postgres-test-connection`, `postgres-list-tables`, `postgres-describe-table`, `postgres-query`
brianc/node-postgres@pg@8.12.0 · MIT - **browser-automate** — `browser-open`, `browser-extract`, `browser-click`, `browser-screenshot`
microsoft/playwright@v1.45.0 · Apache-2.0 - **ocr-parse** — `ocr-languages`, `ocr-psm-modes`, `ocr-recognize`
naptha/tesseract.js@v5.1.0 · Apache-2.0 - **currency-calc** — `currency-calc`, `currency-format`, `currency-distribute`, `currency-parse`
scurker/currency.js@v2.0.4 · MIT - **readability-extract** — `extract-article`, `extract-batch`
mozilla/readability@0.5.0 · MIT - **pdf-report** — `create-report-pdf`
Hopding/pdf-lib@v1.17.1 · MIT - **binary-write** — `write-binary-file`
first-party@- · BSD-3-Clause - **text-diff** — `create-patch`, `apply-patch`, `diff-words`
kpdecker/jsdiff@v9.0.0 · BSD-3-Clause - **crypto-hash** — `hash-text`, `hmac-sign`, `generate-uuid`
first-party@v- · BSD-3-Clause - **math-eval** — `evaluate`, `unit-convert`
josdejong/mathjs@v15.2.0 · Apache-2.0 - **cron-parse** — `next-runs`, `describe-fields`
harrisiirak/cron-parser@v5.10.0 · MIT - **semver-check** — `compare`, `satisfies`, `coerce-valid`
npm/node-semver@v7.8.5 · ISC - **yaml-convert** — `yaml-to-json`, `json-to-yaml`
eemeli/yaml@v2.9.0 · ISC - **pinyin-convert** — `to-pinyin`, `multi-tone`
zh-lx/pinyin-pro@v3.29.2 · MIT - **chinese-convert** — `s2t`, `t2s`
nk2028/opencc-js@v1.4.1 · MIT AND Apache-2.0 - **html-to-markdown** — `html-to-markdown`
mixmark-io/turndown@v7.2.4 · MIT - **text-encoding** — `decode-base64`, `encode-to-base64`
ashtuchkin/iconv-lite@v0.7.3 · MIT - **phone-parse** — `parse-phone`, `format-phone`
catamphetamine/libphonenumber-js@v1.13.11 · MIT - **compress-gzip** — `compress`, `decompress`
first-party@v- · BSD-3-Clause - **dns-lookup** — `resolve-domain`, `reverse-lookup`
first-party@v- · BSD-3-Clause - **json-query** — `query`, `query-multi`
jmespath/jmespath.js@v0.16.0 · Apache-2.0 - **json-schema-validate** — `validate`, `check-schema`
ajv-validator/ajv@v8.20.0 · MIT - **toml-parse** — `toml-to-json`, `json-to-toml`
squirrelchat/smol-toml@v1.8.0 · BSD-3-Clause - **docx-extract** — `docx-to-text`, `docx-to-html`
mwilliamson/mammoth.js@v1.12.1 · BSD-2-Clause - **pptx-generate** — `create-pptx`
gitbrent/pptxgenjs@v4.0.1 · MIT - **barcode-generate** — `barcode-png`, `barcode-types`
metafloor/bwip-js@v4.11.2 · MIT - **string-validate** — `validate-string`, `sanitize-string`
validatorjs/validator.js@v13.15.35 · MIT - **fake-data** — `fake-records`, `fake-text`
faker-js/faker@v10.6.0 · MIT - **num-to-chinese** — `to-chinese`, `from-chinese`
cnwhy/nzh@v1.0.14 · BSD-2-Clause - **jwt-decode** — `decode-jwt`, `verify-jwt-hs256`
panva/jose@v6.2.9 · MIT - **ip-utils** — `parse-ip`, `cidr-match`
whitequark/ipaddr.js@v2.5.0 · MIT - **transliterate** — `transliterate-text`, `make-slug`
dzcpy/transliteration@v2.6.1 · MIT - **rrule-expand** — `expand-rrule`, `describe-rrule`
jkbrzt/rrule@v2.8.1 · BSD-3-Clause - **exif-read** — `read-exif`
MikeKovarik/exifr@v7.1.3 · MIT - **file-type-detect** — `detect-file-type`
sindresorhus/file-type@v22.0.2 · MIT - **color-convert** — `convert-color`, `contrast-check`
Evercoder/culori@v4.0.2 · MIT - **word-segment** — `segment-text`, `extract-keywords`
linonetwo/segmentit@v2.0.3 · MIT - **geo-distance** — `distance`, `bearing`, `center-and-bounds`
manuelbieh/geolib@v3.3.14 · MIT - **url-slugify** — `slugify`, `slugify-unique`, `slugify-custom`
sindresorhus/slugify@v3.0.0 · MIT - **safe-filename** — `sanitize`, `sanitize-path`
sindresorhus/filenamify@v7.0.2 · MIT - **package-registry** — `package-info`, `package-versions`, `check-license`
https://registry.npmjs.org · registry ToS - **weather-forecast** — `current-weather`, `daily-forecast`
https://api.open-meteo.com/v1 · CC-BY-4.0 - **currency-rates** — `latest-rates`, `historical-rate`, `convert-amount`
https://api.frankfurter.dev/v1 · Public-Domain-ECB - **geocode** — `geocode-address`, `reverse-geocode`
https://nominatim.openstreetmap.org · ODbL-1.0 - **public-holidays** — `list-holidays`, `is-workday`, `available-countries`
https://date.nager.at/api/v3 · MIT - **worldbank-data** — `country-indicator`, `common-indicators`
https://api.worldbank.org/v2 · CC-BY-4.0 - **sec-filings** — `lookup-cik`, `company-filings`
https://data.sec.gov · Public-Domain-US-Gov - **scholar-search** — `search-published`, `search-preprints`, `doi-lookup`
https://api.crossref.org · CC0-1.0 / arXiv terms - **wiki-facts** — `page-summary`, `search-entity`, `entity-facts`
https://en.wikipedia.org/api/rest_v1 · CC-BY-SA-4.0 - **research-graph** — `search-works`, `work-citations`, `author-works`
https://api.openalex.org · CC0-1.0 - **feishu-messaging** — `send-message`, `list-chats`, `feishu-capabilities`
https://open.feishu.cn/open-apis · Feishu API ToS - **slack-messaging** — `post-message`, `list-channels`, `slack-capabilities`
https://slack.com/api · Slack API ToS - **github-issues** — `list-issues`, `get-issue`, `create-issue`, `github-capabilities`
https://api.github.com · GitHub ToS

Licences

Wrapped code (library + first-party parts): MIT 46 · Apache-2.0 7 · BSD-3-Clause 7 · ISC 2 · BSD-2-Clause 2 · MIT AND Apache-2.0 1

Data licence / terms (service-backed parts): CC-BY-4.0 2 · registry ToS 1 · Public-Domain-ECB 1 · ODbL-1.0 1 · MIT 1 · Public-Domain-US-Gov 1 · CC0-1.0 / arXiv terms 1 · CC-BY-SA-4.0 1 · CC0-1.0 1 · Feishu API ToS 1 · Slack API ToS 1 · GitHub ToS 1

All permissive — no copyleft exposure in code. Service parts additionally record the data licence, which is a different obligation: Nominatim is ODbL and Wikipedia is CC-BY-SA (attribution / share-alike duties), so both are recorded per entry and travel into each assembly's BOM.

Machine-readable inventory with every repo@rev, licence, terms, rate limit and tool description: index/catalog.yml.


Induction pipeline (the CLI)

The design premise is that the caller is an agent. The CLI does the deterministic half — fetch the source, inventory it, write a work order, install, gate, register — and leaves "which capabilities to slice, and how to write the adapter" to the caller. Every subcommand ends with one machine-readable JSON verdict.

# an OSS library
npm run index:add -- kpdecker/jsdiff --pkg diff --id text-diff
npm run index:verify -- text-diff        # install → smoke (exit 0 required) → independent listTools → report
npm run index:register -- text-diff      # idempotent; federation picks it up on the next assembly

# a public API (pins terms/rate-limit/data-licence instead of a version)
node scripts/index-add.mjs scaffold - --service https://api.open-meteo.com/v1 --id weather-forecast \
  --provider 'Open-Meteo' --license CC-BY-4.0 --terms https://open-meteo.com/en/terms --rate-limit 'free for non-commercial'

# a client's own system (the FDE day-one move): OpenAPI → endpoint work order → client catalog
node scripts/index-add.mjs from-spec <spec-url|file> --id <id> --client acme \
  --requires-secret "TOKEN:what it is for, commas allowed;OTHER:second one"

# a client's documents (past the retrieval gate)
node scripts/index-add.mjs knowledge <docs-dir> --id acme-policies --client acme --version 2026-08
# write probes.json (questions + expected snippets), then:
node scripts/index-add.mjs knowledge-verify acme-policies --client acme

# hands-free: one command, start to finish (needs a running web profile)
npm run index:auto -- sindresorhus/slugify --pkg @sindresorhus/slugify --id url-slugify

npm run index:check     # full regression: every part's smoke (network parts recorded SKIPPED when offline)
node scripts/index-add.mjs coverage   # capability map, for semantic dedup

The gate lives in the pipeline: if verify fails, register refuses. Dedup has two layers — a mechanical gate (same id / same npm package / same upstream repo), and capability-level judgement against the coverage map: the catalog holds capabilities, not libraries.


Sample output

Assemble-then-verify, multi-turn

For "a bookkeeping assistant that records income and expenses locally and can query and total them later", the deriver judged the work to outlive a turn and produced a 3-turn scenario:

自动验证:PASS — 多轮场景「prove the assistant persists entries to SQLite and can query/total them in later turns」共 3 轮,逐轮通过
  turn 1 ✓ "record income: project payment 8899, memo INV-7781…"      marks [INV-7781, 8899]
  turn 2 ✓ "record expense: office supplies 1200, memo OFFICE-2201…"  marks [OFFICE-2201, 1200]
  turn 3 ✓ "query the ledger, list every entry and total them"        marks [INV-7781, OFFICE-2201, 8899]

Turn 3 reads what turns 1 and 2 wrote, which is what makes state continuity provable rather than assumed. A pure-compute requirement gets a single-turn probe instead.

The four credential states

# required credential missing: assembly succeeds, probe SKIPPED, guidance given
自动验证:跳过(pending credential: SLACK_BOT_TOKEN — the assembly is correct but cannot reach the service;
              configure it and re-run the assembly to verify)
所需凭证:SLACK_BOT_TOKEN(pending) — Slack Bot User OAuth Token (xoxb-…)

# optional credential: the anonymous path is exercised and verification PASSes
自动验证:PASS — probe "inspect the public repo octocat/Hello-World…" passed
所需凭证:GITHUB_TOKEN(optional; degrades to anonymous when unset)

Parts BOM — parts.lock.yml (excerpt)

preset: currency-qr-assistant
parts:
  - capability: mcp-qrcode-generate-qr-generate-png
    server: qrcode-generate
    serverName: qrcode-generate-d0fb25cc   # read back from the preset bytes, always matches what mounts
    repo: soldair/node-qrcode
    rev: v1.5.3
    license: MIT
    verified: true
  - capability: mcp-weather-forecast-current-weather
    kind: service
    service: https://api.open-meteo.com/v1
    terms: https://open-meteo.com/en/terms
    rateLimit: free for non-commercial; commercial needs a subscription
knowledge:
  - id: acme-policies
    docs: 2
    source: ACME support knowledge base export
    version: 2026-08

Development

npm run link:dsh   # link DSH peer packages (needs DSH_SOURCE or ~/.dsh/source/current)
npm run build      # tsc → lib/
npm test           # build + three suites (generation invariants / verdicts & BOM / federation cache)
npm run index:check   # full part-smoke regression
npm run bench      # 45-item assembly benchmark

Changes under lib/ need a DSH web restart; changes to capabilities.yml do not (the catalog is read at assembly time).

Two environment facts to know when writing a network part: Node's global fetch ignores HTTP(S)_PROXY unless NODE_USE_ENV_PROXY=1, and the MCP SDK's StdioClientTransport only forwards a whitelist of environment variables, so proxy settings do not reach a part process by themselves. The pipeline handles both; a hand-written smoke must pass the environment down itself.


Licence

BSD-3-Clause. Upstream licences for the wrapped libraries are recorded per entry in index/catalog.yml; service-backed parts record their data licence and terms in their catalog entry the same way.