dsh-file-mention

繁體中文版:README.zh.md

@ file mentions + drag-and-drop file → path text for the
DeepSeek Harness (DSH) Web composer.

A DSH Web plugin that turns files in your workspace into lightweight, agent-readable
references. Pick a file from an @ menu — or drop one onto the page — and the composer
inserts an @-prefixed path as plain text; the agent then reads that path with its
own tools (read, etc.). No provider attachment mechanism is involved.

Features

  • @ file mentions — type @ and a files group appears, scanned from the current
    session's workspace directory.
  • Workspace-relative by default — files inside the workspace present and insert their
    relative path (concise); files reached through an explicit outside path (/…, ~/…,
    ../…) present and insert their absolute path (more information).
  • Bare @ opens the menu — an empty query still lists a default set of workspace files.
  • Tab to accept — highlight a candidate with the arrow keys and press Tab to
    insert it, no mouse needed.
  • Drag-and-drop files — drop any non-image file onto the page; it is uploaded to the
    host and its path is inserted as @-prefixed text. Pure-image drags pass through to the
    built-in image rail untouched.
  • All references carry @ — inserted paths are always prefixed with @ to mark them as
    file references (@README.md, @/Users/you/project/README.md).

Installation

Requires a running DeepSeek Harness
Web profile with the @ input-trigger service (ctx.inputTriggers; the plugin
peer-depends on dsh-client-ui-input-trigger / dsh-client-runtime / dsh-client-locale
^0.1.0-rc.7). On an older host the @/files menu never appears.

Install straight from GitHub — no npm publish required (the plugin is build-less, so a
git install needs no extra allowBuilds step):

dsh plugin --profile web add github:tipoLi5890/dsh-file-mention

While developing, link a local clone instead (edits take effect on restart):

dsh plugin --profile web add link:/absolute/path/to/dsh-file-mention

Verify the entry joined the profile, then restart the web server for the new Loader entry
and its bundle to take effect:

dsh --profile web --dump-config   # you should see a row with id: file-mention

Usage

In the composer:

Action Result
Type @ Menu opens with a default workspace file list
Type @readme Matches README.md; picking inserts @README.md
Type @lib/cl Scoped to lib/; description shows lib, picking inserts @lib/client.js
Type @/etc/ho Outside path; description shows /etc, picking inserts @/etc/hosts
Type @~/Downloads/rep or @../sib/f Outside path; inserts the absolute path
Arrow keys + Tab Accept the highlighted candidate without clicking
Drop a non-image file Uploads and inserts @<absolute upload path>

The path is plain text. On send, the agent resolves a relative path against its session
cwd (the workspace) and reads absolute paths directly.

How it works

The package declares a dsh.bundle.patch (a self-inserting Loader entry) and a dsh.client
browser half served into the /plugins boot graph.

Host half — lib/index.js

Hand-written ESM, no build step. Registers a /api/file-mention route prefix on the
profile's web server (ctx.webServer):

  • GET /api/file-mention/search?base=<abs workspace dir>&q=<query> — a bounded directory
    scan (depth ≤ 8, ≤ 2000 entries, ≤ 50 results, ~600 ms budget; skips .git/node_modules,
    never follows symlinks; hidden files are included only when the query starts with . or
    matches). base is the session workspace cwd (falls back to the host home). Name-only
    queries scan the whole workspace; path-like queries (/…, ~/…, ./…, ../…) resolve
    the directory part and scan only that directory. Returns
    { files: [{ name, dir, path, rel }] }path is always absolute, rel is the
    workspace-relative path (or null when outside), and dir is the display directory
    (relative inside the workspace, absolute outside).
  • POST /api/file-mention/upload?sessionId=&name= (raw bytes body, 50 MB cap) — sanitizes
    the name/session id, writes collision-safely under <DSH_HOME>/uploads/<sessionId>/,
    returns { path }.

Browser half — lib/client.js

Loaded in the lazy-CJS factory form the client module loader expects (no build step, no
React). Registers an @/files InputTriggerSource via ctx.inputTriggers; onPick
returns { text: "@" + path + " " } (the pipeline replaces the @query token span with
plain text). Capture-phase dragenter/dragover/drop listeners claim non-image drags,
upload each file, and insert the returned paths at the caret.

API

Endpoint Method Purpose
/api/file-mention/search GET Bounded file search for the @ menu
/api/file-mention/upload POST Upload a dropped file and return its path

Limitations

  • Browsers do not expose a dragged file's native path, so dropped files are copied into
    <DSH_HOME>/uploads/<sessionId>/ and the copy's path is inserted (the agent can read it;
    your workspace is untouched).
  • The @ menu group title shows the source's raw name files (the
    inputTriggers.menu locale namespace is owned by dsh-client-ui-input-trigger).
  • Mixed drags (images + files) upload images as files too (inserting path text rather than
    going to the image rail).
  • When the composer is busy/locked, the machine may reject the insertion; the plugin falls
    back to copying the paths to the clipboard.
  • Single-file upload cap is 50 MB (host constant).
  • The web server remains loopback-only and unauthenticated (same posture as the app).

License

MIT © 2026 dsh-file-mention contributors