dsh-at-file

438Stars17Forks0Issues0Watchers

Adds a Codex-style `@` file selector to DSH Web: after selecting a path, only injects an existence reference to the model without reading file

Language
JavaScript
License
MIT
Branch
main
dshdsh-plugin

Install

$ dsh plugin --profile web add github:omdsh-dev/dsh-at-file

Run the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial

One-Line Description

Adds a Codex-style @ file picker to the DeepSeek Harness web input box: after selecting or manually entering a path, the plugin only appends a single line of "path + type" reference message to the model input. File content is always read by the agent using its own tools, and never leaked to the model through this plugin.

Core Features

  • Type @ in the web input box to pop up a workspace file/directory picker, supporting plain text keyword matching, /-segment prefix matching, and compact sorting
  • Validate the existence of selected or manually entered @path before each agent step; after validation, inject <workspace-reference path="..." kind="file|directory" /> — a reference message containing only path and type
  • Settings → File mentions provides an enable switch, Global / Workspace two-layer filename filtering rules (Exact and Regex, with case-sensitivity support), and paste text strategy, all persisted through the plugin's own atFile/updateSettings endpoint
  • Selector supports ArrowRight to enter directories, cross-pane folder browsing; clicking a path in the reference bar opens it via Harness's host.openPath endpoint using the system default application
  • Default indexing automatically skips .git, node_modules, build, dist, __pycache__, Xcode/Unity/Unreal and 60+ other common build artifact directories, plus desktop.ini, Thumbs.db, .DS_Store and other system metadata files

Technical Implementation

  • Language: TypeScript (ESM); both host half and client half are bundled within the same package, client half is delivered as a single file /plugins/dsh-at-file/client.js by DSH web server
  • Key Dependencies: zod (runtime-only dependency, used for wire codec validation); @deepseek-ai/cordis (plugin container), @deepseek-ai/dsh-typert-protocol + @deepseek-ai/dsh-typert-registry (strongly-typed endpoint registration), @deepseek-ai/dsh-agent + @deepseek-ai/dsh-llm (pre-step hooks and UserMessage construction)
  • Architecture Pattern: Dual half-host plugin. Host half loads AtFileRuntime with Cordis (@Remote decorated) + ctx.typert.register(TYPERT_MANIFEST) via strict registry to declare wire endpoints, and attaches mentionPreStep hook to each agent scope via agent/pre-step event; client half uses ctx.remote.$mount to mount the same Remote, inputTriggers.registerSource to register @ trigger, ctx.slots.register to register dock + folder navigation + settings panel section
  • Entry Files: Host src/index.ts (exports apply / Config), Client src/client/index.ts (exports apply / inject), mount declarations in cordis.patch.yml + package.json#dsh.bundle.patch + package.json#dsh.client.inject

Use Cases

When you want the DSH agent to operate on specific files in the current workspace but don't want to stuff the entire file content into the prompt beforehand — for example, "rewrite around line 30 of src/runtime.ts" or "check if docs/spec.pdf covers exception flows" — just write a piece of text with @path in the input box, and the agent will see an existence reference line, then read as needed. Another typical scenario is using the plugin's settings page to uniformly manage "which filenames shouldn't appear in the @ menu", blocking interference items like *.lock / *.min.js once in Global.

Prerequisites & Compatibility

DependencyMinimum VersionNotes
DeepSeek Harness (DSH)Not declaredAll @deepseek-ai/dsh-* and @deepseek-ai/cordis in package.json:51-66 use peerDependencies: * / ^4.0.1-rc.1, no minimum DSH version given; recommend using current DSH mainline
Node.jsNot declaredRepository has no engines field, @types/node locked at ^24.0.0 (package.json:131), please choose according to DSH requirements
PlatformmacOS / Windows / LinuxOnly uses node:fs / node:fs/promises / node:path, no native modules
Native ModulesNoneRuntime only depends on zod per package.json:115-117, no native binding

Installation

dsh plugin --profile web add github:omdsh-dev/dsh-at-file

Configuration Options

Host-side Configuration (written in ~/.dsh/profiles/web/cordis.patch.yml)

ConfigTypeDescriptionDefault
maxIndexedFilesnumberMaximum number of file/directory entries indexed per workspace; exceeding this immediately stops and returns truncated=true5000
ignoreDirsstring[]Directories to skip during indexing by basename; set to [] to index all directoriesBuilt-in 60+ .git / node_modules / build etc.

User Preferences (changed in DSH Settings → File mentions panel, persisted via atFile/updateSettings)

PreferenceTypeDescriptionDefault
Enable at-filebooleanWhen disabled, @ selector, reference bar, and pre-step injection are all stoppedtrue
Ignore @ in pasted textbooleanWhen disabled, @path pasted from external sources will be recognized like manual inputtrue
Global file filterExact / Regex rule listBasename filter shared across all workspaces; legacy string rules are treated as case-insensitive Exact rulesBuilt-in desktop.ini / Thumbs.db / .DS_Store
Workspace file filterExact / Regex rule listAdditional rules that only apply to the current workspace, saved independently per workspaceEmpty

FAQ

Q: Will pasted @path be recognized?

A: Not by default. The client marks @ with an invisible U+2060 character during paste, and the Host in scanMentions skips tokens with this mark; meanwhile, ignorePastedMentions is enabled by default in Settings, providing double protection. To have pasted @path from external sources go through the selector flow, just turn off "Ignore @ in pasted text" (src/paste.ts:7 / src/mention.ts:42-55).

Q: Will the plugin send file content to the model?

A: No. mentionPreStep does only two things: scan for @path in the user message, run stat on each token to confirm existence and determine file or directory, then append <workspace-reference path="..." kind="..." /> to the prompt. File bytes never enter the wire, never leave the Host; if the model needs to read files, it uses read / read_image and other tools attached to the current agent session (src/mention.ts:1-8).

Q: Can I reference files outside the workspace?

A: No. resolveMention uses path.relative(cwd, absolute) to detect out-of-bounds: results starting with .. or containing .. are discarded; isAbsolute(token) also directly rejects. Manually typing an absolute path like /etc/passwd results in seeing ordinary @/etc/passwd text in the prompt, not converted to a reference (src/mention.ts:64-81).

Q: How do I filter files in the selector?

A: Open Settings → File mentions: Global list is the shared base for all workspaces, Workspace list is additional rules for the current workspace; each rule independently selects Exact / Regex and whether to be case-sensitive, malformed Regex is rejected by the frontend before saving, and the Host's schema rejects again (src/contract.ts:66-79 / README.md:60-66).

Q: Will indexing large workspaces hang?

A: indexWorkspace uses opendir streaming reads (not一次性 readdir), doesn't follow symlinks, stops immediately when hitting maxIndexedFiles and sets the truncated flag. Default 5000 limit + 30-second session cache is enough for most projects; for超大 repositories, raise the limit in cordis.patch.yml to 10000+ (src/files.ts:99-158).

Q: What about PDFs?

A: The selector treats PDFs as regular path entries; whether the model can read PDFs depends on the agent tools in the current session — DSH provides read for UTF-8 text, read_image for supported images, PDF/Word etc. need corresponding tools attached to the session, the plugin itself doesn't read file content (README.md:98).

Q: How to upgrade?

A: Re-run the same dsh plugin --profile web add ... command, then restart dsh web; lib/ is committed to the repo, profile installation doesn't trigger build scripts (README.md:46-49).

Q: When is cache cleared?

A: Client caches for 30 seconds per session (INDEX_TTL_MS), Host caches by cwd; cache is immediately cleared on filter rule changes or connection reset (src/client/source.ts:34 / src/client/index.ts:157-161).

Learning Curve

Beginner — single dsh plugin add + hard refresh browser to use; advanced usage involves maintaining Global / Workspace filter rules in Settings per team habits, and adjusting maxIndexedFiles limit in cordis.patch.yml.

Known Issues & Limitations

  • Hard limit on indexed entries: maxIndexedFiles defaults to 5000, exceeding immediately stops and returns truncated=true; valid paths beyond the limit need manual input to be referenced (src/files.ts:124-128 / src/index.ts:52)
  • Symlinks skipped entirely: walker doesn't follow symlinked directories to avoid link loops; symlinked files themselves are also not indexed (src/files.ts:130-133)
  • Default exclusion directories are extensive: 60+ built-in basenames include mainstream IDE, build tool, dependency cache output directories; to index node_modules etc., must explicitly set ignoreDirs to [] (src/defaults.ts:4-65)
  • @path token cannot contain whitespace or another @: /@[^\s@]+/g determines boundaries, overly long paths or Windows short names with spaces won't be automatically recognized (src/mention.ts:34)
  • Pasted text not recognized by default: @path copied from other apps won't show in selector, need to turn off "Ignore @ in pasted text" in settings (src/paste.ts:7 / README.md:25)
  • File reading entirely depends on agent tools: plugin doesn't read files, doesn't guarantee PDF/Office formats have processing capability, session needs to bring its own corresponding tools (README.md:98)
  • Node / DSH versions not declared in package.json: missing engines field, all DSH @deepseek-ai/* peerDependencies use *, need to ensure DSH is compatible yourself (package.json:51-66)