Neplich/dsh_plugin--packages-file-mention ↗★ 0
@neplich/dsh-file-mention
dsh Web GUI plugin: '@' file mentions in the composer — pick workspace files into the chat context
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Neplich/dsh_plugin#a03eed0fbe3f9b504c074b241b0b3d33b95ef12d&path:packages/file-mention说明文档
阅读完整 README ↗@neplich/dsh-file-mention
Web GUI plugin: typing @ in the dsh composer opens the input-trigger menu with a file group listing the session workspace's files; picking one inserts a chip-style reference into the draft. A single prompt can mention several files. On send, each mention is serialized host-side: the file's text content is inlined into the prompt as ..., so the model receives the actual content, not just a path.
Interaction details: the query filters by plain case-insensitive substring (basename prefix → basename containment → path containment, no fuzzy tier). Each menu row carries a 📄 icon, the file basename (with a (dir) suffix when the page collides), and its directory in the description column. The composer's reference chip shows 📄 plus the basename, tail-elided against a canvas-measured width so the marker and the name's leading characters always survive the chip's fixed-width cell (its CSS would otherwise cut a random middle slice). After one per-session listing fetch, every keystroke filters the settled snapshot locally (the ui-skill cache pattern), so refinement never waits on the network; trees bigger than maxListed fall back to per-keystroke server-side ranking.
The plugin runs against a stock deepseek-harness — menu behavior it does not own keeps the pipeline's design: the menu keeps its design width, a no-match query closes the menu, and @ directly after a CJK character does not open the trigger (type a space first, e.g. 请读取 @文件).
How it works
- Host half (
src/index.ts): registers two same-origin routes on the Web GUI server (ctx.webServer):GET /file-mention/search?session=&q=— walks the session's cwd (bounded, ignore-listed, single-flight cached per cwd) and answers either the listing (empty query: capped atmaxListed, with acompleteflag the client turns into local filtering) or the ranked top matches (non-empty query: the client fallback for huge trees).GET /file-mention/read?session=&path=— answers one file's text, confined to the session cwd (lexical + realpath escape checks), size-bounded, binary-refused.
- Browser half (
src/client/index.ts): registers the source on the input-trigger pipeline (). The first interaction (or the scope-birth ) fetches the listing once per session; candidates then filter the settled snapshot locally and only fall back to the server when the listing was cut. A pick inserts a occurrence (the full path rides a candidate-keyed side table because the menu keys rows by display name); the source codec's inlines the file content at submit, and a failed read blocks the send (never a silent downgrade to the bare ).