From ff6f1d0d52e087e82b662637de139b89eda50e45 Mon Sep 17 00:00:00 2001 From: bluelovers Date: Mon, 26 Oct 2026 06:21:34 +0800 Subject: [PATCH] feat(core): add pcre2 option to grep tool ripgrep's default regex engine rejects look-around and backreferences, so patterns such as `(?("FileSystem.GrepInput")({ include: Schema.optionalKey(Schema.String), literal: Schema.optionalKey(Schema.Boolean), caseSensitive: Schema.optionalKey(Schema.Boolean), + pcre2: Schema.optionalKey(Schema.Boolean), limit: Schema.optionalKey(PositiveInt), }) {} diff --git a/packages/core/src/ripgrep.ts b/packages/core/src/ripgrep.ts index aa3b06f827dc..0cc3748d06e9 100644 --- a/packages/core/src/ripgrep.ts +++ b/packages/core/src/ripgrep.ts @@ -76,6 +76,8 @@ export interface GrepInput { readonly include?: string readonly literal?: boolean readonly caseSensitive?: boolean + /** Enable ripgrep's PCRE2 engine so patterns may use look-around and backreferences. */ + readonly pcre2?: boolean readonly limit: number readonly signal?: AbortSignal } @@ -228,6 +230,8 @@ const layer = Layer.effect( ...(input.caseSensitive === false ? ["--ignore-case"] : []), ...(input.include ? [`--glob=${input.include}`] : []), "--glob=!**/.git/**", + // PCRE2 is opt-in; it is the only engine switch exposed because it enables look-around/backreferences. + ...(input.pcre2 ? ["--pcre2"] : []), "--", input.pattern, input.file ?? ".", diff --git a/packages/core/src/tool/plugin/grep.ts b/packages/core/src/tool/plugin/grep.ts index 0062e33b8289..c447f98ab9ef 100644 --- a/packages/core/src/tool/plugin/grep.ts +++ b/packages/core/src/tool/plugin/grep.ts @@ -32,6 +32,10 @@ export const Input = Schema.Struct({ caseSensitive: FileSystem.GrepInput.fields.caseSensitive.annotate({ description: "Use case-sensitive matching (default: true).", }), + pcre2: FileSystem.GrepInput.fields.pcre2.annotate({ + description: + "Search with ripgrep's PCRE2 engine (default: false). Required when `pattern` uses look-around or backreferences.", + }), limit: FileSystem.GrepInput.fields.limit.annotate({ description: `Maximum number of matching lines to return (default: ${FileSystem.DEFAULT_SEARCH_LIMIT})`, }), @@ -76,7 +80,7 @@ export const Plugin = { name, options: { codemode: false }, description: - "Search file contents using ripgrep's regular expression syntax or literal text matching. Use it to locate specific code, symbols, or text patterns, and narrow searches with `path` or `include`. Returns matching file paths, line numbers, and line previews.", + "Search file contents using ripgrep's regular expression syntax or literal text matching. Use it to locate specific code, symbols, or text patterns, and narrow searches with `path` or `include`. Patterns with look-around or backreferences need `pcre2: true`. Returns matching file paths, line numbers, and line previews.", input: Input, output: Output, execute: (input, context) => @@ -94,6 +98,7 @@ export const Plugin = { include: input.include, literal: input.literal, caseSensitive: input.caseSensitive, + pcre2: input.pcre2, limit: input.limit, }, sessionID: context.sessionID, @@ -116,6 +121,7 @@ export const Plugin = { include: input.include, literal: input.literal, caseSensitive: input.caseSensitive, + pcre2: input.pcre2, limit: limit + 1, }) .pipe(