@@ -259,18 +259,16 @@ func searchWithRipgrep(ctx context.Context, pattern, path, include string) ([]gr
262− // Parse ripgrep output format: file:line:content
262+ // Parse ripgrep output using null separation
263− parts := strings.SplitN(line, ":", 3)
263+ filePath, lineNumStr, lineText, ok := parseRipgrepLine(line)
264− if len(parts) < 3 {
264+ if !ok {
268− filePath := parts[0]
269− lineNum, err := strconv.Atoi(parts[1])
268+ lineNum, err := strconv.Atoi(lineNumStr)
270 if err != nil {
269 if err != nil {
273− lineText := parts[2]
275 fileInfo, err := os.Stat(filePath)
273 fileInfo, err := os.Stat(filePath)
276 if err != nil {
274 if err != nil {
@@ -288,6 +286,33 @@ func searchWithRipgrep(ctx context.Context, pattern, path, include string) ([]gr
288 return matches, nil
286 return matches, nil
289+// parseRipgrepLine parses ripgrep output with null separation to handle Windows paths
290+func parseRipgrepLine(line string) (filePath, lineNum, lineText string, ok bool) {
291+ // Split on null byte first to separate filename from rest
292+ parts := strings.SplitN(line, "\x00", 2)
293+ if len(parts) != 2 {
294+ return "", "", "", false
298+ remainder := parts[1]
300+ // Now split the remainder on first colon: "linenum:content"
301+ colonIndex := strings.Index(remainder, ":")
302+ if colonIndex == -1 {
303+ return "", "", "", false
306+ lineNumStr := remainder[:colonIndex]
307+ lineText = remainder[colonIndex+1:]
309+ if _, err := strconv.Atoi(lineNumStr); err != nil {
310+ return "", "", "", false
313+ return filePath, lineNumStr, lineText, true
291func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error) {
316func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error) {
292 matches := []grepMatch{}
317 matches := []grepMatch{}