/ mervelas / src / utils / idePathConversion.ts
Swadhin Biswas feat: implement mervelas cli engine, built-in tools, and reactive UI · 4/3/2026
0358986
91 lines · 2.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * Path conversion utilities for IDE communication
 * Handles conversions between Claude's environment and the IDE's environment
 */

import { execFileSync } from 'child_process'

export interface IDEPathConverter {
  /**
   * Convert path from IDE format to Claude's local format
   * Used when reading workspace folders from IDE lockfile
   */
  toLocalPath(idePath: string): string

  /**
   * Convert path from Claude's local format to IDE format
   * Used when sending paths to IDE (showDiffInIDE, etc.)
   */
  toIDEPath(localPath: string): string
}

/**
 * Converter for Windows IDE + WSL Claude scenario
 */
export class WindowsToWSLConverter implements IDEPathConverter {
  constructor(private wslDistroName: string | undefined) {}

  toLocalPath(windowsPath: string): string {
    if (!windowsPath) return windowsPath

    // Check if this is a path from a different WSL distro
    if (this.wslDistroName) {
      const wslUncMatch = windowsPath.match(
        /^\\\\wsl(?:\.localhost|\$)\\([^\\]+)(.*)$/,
      )
      if (wslUncMatch && wslUncMatch[1] !== this.wslDistroName) {
        // Different distro - wslpath will fail, so return original path
        return windowsPath
      }
    }

    try {
      // Use wslpath to convert Windows paths to WSL paths
      const result = execFileSync('wslpath', ['-u', windowsPath], {
        encoding: 'utf8',
        stdio: ['pipe', 'pipe', 'ignore'], // wslpath writes "wslpath: <errortext>" to stderr
      }).trim()

      return result
    } catch {
      // If wslpath fails, fall back to manual conversion
      return windowsPath
        .replace(/\\/g, '/') // Convert backslashes to forward slashes
        .replace(/^([A-Z]):/i, (_, letter) => `/mnt/${letter.toLowerCase()}`)
    }
  }

  toIDEPath(wslPath: string): string {
    if (!wslPath) return wslPath

    try {
      // Use wslpath to convert WSL paths to Windows paths
      const result = execFileSync('wslpath', ['-w', wslPath], {
        encoding: 'utf8',
        stdio: ['pipe', 'pipe', 'ignore'], // wslpath writes "wslpath: <errortext>" to stderr
      }).trim()

      return result
    } catch {
      // If wslpath fails, return the original path
      return wslPath
    }
  }
}

/**
 * Check if distro names match for WSL UNC paths
 */
export function checkWSLDistroMatch(
  windowsPath: string,
  wslDistroName: string,
): boolean {
  const wslUncMatch = windowsPath.match(
    /^\\\\wsl(?:\.localhost|\$)\\([^\\]+)(.*)$/,
  )
  if (wslUncMatch) {
    return wslUncMatch[1] === wslDistroName
  }
  return true // Not a WSL UNC path, so no distro mismatch
}