Back to home@Slothtron

dsh-fff

No description

Stars
0
Language
JavaScript
Created
Aug 17, 2026
Updated
Aug 18, 2026
GitHub repo

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

ToolPurposeParameters
fffindFuzzy file-path search (or glob)query (req), pageSize, useGlob
ffgrepContent 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):

KeyDefaultDescription
basePath''Fixed index root; empty resolves from the session workspace per call
scanTimeoutMs30000Wait budget for an index scan / reindex
toolCallTimeoutMs30000RPC timeout per tool call
serverPathpackaged copyAbsolute path to the helper script
enableWatchfalseStatic 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:

  • fffindcard: 'search', shape: 'paths' (flat path list, truncated/total signal).
  • ffgrepcard: '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).