Bundle
@slothtron/dsh-fff
FFF (Fast File Finder) tools for DeepSeek Harness — fffind (fuzzy path search) and ffgrep (content grep) backed by a resident @ff-labs/fff-node index that follows the current session workspace, with a runtime watch toggle on the web settings page.
- Source
- Slothtron
- stars
- 1 stars
- Updated
- Updated 15 days ago
Readme
# @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`](https://github.com/dmtrKovalenko/fff) index whose root **follows the current session workspace**.
## Install
```sh
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`:
```sh
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`/`total` signal).
- `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 the `agent/session-start` warm-up is future work.
- **Native binary platform coverage** is whatever `@ff-labs/fff-node` ships; 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).
Install
dsh plugin --profile web add github:Slothtron/dsh-fff
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install slothtron-dsh-fff from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.