|
|
3 months ago | |
|---|---|---|
| .. | ||
| src | 3 months ago | |
| tests | 3 months ago | |
| README.md | 3 months ago | |
| package.json | 3 months ago | |
| tsconfig.json | 3 months ago | |
The model-facing filesystem discovery tools — glob, grep — backed by the bash executor seam, not by ctx.fs provider methods. Each call assembles a fixed ripgrep command (every model-controlled value through one package-private shell-quoting helper), runs it via ctx.bash.resolve(request) → ctx.bash.run(spec) as an ordinary foreground tool call, parses the raw rg output, and returns a bounded, workdir-relative result. The package injects tools, systemPrompt, and bash — deliberately not fs; ctx.spillStore is read opportunistically with ctx.get() because formatted-result spill is optional.
// Default deployment: a bash executor, then the discovery tools.
await ctx.plugin(LocalBashExecutor, { cwd: process.cwd() }) // @deepseek-ai/dsh-bash-local
await ctx.plugin(ToolFsSearch) // this package — registers glob/grep
// Optional: a spill backend makes capped results fully recoverable.
await ctx.plugin(LocalSpillStore) // @deepseek-ai/dsh-spill-local
Why bash-backed: local workspace discovery is naturally a process-backed rg workflow, and putting search on ctx.fs would force every filesystem backend to grow a search API. The bash executor owns request defaulting/capping, subprocess execution, process-group termination, environment scrubbing, raw output capture, and backend substitution (local, sandboxed, remote); this package owns schemas, argument validation, shell quoting, parsing, retention, formatted-result spill, and timeout declaration. The tools never call ctx.bash.start() and never expose a bash task id — the call returns only after rg exits, times out, is aborted, or fails.
Returned paths are displayed relative to the resolved bash workdir (the calling agent's session cwd when present, else the executor's configured default) and are follow-up-readable with read only when the bash workdir and the filesystem root are the same workspace. v1 documents that requirement and performs no runtime cross-service validation; remote or virtual filesystem search waits for a shared workspace contract or a provider-specific search backend.
All keys are optional; the defaults are the shipped search caps.
| Key | Default | Meaning |
|---|---|---|
globMaxResults |
100 |
Max paths one glob call retains inline (matches Claude Code's GlobTool limit); later paths go to the formatted spill artifact. |
grepMaxMatches |
250 |
Max flat matches one grep call retains inline (matches Claude Code's GrepTool head_limit); later matches go to the formatted spill artifact. |
grepMaxLineBytes |
2000 |
Byte cap per matched-line preview; the cut preserves UTF-8 boundaries and is marked (line truncated). |
rawOutputMaxBytes |
20000000 |
Max complete raw rg stdout a search will parse (matches Claude Code's ripgrep raw buffer); larger raw output fails with SEARCH_RAW_OUTPUT_OVERFLOW. |
timeoutMs |
30000 |
Cooperative tool-call budget attached to both tool definitions, enforced by @deepseek-ai/dsh-timeout-policy through exec.signal; the bash backend's own timeout stays a second safety cap. |
| Tool | Arguments | Behavior |
|---|---|---|
glob |
pattern, path? |
rg --files --glob <pattern> --sort=modified --no-ignore --hidden plus VCS metadata excludes (.git, .svn, .hg, .bzr, .jj, .sl). path is an optional directory search root; omitted means the resolved bash workdir. Returns one path per line, modification-time ordered. |
grep |
pattern, path?, include? |
Line-oriented rg --json parse (no colon-splitting ambiguity). pattern is a ripgrep regex; path is an optional file or directory target; include is ONE positive glob filter — a comma-separated list or a negated (!…) value is rejected up front (brace alternation like *.{ts,tsx} is fine). Returns matches grouped by file as Line N: <preview>. |
Routine budgets stay out of the model-facing schema (no head_limit/offset/case_insensitive/output modes): a model that needs surrounding context reads the matched file with read; one that needs later results follows the returned spill locator's retrieval hint.
Raw rg stdout is an internal transport detail. Each search requests stdoutMaxBytes: rawOutputMaxBytes from the bash seam and parses only complete retained stdout; if the executor still returns stdout.truncated, the search fails with SEARCH_RAW_OUTPUT_OVERFLOW and tells the model to narrow the query. The model-facing recovery artifact is different: when a search yields more logical results than the inline cap, the tool saves the COMPLETE formatted result through ctx.spillStore.saveText() (suggested names glob-results.txt / grep-results.txt, owner = the calling session, source = the tool execution identity) and appends a footer naming the returned locator and retrieval hint. This is the first tool-owned spill call in the codebase — deliberate, because retention here is item-level: the generic @deepseek-ai/dsh-spill-policy only sees the final text on tools/post-execute, by which point a capped search has already omitted later paths/matches. A missing spill backend, a call with no session owner, or a saveText() failure keeps the inline page and reports that the complete result could not be saved — never an isError.
Search failures carry the package-owned SearchError (a HarnessError subclass), surfaced as { name, code } on isError results: SEARCH_INVALID_PATTERN (ripgrep rejected the regex/glob), SEARCH_FAILED (missing rg, inaccessible target, signal kill, malformed --json output), SEARCH_RAW_OUTPUT_OVERFLOW (raw output over rawOutputMaxBytes, or still truncated after the requested stdout capture budget), and SEARCH_ABORTED (tool timeout, caller cancellation, or the bash executor's own timeout). ripgrep exit semantics are tool-owned: exit 0 is success with results, exit 1 is a successful empty search (No files found / No matches found), and only other exits are failures. Model argument mistakes (blank pattern, a list-valued include) stay ordinary tool argument errors.
What the model sees: Every request in this plugin's registration scope contains the independently registered glob and grep guidance below. Agent-scoped tool restrictions can hide either schema without removing its prompt section.
Token effect: Fixed guidance cost per request while the plugin is active.
Use the glob tool — not shell find or ls — to discover files by path pattern. Results are sorted by modification time and include hidden and ignored files.
Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context.
What the model sees: The generated glob and grep schemas while this surface is visible.
Token effect: Fixed schema cost on every request where the tools are visible.
What the model sees: glob returns one path per line; grep groups Line <line>: <preview> matches beneath each path. Empty searches return No files found or No matches found. A capped result ends with its omission count plus the spill locator and backend retrieval hint, or says the complete result could not be saved.
Token effect: Inline paths and matches are bounded by globMaxResults, grepMaxMatches, and grepMaxLineBytes; the call and retained result remain in history until compaction.
What the model sees: Failures are normalized as Error: <message> with structured SEARCH_INVALID_PATTERN, SEARCH_FAILED, SEARCH_RAW_OUTPUT_OVERFLOW, or SEARCH_ABORTED metadata for callers.
Token effect: Only a failing call adds these retained tokens.
rg executable fails calls with SEARCH_FAILED; remote or virtual filesystems need a co-located executor or another search consumer.