# dsh-at-file

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

## Metadata

- Author: [@omdsh-dev](https://github.com/omdsh-dev)
- Repo: <https://github.com/omdsh-dev/dsh-at-file.git>
- GitHub: [omdsh-dev/dsh-at-file](https://github.com/omdsh-dev/dsh-at-file)
- Stars: 438
- Language: JavaScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `dsh`, `dsh-plugin`
- Forks: 17
- Open Issues: 0
- Last push: 2026-08-20T15:35:45.000Z
- Added: 2026-08-13T00:00:00.000Z

## Install

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

## Wiki

## 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
| Dependency | Minimum Version | Notes |
|---|---|---|
| DeepSeek Harness (DSH) | Not declared | All `@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.js | Not declared | Repository has no `engines` field, `@types/node` locked at `^24.0.0` (`package.json:131`), please choose according to DSH requirements |
| Platform | macOS / Windows / Linux | Only uses `node:fs` / `node:fs/promises` / `node:path`, no native modules |
| Native Modules | None | Runtime only depends on `zod` per `package.json:115-117`, no native binding |

## Installation
```bash
dsh plugin --profile web add github:omdsh-dev/dsh-at-file
```

## Configuration Options
### Host-side Configuration (written in `~/.dsh/profiles/web/cordis.patch.yml`)
| Config | Type | Description | Default |
|---|---|---|---|
| `maxIndexedFiles` | number | Maximum number of file/directory entries indexed per workspace; exceeding this immediately stops and returns `truncated=true` | `5000` |
| `ignoreDirs` | string[] | Directories to skip during indexing by basename; set to `[]` to index all directories | Built-in 60+ `.git` / `node_modules` / `build` etc. |

### User Preferences (changed in DSH Settings → File mentions panel, persisted via `atFile/updateSettings`)
| Preference | Type | Description | Default |
|---|---|---|---|
| Enable at-file | boolean | When disabled, `@` selector, reference bar, and pre-step injection are all stopped | `true` |
| Ignore @ in pasted text | boolean | When disabled, `@path` pasted from external sources will be recognized like manual input | `true` |
| Global file filter | Exact / Regex rule list | Basename filter shared across all workspaces; legacy string rules are treated as case-insensitive Exact rules | Built-in `desktop.ini` / `Thumbs.db` / `.DS_Store` |
| Workspace file filter | Exact / Regex rule list | Additional rules that only apply to the current workspace, saved independently per workspace | Empty |

## 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)

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-at-file](https://deepseek-plugin.org/plugins/omdsh-dev/dsh-at-file)
Wiki generated by AI (model: `MiniMax-M2.5`)
