dsh-fff
No description
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 17, 2026
- Updated
- Aug 18, 2026
Introduction
@slothtron/dsh-fff
FFF (Fast File Finder) tools for DeepSeek Harness. Registers fffind (fuzzy file-path search / glob) and ffgrep (content grep) as model tools, backed by a resident @ff-labs/fff-node index whose root follows the current session workspace.
Install
dsh plugin --profile <name> add ./dsh-fff
# or from git:
dsh plugin --profile <name> add github:Slothtron/dsh-fff
# restart dsh web for the bundle layer to activate
This links the bundle into the profile's node_modules, appends it to dsh.profile.bundles, and inserts the fff-tools plugin row. Remove with dsh plugin --profile <name> remove @slothtron/dsh-fff.
The bundle declares @ff-labs/fff-node as a dependency, so pnpm installs the platform native binary (@ff-labs/fff-bin-<platform>) alongside. Everything else — @deepseek-ai/schemastery for the settings schema, and the browser-half externals — resolves from the DSH installation's profiles/node_modules fallback at runtime, so the plugin ships with zero additional npm dependencies beyond the native fff SDK.
Build (browser half)
The Host half is plain ESM JavaScript (lib/*.js), no build step. The browser half (src/client/*) is bundled once with esbuild and committed as lib/client.js:
node scripts/build.mjs # esbuild is resolved from the DSH checkout ($DSH_SOURCE or ~/.dsh/source/current)
lib/client.js is a build artifact committed to git, so a git install needs no prepare script or build allowlisting.
How it works
The native fff SDK cannot load inside the harness process, so the plugin spawns a resident helper (lib/fff-server.mjs) that owns one FileFinder hot index and answers line-delimited JSON on stdio — the same spawn-a-native-binary pattern as @deepseek-ai/dsh-tool-fs-search.
Each tool call resolves the calling session's workspace from exec.agent.session.header.cwd. When it differs from the helper's current index root, the plugin asks the helper to reindex (and waits for the new scan, ~50–100 ms) before querying. This is what the MCP-injected fff-mcp could not do: its index root was fixed by the process cwd's git-root probe, so it searched the wrong tree for every session in another workspace.
Tools
| Tool | Purpose | Parameters |
|---|---|---|
fffind | Fuzzy file-path search (or glob) | query (req), pageSize, useGlob |
ffgrep | Content grep (plain/regex/fuzzy) | query (req), mode, pageSize, beforeContext, afterContext, classifyDefinitions |
Both return { base, totalMatched, totalFiles, items }; output.render presents the matches as model text.
Configuration
The fff-tools row accepts these keys (all optional):
| Key | Default | Description |
|---|---|---|
basePath | '' | Fixed index root; empty resolves from the session workspace per call |
scanTimeoutMs | 30000 | Wait budget for an index scan / reindex |
toolCallTimeoutMs | 30000 | RPC timeout per tool call |
serverPath | packaged copy | Absolute path to the helper script |
enableWatch | false | Static fallback for the background file watcher; the web settings card flips it live at runtime |
Watch toggle (runtime settings)
The bundle ships a browser half (the dsh.client entry) that registers a card
in the web Settings → Plugins tab. The card's switch flips the helper's
background file watcher without a restart: the Host half exposes the dsh-fff
settings namespace, and a change calls reconfigure, which destroys the
resident index and rebuilds it with the new watch mode on the next search.
- On — the index reflects filesystem changes (create/edit/delete) live.
- Off (default) — snapshot semantics: the index updates only when the session workspace switches (a reindex), not on in-workspace edits.
A deployment without a settings provider (or with the browser half absent)
keeps the enableWatch composition value, so the switch is additive and never
required.
Model Experience
Request context and condition
What the model sees
Two tool schemas (fffind, ffgrep) with descriptions that direct fuzzy/indexed search over the built-in ripgrep tools.
Token effect
Fixed: two tool definitions are always registered while the plugin is loaded; their descriptions are part of the assembled tool catalog.
KV Cache effect
The tool-catalog prefix is stable while the plugin is loaded; no per-request dynamic content is injected into the prompt.
UI presentation (search cards)
Both tools declare the dsh render-intent system (presentCall / presentationMeta / presentResult, per docs/cookbook/adding-a-tool.md), so a capable UI renders a search card instead of a generic text card:
fffind→card: 'search',shape: 'paths'(flat path list,truncated/totalsignal).ffgrep→card: 'search',shape: 'matches'(matches grouped by file, expandable per-file groups).
presentationMeta is a pure, byte-bounded projection (capped at 32 KB) persisted with the session log, so the card reproduces on replay without persisting the canonical value; malformed or absent metadata falls back to the generic card. The model-facing text (output.render) is unchanged.
Known Limitations and Deferred Work
- Concurrent sessions share one index root. The resident helper holds a single
FileFinder; when two sessions in different workspaces interleave calls, each call reindexes to its own workspace (correct but pays the reindex cost on each switch). A per-session cache or theagent/session-startwarm-up is future work. - Native binary platform coverage is whatever
@ff-labs/fff-nodeships; an unsupported platform surfaces a clear tool error. - Watcher off by default (
enableWatch: false). When off, the index reflects the state at last reindex, not live filesystem changes; within one workspace, files edited after indexing are picked up on the next reindex. When on, a switch in the settings card rebuilds the index live, but each watched finder adds a background thread and OS watch handle (see the refactor plan for the multi-finder pool that would make per-workspace watch cheaper).