feat(fs-search): spawn the packaged ripgrep binary through the subprocess seam

glob/grep now run the @vscode/ripgrep binary via ctx.subprocess with a plain
argv vector: no system rg install, no shell layer, unconditional registration.
The load-time command -v rg probe and the bash-seam coupling are removed;
timeouts ride the cooperative exec.signal plus the seam's terminate
escalation. The fs-glob-sampling ACP snapshot executes the real packaged
binary against an mtime-pinned fixture. Adds the packaged-ripgrep-search
Agent Note, updates the roster-note facts and both shipped-composition e2es,
and regenerates the doc catalogs and third-party notices (surfacing
pre-existing manifest drift plus the new @vscode/ripgrep row; the notices
generator also learns pnpm 11's truncated virtual-store names).
This commit is contained in:
Huanqi Cao
2026-08-01 18:04:38 +08:00
parent de520bee24
commit 18700f428d
32 changed files with 923 additions and 699 deletions

View File

@@ -1,11 +1,12 @@
/**
* The model-facing `grep` tool: search file contents with a ripgrep regular
* expression. Execution goes through the bash seam (`ctx.bash`) with a fixed
* line-oriented `rg --json` command so file path, line number, and line text
* parse without colon-splitting ambiguity — this module owns the model-facing
* schema, argument validation, shell-safe command construction, `--json`
* record parsing, per-line preview retention, match retention, grouping, and
* formatting; process concerns stay behind `ctx.bash`.
* expression. Execution spawns the packaged ripgrep binary
* (`@vscode/ripgrep`) directly through the subprocess seam with a plain argv
* vector using a fixed line-oriented `rg --json` command so file path, line
* number, and line text parse without colon-splitting ambiguity — this module
* owns the model-facing schema, argument validation, argv construction,
* `--json` record parsing, per-line preview retention, match retention,
* grouping, and formatting; process concerns stay behind `ctx.subprocess`.
*
* @module @deepseek-ai/dsh-tool-fs-search/grep
*/
@@ -15,12 +16,10 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type { RetainedItems } from '@deepseek-ai/dsh-retention'
import type { SpillRef } from '@deepseek-ai/dsh-spill'
import type {} from '@deepseek-ai/dsh-bash'
import type {} from '@deepseek-ai/dsh-system-prompt'
import type { GrepMatch } from './search-core.ts'
import { SearchError, previewLine, retainGrepMatches, runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts'
import { grepSearchMeta, searchViewFromMeta } from './presentation.ts'
import { singleQuote } from './shell-quote.ts'
import { acceptedSurfaceValue } from './surface.ts'
/**
@@ -96,20 +95,21 @@ export function parseGrepArgs(args: { pattern: string; path?: string; include?:
}
/**
* Build the fixed line-oriented `rg --json` command for one `grep` call. Every
* Build the fixed line-oriented `rg --json` argv for one `grep` call. Every
* model-controlled value ({@link GrepInput.pattern}, {@link GrepInput.path},
* {@link GrepInput.include}) passes through {@link singleQuote}; the pattern
* and include ride in `--flag=value` form and the target behind `--`, so a
* leading-dash value can never be parsed as a flag.
* {@link GrepInput.include}) is a plain argv element — no shell layer exists,
* so no quoting applies; the pattern and include ride in `--flag=value` form
* and the target behind `--`, so a leading-dash value can never be parsed as
* a flag.
*
* @param input - the validated arguments.
* @returns the complete, shell-safe command string.
* @returns the complete ripgrep argument vector (excluding the binary itself).
*/
export function buildGrepCommand(input: GrepInput): string {
const parts = ['rg --json', `--regexp=${singleQuote(input.pattern)}`]
if (input.include !== undefined) parts.push(`--glob=${singleQuote(input.include)}`)
if (input.path !== undefined) parts.push('--', singleQuote(input.path))
return parts.join(' ')
export function buildGrepCommand(input: GrepInput): string[] {
const parts = ['--json', `--regexp=${input.pattern}`]
if (input.include !== undefined) parts.push(`--glob=${input.include}`)
if (input.path !== undefined) parts.push('--', input.path)
return parts
}
/**