English | 中文
A reproducible census of the DeepSeek Harness plugin ecosystem: what the
dsh-plugin topic actually contains, and which entries can actually be
installed.
Disclaimer. This is a personal, community-run project. It is not an official DeepSeek product and does not represent DeepSeek's position. Inclusion here is not an endorsement, and exclusion is not a judgement of quality. The maintainer contributes to DeepSeek Harness upstream; that does not make this catalogue authoritative. Verdicts are produced by the scripts in this repository and are reproducible — check them rather than trusting them.
The dsh-plugin topic held 6923
repositories on 2026-08-18, up from 1064 four days earlier. GitHub sorts topic
pages by stars, and the most-starred entries are the least likely to be plugins.
The topic is enumerated in full rather than sampled: 7746 unique repositories, by sharding around the search API's 1000-result per-query ceiling. Contract probing then accumulates across runs, so the verdict figures below cover the 3098 repositories probed so far:
| Stars | Satisfies the plugin contract |
|---|
| 0 | 64.7% | | 1-2 | 66.4% | | 3-9 | 76.8% | | 10-49 | 73.1% | | 50+ | 50.5% | | all | 68.3% |
Only the top band is depressed, and its contents explain why: the
highest-starred repositories carrying this topic are mostly the catalogues of
it — awesome-dsh-plugin (7773), AdamPlatin123/awesome-dsh-plugins (1158) —
plus adjacent tooling. None is a plugin, none claims to be, and all sort above
the plugins a visitor is looking for.
An earlier measurement (2026-08-14, n=999) found compliance falling monotonically with stars, from 64.5% at zero to 36.4% above fifty. That pattern no longer holds and the earlier figures should not be cited: the ecosystem grew 6.5x in four days, and this probe now finds bundles in subpackages, which the earlier one missed.
All 3098 probed repositories, by verdict:
| Verdict | Count | Share |
|---|
| CONTRACT_OK | 2116 | 68.3% |
| NO_DSH_FIELD | 427 | 13.8% |
| NO_PACKAGE_JSON | 314 | 10.1% |
| DSH_WITHOUT_BUNDLE_PATCH | 209 | 6.7% |
| VENDORED_HARNESS | 14 | 0.5% |
| PATCH_FILE_MISSING | 5 | 0.2% |
| MALFORMED_PACKAGE_JSON | 4 | 0.1% |
| PATCH_FILE_EMPTY_OR_INVALID | 4 | 0.1% |
| BUNDLE_UNDETERMINED | 2 | 0.1% |
| TREE_UNREADABLE | 2 | 0.1% |
| FIRST_PARTY_HARNESS | 1 | 0.0% |
Probing accumulates: each run spends its API allowance on repositories never probed, then on the stalest, so this table covers a growing share of the
7746 enumerated repositories rather than a fresh sample each time.VENDORED_HARNESS marks a repository that ships a copy of the harness rather
than a plugin: it satisfies the contract because it contains DSH's own bundle
packages. All 14 have fork: false, so they are source copies that neither an
owner check nor a fork check detects; they are identified by first-party package
name. The largest is fufankeji/deepseek-harness-studio at 260 stars.
These figures describe one sample at one moment, and the moment is short. All 257
repositories judged non-compliant were re-probed a few hours after the first
pass: two had become compliant because their authors added dsh.bundle in the
interval — songoao25/dsh-plugin-guardian at 08:58 and
xinyuehtx/dsh-plugin-hooks-ordering at 07:54 on the same day. Neither change
came from a probe fix; the authors simply shipped.
Treat any number here as a reading with a timestamp, not a standing fact. The scripts are included so the reading can be retaken rather than trusted.
Earlier revisions of this README reported catalogue sizes that moved by tens of entries between runs hours apart. That variation was an artefact of sampling one page of the topic: each run drew a different set. Enumeration now covers the whole topic, so a change between runs reflects the ecosystem rather than which repositories happened to be sampled — but the timestamp still matters, because the topic grew 6.5x in four days.
Verification mirrors what DSH actually enforces when it loads a bundle, in
packages/boot/app-boot/src/profile.ts:388-397. Three tiers, each a separate
failure the loader would raise:
| Tier | Check | Loader behaviour without it |
|---|---|---|
1 DECLARED |
package.json has dsh.bundle.patch |
throws at profile.ts:391-393 |
2 RESOLVED |
the declared path exists | fails to read at profile.ts:395 |
3 PARSED |
the patch file holds patch entries | fails to parse at profile.ts:396 |
Only PARSED entries are listed as plugins.
Honest limit: tiers 2 and 3 reject little in practice — 9 of the
419 repositories that declare a patch fail them.Static verification is close to exhausted at tier 1, and the remaining uncertainty can only be resolved by installing a plugin. Install verification is not implemented; nothing here claims a plugin runs.
Each verified plugin is attributed to the surface it extends, with the evidence ranked by strength and the confidence published alongside:
| Confidence | Basis | Count | Share |
|---|
| high | depends on @deepseek-ai/dsh-client-* (client) or @deepseek-ai/dsh-host-* and host-only packages (host) | 1286 | 60.8% |
| declared | the plugin's own dsh.client or dsh.host block declares the surface | 419 | 19.8% |
| medium | depends on @deepseek-ai/*, but no dependency distinguishes client from host — surface indeterminate | 87 | 4.1% |
| low | no @deepseek-ai/* dependency; surface guessed from a name or description keyword | 234 | 11.1% |
| none | no dependency evidence and no keyword match — not attributed at all | 90 | 4.3% |
The 1373 high and medium rows rest on an installed
dependency. A further 419 are declared: the plugin's own dsh
block names the surface, which is the author's statement rather than an installed
package, so it ranks below dependency evidence and above a guess.
Attribution read only dependencies until recently, which discarded those declarations and published a guess in their place. 52% of those guesses were wrong about the surface — 147 of 283 changed once the declaration was read, so this was a correctness defect rather than a labelling one.
A low attribution is a guess from a word in the repository name and is labelled
as one. A none row carries surface indeterminate and empty evidence: it is an
absence of attribution, not a weak attribution, and it should not be read as a
statement about the plugin.
Contract compliance says a plugin declares a valid patch. It does not say the package can be obtained. Two failure modes are decidable without installing anything:
| Verdict | Meaning | Count |
|---|
| published | the declared name resolves on the npm registry | 1020 |
| git-only | absent from npm; installable from a Git specifier | 1032 |
| unpublishable-scope | names itself under @deepseek-ai/ from a repository outside that organisation | 64 |
A further 14 repositories carry @deepseek-ai/dsh-base verbatim. They are
not misnamed plugins but vendored copies of the harness, so they are
classified VENDORED_HARNESS and excluded from the catalogue rather than
counted here.
The blocked entries are listed separately in the catalogue. They satisfy the
bundle contract, but only the DeepSeek organisation can publish to the
@deepseek-ai scope, so those names cannot be created by their current owners
and dsh plugin add @deepseek-ai/... fails for every one of them. This is a
naming defect that renaming fixes; it is not a statement about code quality.
The check deliberately does not flag lookalike scopes such as
@deepseek-ai-community. Those are separate, independently registrable scopes
whose owners can publish normally, so they are installable — confusing branding
is a different concern this catalogue does not adjudicate.
scripts/scan-decay.mjs re-checks catalogued entries and reports four
conditions, flagging and never removing: gone (404), archived, dormant
(no push within 30 days) and unbundled (the contract no longer holds). A probe
that cannot reach a conclusion is reported as inconclusive, never as decay,
because every decay state invites a deletion the evidence may not support.
Over all 2116 entries:
| State | Count |
|---|
| live | 1544 |
| archived | 6 |
| gone | 3 |
| unbundled | 2 |
| dormant | 0 |
| inconclusive | 283 |
dormant: 0 reflects the topic's age, not its health. The oldest push among
the catalogued entries is 6 days old, so a 30-day dormancy threshold
cannot fire yet. This is no longer the sampling artefact it once was — the
enumeration covers the whole topic rather than its most-recently-updated page —
but the figure still says nothing about long-term maintenance, because nothing in
this ecosystem has had time to go quiet.
inconclusive is 283 entries (15.4%), and that is a
limitation of the scan rather than a finding about those repositories. The scan
spends the same hourly API allowance as the probe, and a run that exhausts it
reports what it could not check instead of guessing. The refusal threshold is 40%,
so this run published; a reader should treat the decay table as covering the
entries that were actually reachable.
The scan is incremental for the same reason the probe is: re-checking every entry cost two API calls per catalogued plugin and grew with the catalogue, reaching 85% of the hourly allowance and failing a scheduled run outright at 45.4% inconclusive. Each run now checks a bounded batch, oldest results first, and carries the rest forward, so a state in this table may have been observed on an earlier run than the figures above.
The 11 entries flagged as decayed (inconclusive is not decay and is excluded):
| Entry | State |
|---|---|
1HelloMan1/dsh-stats-dashboard |
archived — repository is archived |
ccch1mneyyy/dsh-working-activity |
archived — repository is archived |
ccq1/dsh-side-panel |
archived — repository is archived |
haiyoucuv/dsh-model-provider-label |
archived — repository is archived |
Hanihahaha/deepseek-harness-plugins |
archived — repository is archived |
hyls9527/dsh-plugins |
archived — repository is archived |
E83737664/dsh-skills-manager |
gone — repository returns 404 |
Gdnaiteab/pingo-dsh-plugin |
gone — repository returns 404 |
omdsh-plugins/omdsh-base |
gone — repository returns 404 |
ghbhiee/dsh-plugins |
unbundled — no dsh.bundle.patch found; previously at packages/cli-session/package.json |
jlu-lujing/dsh-kit |
unbundled — no dsh.bundle.patch found; previously at packages/dsh-kit/package.json |
Everything above is decidable. This section is not: scripts/ai-review.mjs
asks a model to score a plugin 1-5 on how much a competent DSH user would trust
it, and publishes the result as the subjective judgement it is. The rubric lives
in the script, and every review records the commit SHA it read, the SHA of the
README bytes it was shown, and the prompt version that produced it, so any score
can be re-derived from that exact commit.
A published score is the mean of repeated samples, not a single verdict. Each run re-draws its sample, so an entry drawn again is scored again and the scores accumulate; the record carries the mean, the number of runs, and every raw score. Samples taken under a different prompt version or against a different commit are discarded rather than averaged in.
This is a correction, not a refinement. Scoring once and never re-asking assumed
the verdict is a function of the content. It is not: dsh-toolkit and dsh-TUI
once scored 4 and 3 on an identical commit SHA and identical README bytes, and
the first entry to be drawn twice under the new mechanism
(wangzhuo-coding/geo-content-optimizer) scored 5 then 4 on unchanged bytes.
Over 262 reviewed entries, by mean score:
| Score | Meaning | Count | Share |
|---|
| 5 | substantial, documented, tested | 142 | 54.2% | | 4 | solid and usable | 112 | 42.7% | | 3 | ordinary, thin, undocumented | 5 | 1.9% | | 2 | barely a plugin | 1 | 0.4% | | 1 | empty or broken | 2 | 0.8% |
This distribution is the finding, and it makes the score nearly useless as a filter. Almost nothing scores below 4. Two explanations were tested and only one survived:
- The rubric cannot reach low scores. False. Given a synthetic 3-file plugin with an 18-byte README and no dependencies, the same prompt returns 2.
- The population is already filtered. Supported. Every entry here cleared all three tiers of the bundle contract, which excludes stubs before review begins.
Two hand-checks confirmed the scores rather than the suspicion that produced
them. fengs2021/dsh-plugin-catalog has 5 files and no tests, which looks thin
until the files are opened: 30 KB of implementation across lib/index.js and
lib/client.js, two documented HTTP endpoints, and a rollback path for its only
write. It scored 4, correctly. AngelosZou/dsh-python-env has a null GitHub
description — and 42 files, 21 lib/ modules, 12 test files, a CHANGELOG and
bilingual docs. It scored 5, correctly. File count and description length are
bad proxies for depth, which is the reason a model is asked at all.
Sampling is star-neutral by construction, because an earlier version of this feature was not: selecting the catalogue head drew a sample averaging 1055 stars from a catalogue averaging 26 and containing none of its 326 zero-star entries. Selection is now a seeded shuffle over repository names, re-drawn per run. The published sample has median 2 stars and includes 21 zero-star entries of 79.
Repeated samples agree more often than not. Of the 13 entries sampled more than once, 12 returned an identical score and
1 moved, with a mean spread of 0.08 points and amaximum of 1. An earlier reading of this table, taken when only two entries had been sampled twice and both had moved, described the scores as noisy; that reading was too small to support the claim and this one supersedes it.
The figure is still provisional. Repeat samples accumulate only where two draws
collide, so the count grows slowly, and a single-run score (runs: 1) remains one
observation rather than a stable value — prefer entries with a higher runs
count.
Coverage is 262 of 2116 and grows with each scheduled run, which reviews up to 180 entries under a seed derived from the hour. The batch is the smaller of that cap and what the remaining hourly API allowance affords, and the reviewer stops at a 15-minute deadline so a slow run cannot exceed the job timeout and take the census down with it.
Reviews run in a pool of four rather than one at a time, measured at 4.83 seconds
each against the live endpoint instead of 17.6 sequentially. Output order follows
the draw, not completion, so the same seed produces byte-identical ordering. A failed review is recorded with
reviewed: false and no score, and a run in which more than 30% of reviews fail
exits non-zero rather than publishing a transport failure as an opinion about
someone's code.
The reviewer calls the Messages API directly when CENSUS_API_KEY is set, and
falls back to a local model CLI otherwise. The scheduled workflow uses the API
path and cannot fail the job: the census is decidable evidence and the review
is an opinion, so a model outage leaves data/reviews.jsonl untouched rather than
blocking the catalogue from publishing.
# 1. collect repositories carrying the topic
gh api "search/repositories?q=topic:dsh-plugin&sort=updated&per_page=100&page=1" > /dev/null
# 2. three-tier contract probe
node scripts/probe-contract.mjs < repos.txt > data/contract.jsonl
# 3. surface attribution
node scripts/attribute.mjs < data/contract.jsonl > data/surface.jsonl
# 4. installability (npm registry + reserved-scope check)
node scripts/installability.mjs < data/contract.jsonl > data/installability.jsonl
# 5. decay scan over the catalogue
node scripts/scan-decay.mjs < data/catalog.jsonl > data/decay.jsonl
# 6. model quality review (needs a model CLI; CENSUS_MODEL_CLI overrides `claude`)
# change the seed between runs: draws overlap, and overlapping entries average
node scripts/ai-review.mjs --limit 20 --seed 1 \
--existing data/reviews.jsonl < data/catalog.jsonl > data/reviews.next.jsonl
# 7. negative controls for every gate
node scripts/test-gates.mjs
node scripts/test-monorepo.mjs
node scripts/test-inconclusive.mjs
node scripts/test-decay.mjs
node scripts/test-attribution.mjs
node scripts/test-queue.mjs
node scripts/test-review.mjs
./scripts/test-fetch.shscripts/test-gates.mjs exists because a gate that cannot fail is not a gate.
It asserts the tier-3 predicate rejects empty, whitespace-only, comment-only,
and prose files, and that the reserved-scope rule flags foreign owners while
leaving the entitled owner, unrelated scopes, and lookalike scopes alone. Both
gates carry a sentinel that fails if the rule is ever made unconditionally
permissive, and both were verified by injecting a defect and confirming the
suite turns red.
| File | Contents |
|---|---|
data/repos-raw.jsonl |
repository metadata as returned by the search API |
data/contract-v3.jsonl |
three-tier contract verdicts |
data/surface-v3.jsonl |
surface attribution with confidence |
data/installability-v3.jsonl |
npm resolution and reserved-scope verdicts |
data/catalog.jsonl |
joined, classified catalogue |
data/decay.jsonl |
per-entry decay state |
data/reviews.jsonl |
model quality scores (mean, run count, raw samples), pinned to a commit and prompt version |
The search API returns at most 1000 results per query. scripts/enumerate-topic.mjs
shards around that ceiling by star bucket and then by creation day, reaching
Day boundaries come from result counts rather than sort order, because this search backend does not order by creation date.
Several catalogues already cover this ecosystem, with different tradeoffs:
AdamPlatin123/awesome-dsh-plugins— runtime-level testing (installs into DSH, drives a local model, observes tool invocation) via local cron rather than Actions. The most thorough verification in the ecosystem.Sunrisepeak/dsh-index— scheduled discovery workflow withpnpm packvalidation and stale-pin alarms.wangshunnn/oh-my-dsh— registry refreshed every eight hours fromtopic:dsh-plugin, with schema validation.
This project does not compete on per-repository depth, and two of the projects above are ahead of it on criteria:
awesome-dsh-plugin/awesome-dsh-plugin(6439 stars) runs a submission gate on every PR (scripts/check-submission.mjs):dsh.bundleanywhere in the tree, repository age, commit count. Its tree walk is more careful than this one — it treats a truncated tree or a manifest count above its cap as unknown rather than absent, which this probe does not. It also resolves npm publication (scripts/probe-npm.mjs) and guards against name squatting by checking that a published package points back at the same repository. Roughly 1300 submissions have passed through it.omdsh-dev/dsh-plugin-checkapplies 36 criteria to a single repository — manifest protocol, patch structure, build layout, TypeScript imports, row-id registration — far finer than the three contract tiers published here.
What remains distinct here:
- Unsolicited coverage. Both projects above examine repositories that are submitted to them. This one probes a topic-wide sample regardless of whether anyone submitted it, so it measures the ecosystem rather than its inbox.
- Vendored-harness detection. Five repositories in this sample carry
@deepseek-ai/dsh-baseverbatim withfork: false, and more exist outside it. They pass a contract check by containing DSH's own packages, and neither an owner check nor a fork check sees them. - Decay scanning that refuses to guess.
scripts/scan-decay.mjsreportsgone,archived,dormantandunbundled, and reports an unreadable probe asinconclusiverather than as decay, because every decay state invites a deletion the evidence may not support. - Published distributions of the whole sample rather than a curated selection: verdict shares, star-band compliance, and installability across all 3098 probed repositories, with the probe scripts included.
A deeper per-repository audit exists but is not published — see AUDIT-EXPERIMENTAL.md. Its first implementation produced false positives on every repository tested, so no criterion ships before it passes a hand-labelled fixture suite.
MIT