English · 中文
A dsh plugin that adds @ project file/directory suggestions to the chat composer: type @ to browse the current project (the session's working directory), pick a file or directory, and the draft gains a @path reference that ships to the model verbatim.
Built as a fully out-of-tree plugin — the deepseek-harness source stays untouched. It rides the same input-trigger pipeline as ui-skill's / (so it coexists with ui-subagent's @ mentions as a second menu group), and it serves its project tree over its own HTTP route (ctx.webServer).
-
Install the plugin from npm (published as
@dsh-mixxed/dsh-client-ui-filesystem):dsh plugin --profile web add @dsh-mixxed/dsh-client-ui-filesystem
The package declares
dsh.bundle(its bundledcordis.patch.yml), sodsh plugin addautomatically appends it to the profile'sdsh.profile.bundleslayer stack and the plugin mounts on the next boot — no manualcordis.patch.ymlediting.Upgrading an install that predates the bundle declaration: remove the legacy
ui-filesystemrow from$DSH_HOME/profiles/<name>/cordis.patch.yml— the bundle layer now supplies it, and leaving both would mount the id twice. -
Restart the profile (new plugins are discovered at boot), open any session, and type
@.
pnpm install
pnpm run typecheck
pnpm test
pnpm run build
npm pack # produces dsh-mixxed-dsh-client-ui-filesystem-<version>.tgz
dsh plugin --profile web add ./dsh-mixxed-dsh-client-ui-filesystem-<version>.tgz@trigger: same pipeline as ui-skill's/(ui-input-trigger), shown alongside ui-subagent's@mentions as its own menu group- Basename-prefix matching: the query matches only the file/directory name prefix, never the path prefix —
@indexhitssrc/index.tsandtest/index.ts; path-shaped queries like@src/return nothing (by design) - Filename-first rows: every candidate shows the file/directory name first and the relative path after (e.g.
index.ts src/index.ts) - Plain-text references: picking replaces the
@token with the literal@relative/path— the prompt ships the same literal and the model resolves the file with its own fs tools - Per-session caching: each session fetches the bounded project tree once; keystroke filtering happens locally, and
connection/resetrebuilds the cache - Bounded walk: skips
node_modules/.git/ hidden entries (configurable), caps depth at 6 and entries at 2000, and never lists symlinks - Retry on failure: a failed tree request (e.g. right after the host restarts, before the session is attached) retries once, keeping the menu in its loading state instead of vanishing
- Where
@triggers: the trigger char must sit at the start of the draft, after whitespace, or after punctuation. A Chinese character directly before@does not trigger (请查看@indexis a miss;请查看,@indexworks) — the trigger detector treats CJK letters as word characters. That rule lives in the harness'sui-input-trigger; this plugin does not modify the harness, so inline references work best with a space or punctuation before@. - Loading state: while the project tree loads, the menu opens immediately with the group's existing text-only loading row (
正在加载…); an animated spinner would require modifying the harness menu component, which this plugin does not do. - No chip decoration:
@pathstays plain text in the draft — the decoration scanner only matches word-ish names, so paths never render as chips (DESIGN.md §7.1). - No menu group title: the harness menu renders a raw per-source title row (the
slash.menudictionary is exclusively owned by the harness); since@shows only this plugin's group, the title row is hidden with one injected CSS rule. - Full boundary list:
DESIGN.md§7.
dsh --profile <name> --dump-config | Select-String ui-filesystemThe composed config shows the ui-filesystem row, and $DSH_HOME/profiles/<name>/package.json lists @dsh-mixxed/dsh-client-ui-filesystem under dsh.profile.bundles (auto-appended by dsh plugin add).
After the restart: @ opens the filesystem group (name first, path after) → basename-prefix filtering → picking inserts @path → the model can read the file from the prompt literal.
Tune the walk bounds from your profile's own cordis.patch.yml — the user layer is applied after the bundle layer, so an id-targeted patch overrides the bundled mount row:
- id: ui-filesystem
name: "@dsh-mixxed/dsh-client-ui-filesystem"
config:
maxDepth: 6 # max path-segment depth (default 6)
maxEntries: 2000 # total entry cap (default 2000)
skipHidden: true # skip dot-prefixed entries (default true)
skipPatterns: # basename exact-match skip patterns (default node_modules, .git)
- node_modules
- .git