Skip to content

Commit 78b0896

Browse files
author
SqlRush
committed
Add Grep no-ignore-dot support
1 parent 3c94778 commit 78b0896

4 files changed

Lines changed: 54 additions & 5 deletions

File tree

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ test/parity/ # golden tests against TS/official behavior
231231
- 本轮补充:Grep 常用布尔参数继续补齐 ripgrep 长参数 aliases,覆盖 `--line-number``--ignore-case``--fixed-strings``--word-regexp``--invert-match``--only-matching`,并兼容 quoted semantic boolean。
232232
- 本轮补充:Grep multiline 搜索现在支持 ripgrep 风格 `-U``--multiline``multiline-dotall``--multiline-dotall` aliases,统一映射到既有跨行 dotall 匹配逻辑并兼容 quoted semantic boolean。
233233
- 本轮补充:Grep 搜索现在支持 `no_ignore`/`noIgnore`/`no-ignore`/`--no-ignore`,可跳过 `.gitignore`/`.ignore`/`.rgignore` 规则,同时继续排除 VCS metadata 目录并保留 Read deny 额外 ignore 保护;`--no-ignore` 兼容 quoted boolean。
234-
- 本轮补充:Grep 搜索现在支持 ripgrep 风格 `ignore_files`/`--ignore-files``no_ignore_files`/`--no-ignore-files` aliases;`--no-ignore-files` 只跳过 `.ignore`/`.rgignore`,保留 `.gitignore` 规则继续生效,并兼容 quoted boolean。
234+
- 本轮补充:Grep 搜索现在支持 ripgrep 风格 `ignore_dot`/`--ignore-dot``no_ignore_dot`/`--no-ignore-dot` aliases;`--no-ignore-dot` 只跳过 `.ignore`/`.rgignore`,保留 `.gitignore` 规则继续生效,并兼容 quoted boolean;既有 `no_ignore_files`/`--no-ignore-files` 保留为同路径兼容输入
235235
- 本轮补充:Grep 搜索现在支持 ripgrep 风格 `ignore_vcs`/`--ignore-vcs``no_ignore_vcs`/`--no-ignore-vcs` aliases;`--no-ignore-vcs` 只跳过 `.gitignore`,保留 `.ignore`/`.rgignore` 规则继续生效,并兼容 quoted boolean。
236236
- 本轮补充:Glob/Grep 层级 ignore 文件现在支持 ripgrep 专用 `.rgignore`,并和 `.gitignore`/`.ignore` 共享解析、否定规则和目录规则语义。
237237
- 本轮补充:Grep 的 `files_with_matches` 输出现在按官方行为使用文件修改时间倒序排序,mtime 相同再按路径排序;分页和 `head_limit` 会在排序后应用。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ Covered behavior:
158158
- `Grep` long-line output now accepts `max_columns_preview`/`--max-columns-preview` plus `no_max_columns_preview`/`--no-max-columns-preview`, showing a ripgrep-style truncated preview when `max_columns` is exceeded.
159159
- `Glob`/`Grep` traversal now applies `Read(...)` deny rules from the permission context as extra search ignore rules, hiding denied basename, path, and directory patterns from search results.
160160
- `Glob`/`Grep` hierarchical ignore loading now includes ripgrep-specific `.rgignore` files in addition to `.gitignore` and `.ignore`.
161-
- `Grep` now accepts ripgrep-style `ignore_files`/`--ignore-files` and `no_ignore_files`/`--no-ignore-files`, letting `.ignore`/`.rgignore` rules be disabled while `.gitignore` remains active.
161+
- `Grep` now accepts ripgrep-style `ignore_dot`/`--ignore-dot` and `no_ignore_dot`/`--no-ignore-dot`, letting `.ignore`/`.rgignore` rules be disabled while `.gitignore` remains active; `no_ignore_files`/`--no-ignore-files` remains accepted as a compatibility alias for the same current path.
162162
- `Grep` now accepts ripgrep-style `ignore_vcs`/`--ignore-vcs` and `no_ignore_vcs`/`--no-ignore-vcs`, letting `.gitignore` rules be disabled while `.ignore`/`.rgignore` remain active.
163163
- `Read`/`Edit` now coerce quoted semantic strings for `offset`/`limit` and `replace_all`, including whole-decimal numeric strings such as `"2.0"` for integer fields while keeping fractional values rejected.
164164
- `Bash`/`BashOutput` now coerce quoted semantic strings for `timeout`, `run_in_background`/`runInBackground`, and `tail_lines`/`tailLines`, matching official SDK-style number/boolean inputs without relaxing unknown-field validation.

internal/tools/file/search_tools.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ var allowedGrepInputKeys = map[string]struct{}{
6969
"include_zero": {}, "includeZero": {}, "include-zero": {}, "--include-zero": {},
7070
"no_ignore": {}, "noIgnore": {}, "no-ignore": {}, "--no-ignore": {},
7171
"ignore_files": {}, "ignoreFiles": {}, "ignore-files": {}, "--ignore-files": {}, "no_ignore_files": {}, "noIgnoreFiles": {}, "no-ignore-files": {}, "--no-ignore-files": {},
72+
"ignore_dot": {}, "ignoreDot": {}, "ignore-dot": {}, "--ignore-dot": {}, "no_ignore_dot": {}, "noIgnoreDot": {}, "no-ignore-dot": {}, "--no-ignore-dot": {},
7273
"ignore_vcs": {}, "ignoreVCS": {}, "ignore-vcs": {}, "--ignore-vcs": {}, "no_ignore_vcs": {}, "noIgnoreVCS": {}, "no-ignore-vcs": {}, "--no-ignore-vcs": {},
7374
}
7475

@@ -112,6 +113,7 @@ var grepSemanticBooleanKeys = map[string]struct{}{
112113
"include_zero": {}, "includeZero": {}, "include-zero": {}, "--include-zero": {},
113114
"no_ignore": {}, "noIgnore": {}, "no-ignore": {}, "--no-ignore": {},
114115
"ignore_files": {}, "ignoreFiles": {}, "ignore-files": {}, "--ignore-files": {}, "no_ignore_files": {}, "noIgnoreFiles": {}, "no-ignore-files": {}, "--no-ignore-files": {},
116+
"ignore_dot": {}, "ignoreDot": {}, "ignore-dot": {}, "--ignore-dot": {}, "no_ignore_dot": {}, "noIgnoreDot": {}, "no-ignore-dot": {}, "--no-ignore-dot": {},
115117
"ignore_vcs": {}, "ignoreVCS": {}, "ignore-vcs": {}, "--ignore-vcs": {}, "no_ignore_vcs": {}, "noIgnoreVCS": {}, "no-ignore-vcs": {}, "--no-ignore-vcs": {},
116118
}
117119

@@ -360,6 +362,14 @@ type grepInput struct {
360362
NoIgnoreFilesAlt bool `json:"noIgnoreFiles,omitempty"`
361363
NoIgnoreFilesDash bool `json:"no-ignore-files,omitempty"`
362364
LongNoIgnoreFiles bool `json:"--no-ignore-files,omitempty"`
365+
IgnoreDot bool `json:"ignore_dot,omitempty"`
366+
IgnoreDotAlt bool `json:"ignoreDot,omitempty"`
367+
IgnoreDotDash bool `json:"ignore-dot,omitempty"`
368+
LongIgnoreDot bool `json:"--ignore-dot,omitempty"`
369+
NoIgnoreDot bool `json:"no_ignore_dot,omitempty"`
370+
NoIgnoreDotAlt bool `json:"noIgnoreDot,omitempty"`
371+
NoIgnoreDotDash bool `json:"no-ignore-dot,omitempty"`
372+
LongNoIgnoreDot bool `json:"--no-ignore-dot,omitempty"`
363373
IgnoreVCS bool `json:"ignore_vcs,omitempty"`
364374
IgnoreVCSAlt bool `json:"ignoreVCS,omitempty"`
365375
IgnoreVCSDash bool `json:"ignore-vcs,omitempty"`
@@ -756,6 +766,16 @@ func NewGrepTool() tool.Tool {
756766
"--no-ignore-files": map[string]any{
757767
"type": "boolean",
758768
},
769+
"ignore_dot": map[string]any{"type": "boolean"},
770+
"ignoreDot": map[string]any{"type": "boolean"},
771+
"ignore-dot": map[string]any{"type": "boolean"},
772+
"--ignore-dot": map[string]any{"type": "boolean"},
773+
"no_ignore_dot": map[string]any{"type": "boolean"},
774+
"noIgnoreDot": map[string]any{"type": "boolean"},
775+
"no-ignore-dot": map[string]any{"type": "boolean"},
776+
"--no-ignore-dot": map[string]any{
777+
"type": "boolean",
778+
},
759779
"ignore_vcs": map[string]any{"type": "boolean"},
760780
"ignoreVCS": map[string]any{"type": "boolean"},
761781
"ignore-vcs": map[string]any{"type": "boolean"},
@@ -779,7 +799,7 @@ func NewGrepTool() tool.Tool {
779799
},
780800
},
781801
PromptFunc: func(tool.PromptContext) (string, error) {
782-
return "Searches text files under path using a regular expression or fixed string. pattern is the canonical search expression; regex/regexp/--regexp/-e are accepted aliases. output_mode may be files, files_with_matches, files_without_matches, content, or count; glob/-g/--glob, iglob/--iglob, type/-t/--type, and type_not/-T/--type-not optionally filter file paths. glob and iglob accept whitespace/comma-separated patterns, negation, and brace alternation; glob_case_insensitive/--glob-case-insensitive makes glob patterns ignore case. content mode supports context, before_context, after_context, -C, -B, -A, -n/--line-number and -N/--no-line-number line-number control, --column column-number output, byte_offset/--byte-offset/-b byte offset output, -H/--with-filename and -I/--no-filename filename prefix control, heading/--heading grouped file headings, path_separator/--path-separator display path separator control, null/--null NUL path terminators/separators, field_match_separator/--field-match-separator and field_context_separator/--field-context-separator output field separators, context_separator/--context-separator and no_context_separator/--no-context-separator context group separator control, offset, head_limit pagination, max_count/-m per-file match limiting, max_columns/--max-columns long-line omission, --max-columns-preview long-line previews, replace/--replace/-r display-only replacement, only_matching/-o/--only-matching matched-text output, vimgrep/--vimgrep per-match line output, passthru/--passthru/--passthrough all-line output, trim/--trim leading-whitespace trimming, and hidden/--hidden or no_hidden/--no-hidden hidden file traversal control. Use files/--files to list files that would be searched without requiring pattern, files_with_matches or -l to list files with matches, files_without_match or -L to list files without matches, and count/--count/-c for count mode. Count mode supports count_matches/--count-matches for occurrence counts and include_zero/--include-zero to include zero-count files. Use max_depth/--max-depth/-d to limit directory descent, and sort/--sort or sortr/--sortr with path or modified to control result ordering; --sort-files is accepted as a path-sort alias. Use fixed_strings/-F/--fixed-strings for literal matching, text/-a/--text to search binary-extension files as text, word_regexp/-w/--word-regexp for whole-word matches, line_regexp/-x/--line-regexp for whole-line matches, ignore_case/-i/--ignore-case for case-insensitive search, case_sensitive/-s/--case-sensitive to force case-sensitive matching, smart_case/-S/--smart-case for lowercase-only patterns, and invert_match/-v/--invert-match to select non-matching lines. Set no_ignore/--no-ignore to skip .gitignore/.ignore/.rgignore files, no_ignore_files/--no-ignore-files to skip .ignore/.rgignore while keeping .gitignore active, or no_ignore_vcs/--no-ignore-vcs to skip .gitignore while keeping .ignore/.rgignore active; VCS metadata and read-denied paths remain excluded. Set multiline to allow patterns to span lines with dot matching newlines.", nil
802+
return "Searches text files under path using a regular expression or fixed string. pattern is the canonical search expression; regex/regexp/--regexp/-e are accepted aliases. output_mode may be files, files_with_matches, files_without_matches, content, or count; glob/-g/--glob, iglob/--iglob, type/-t/--type, and type_not/-T/--type-not optionally filter file paths. glob and iglob accept whitespace/comma-separated patterns, negation, and brace alternation; glob_case_insensitive/--glob-case-insensitive makes glob patterns ignore case. content mode supports context, before_context, after_context, -C, -B, -A, -n/--line-number and -N/--no-line-number line-number control, --column column-number output, byte_offset/--byte-offset/-b byte offset output, -H/--with-filename and -I/--no-filename filename prefix control, heading/--heading grouped file headings, path_separator/--path-separator display path separator control, null/--null NUL path terminators/separators, field_match_separator/--field-match-separator and field_context_separator/--field-context-separator output field separators, context_separator/--context-separator and no_context_separator/--no-context-separator context group separator control, offset, head_limit pagination, max_count/-m per-file match limiting, max_columns/--max-columns long-line omission, --max-columns-preview long-line previews, replace/--replace/-r display-only replacement, only_matching/-o/--only-matching matched-text output, vimgrep/--vimgrep per-match line output, passthru/--passthru/--passthrough all-line output, trim/--trim leading-whitespace trimming, and hidden/--hidden or no_hidden/--no-hidden hidden file traversal control. Use files/--files to list files that would be searched without requiring pattern, files_with_matches or -l to list files with matches, files_without_match or -L to list files without matches, and count/--count/-c for count mode. Count mode supports count_matches/--count-matches for occurrence counts and include_zero/--include-zero to include zero-count files. Use max_depth/--max-depth/-d to limit directory descent, and sort/--sort or sortr/--sortr with path or modified to control result ordering; --sort-files is accepted as a path-sort alias. Use fixed_strings/-F/--fixed-strings for literal matching, text/-a/--text to search binary-extension files as text, word_regexp/-w/--word-regexp for whole-word matches, line_regexp/-x/--line-regexp for whole-line matches, ignore_case/-i/--ignore-case for case-insensitive search, case_sensitive/-s/--case-sensitive to force case-sensitive matching, smart_case/-S/--smart-case for lowercase-only patterns, and invert_match/-v/--invert-match to select non-matching lines. Set no_ignore/--no-ignore to skip .gitignore/.ignore/.rgignore files, no_ignore_dot/--no-ignore-dot to skip .ignore/.rgignore while keeping .gitignore active, or no_ignore_vcs/--no-ignore-vcs to skip .gitignore while keeping .ignore/.rgignore active; VCS metadata and read-denied paths remain excluded. Set multiline to allow patterns to span lines with dot matching newlines.", nil
783803
},
784804
NormalizeFunc: normalizeGrepRawInput,
785805
ValidateFunc: validateGrep,
@@ -1087,8 +1107,10 @@ func callGrep(ctx tool.Context, raw json.RawMessage, _ tool.ProgressSink) (contr
10871107
"no_ignore": noIgnore,
10881108
"ignore_vcs": !noIgnore && ignoreVCS,
10891109
"no_ignore_vcs": noIgnore || !ignoreVCS,
1110+
"ignore_dot": !noIgnore && ignoreFiles,
1111+
"no_ignore_dot": noIgnore || !ignoreFiles,
10901112
"ignore_files": !noIgnore && ignoreFiles,
1091-
"no_ignore_files": noIgnore || !ignoreFiles,
1113+
"no_ignore_files": noIgnore || grepNoIgnoreFiles(input),
10921114
"hidden": includeHidden,
10931115
"no_hidden": !includeHidden,
10941116
"multiline": grepMultiline(input),
@@ -2398,8 +2420,16 @@ func grepNoIgnoreFiles(input grepInput) bool {
23982420
input.LongNoIgnoreFiles
23992421
}
24002422

2423+
func grepNoIgnoreDot(input grepInput) bool {
2424+
return input.NoIgnoreDot ||
2425+
input.NoIgnoreDotAlt ||
2426+
input.NoIgnoreDotDash ||
2427+
input.LongNoIgnoreDot ||
2428+
grepNoIgnoreFiles(input)
2429+
}
2430+
24012431
func grepIgnoreFiles(input grepInput) bool {
2402-
if grepNoIgnoreFiles(input) {
2432+
if grepNoIgnoreDot(input) {
24032433
return false
24042434
}
24052435
return true

internal/tools/file/tools_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4120,10 +4120,29 @@ func TestGlobAndGrepRespectIgnoreFiles(t *testing.T) {
41204120
}
41214121
if noIgnoreFilesResult.StructuredContent["no_ignore"] != false ||
41224122
noIgnoreFilesResult.StructuredContent["ignore_files"] != false ||
4123+
noIgnoreFilesResult.StructuredContent["no_ignore_dot"] != true ||
41234124
noIgnoreFilesResult.StructuredContent["no_ignore_files"] != true {
41244125
t.Fatalf("grep no-ignore-files structured content = %#v", noIgnoreFilesResult.StructuredContent)
41254126
}
41264127

4128+
noIgnoreDotResult, err := executor.Execute(ctx, contracts.ToolUse{
4129+
ID: "toolu_grep_no_ignore_dot",
4130+
Name: "Grep",
4131+
Input: json.RawMessage(`{"pattern":"Needle","--no-ignore-dot":"true"}`),
4132+
}, nil)
4133+
if err != nil {
4134+
t.Fatal(err)
4135+
}
4136+
if noIgnoreDotResult.Content != expectedNoIgnoreFiles {
4137+
t.Fatalf("grep no-ignore-dot content = %#v", noIgnoreDotResult.Content)
4138+
}
4139+
if noIgnoreDotResult.StructuredContent["no_ignore"] != false ||
4140+
noIgnoreDotResult.StructuredContent["ignore_dot"] != false ||
4141+
noIgnoreDotResult.StructuredContent["no_ignore_dot"] != true ||
4142+
noIgnoreDotResult.StructuredContent["no_ignore_files"] != false {
4143+
t.Fatalf("grep no-ignore-dot structured content = %#v", noIgnoreDotResult.StructuredContent)
4144+
}
4145+
41274146
noIgnoreVCSResult, err := executor.Execute(ctx, contracts.ToolUse{
41284147
ID: "toolu_grep_no_ignore_vcs",
41294148
Name: "Grep",

0 commit comments

Comments
 (0)