Skip to content
dsh.fish
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

  • 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.
Source