nonmean
dsh-plugin-explorer
DSH client plugin: browse GitHub repos tagged dsh-plugin (name, README, stats) with sync and search
- Stars
- 1
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-plugin-explorer
A local tool for the DeepSeek Harness web GUI that browses the GitHub
dsh-plugin topic: every repo's
name, README, and statistics (stars, forks, open issues, language,
license, last update, topics), with a Sync button to refresh the data and
an in-memory search over the cached snapshot.
It is a client plugin
(dsh.client dual-face package) that mounts one entry into the sidebar's
footer action list (sidebar.footer.action): a Plugin Explorer trigger
beside Settings that opens the tool's modal.
Using the tool
- Open the web GUI (
http://127.0.0.1:3080) and refresh the page. - Click Plugin Explorer in the sidebar footer (bottom-left, next to Settings).
- Press Sync to fetch the topic's repos from the GitHub search API (sorted by stars, up to 1000 repos — GitHub's search cap).
- Search filters the cached snapshot by repo name, description, topics, language, license, and README text (readmes the tool already fetched).
- Expand a repo's README to load and render it (fetched lazily on first expand, then cached).
Data persists in localStorage under dsh-plugin-explorer:repos:v1 and
dsh-plugin-explorer:readmes:v1, so the list survives restarts and works
offline. The readme cache is size-bounded (oldest entries evicted).
GitHub API notes
- Unauthenticated REST rate limits apply (60 req/h core, 10 req/min search, per IP). One full sync costs up to 10 search requests; READMEs cost one request each and are cached. Rate-limit hits abort gracefully with a message and keep the previous data (a partial sync is preserved).
- The tool talks to
api.github.comstraight from the browser (CORS is permissive); no token is required for public data. If you hit limits often, set aGITHUB_TOKEN? No — the browser cannot read your env; the tool is intentionally anonymous.
Architecture
src/
index.ts node half: empty apply() (mounts the loader row)
client/
index.ts browser half: inject ['slots'], slots.inject(...)
into 'sidebar.footer.action', register RepoExplorer
RepoExplorer.tsx trigger + modal: sync, search, list, README expand
RepoExplorer.module.css
github.ts GitHub REST client + localStorage cache + helpers
The browser bundle (lib/client.js) is emitted by tsdown in the shell's
closure-factory format: window.__ModuleLoader__.load({ id, factory }) with
the frozen platform module table as externals (react, @deepseek-ai/cordis,
@deepseek-ai/dsh-client-ui-primitives, ...), CSS Modules compiled by
lightningcss and injected as <style data-plugin> tags.
Mounting into the GUI
The tool is mounted through the web profile's user patch layer (no changes
to the DeepSeek Harness checkout):
# ~/.dsh/profiles/web/cordis.patch.yml
- insert:
- id: dsh-plugin-explorer
name: dsh-plugin-explorer
plus a symlink so the profile can resolve the package:
ln -s /Users/boxiao/Documents/dev/dhs-plugin-explorer \
/Users/boxiao/.dsh/profiles/node_modules/dsh-plugin-explorer
New rows are read at GUI boot (config hot-reload is off in the web profile),
so a restart of the dsh web process was required once. After that,
rebuilds of lib/client.js hot-reload through the client-plugin HMR chain
(the host polls the bundle and broadcasts rebuilt over SSE; no refresh
needed).
Development loop
pnpm install # registry dev deps only; dsh types resolve through a
# node_modules/@deepseek-ai -> ~/.dsh/profiles/node_modules/@deepseek-ai
# symlink (the exact package versions the GUI runs)
pnpm run typecheck # tsc --noEmit against the real dsh .d.ts artifacts
pnpm run build # tsdown: lib/index.js + lib/client.js (+ .map)
pnpm run watch # tsdown --watch; bundle rewrites trigger client HMR
Known limitations
- GitHub search caps topic results at 1000 of ~2370 repos; the status line reports the topic total and the capped list length.
- The list renders the first 300 matches at once (scroll container); the status line notes when more matches exist.
- Unauthenticated rate limits can interrupt a sync; partial results are kept and the UI explains the reset time.