@@ -302,10 +302,24 @@ func (d *IDEDetector) detectLinux(ctx context.Context, spec ideSpec) (model.IDE,
302302}
303303
304304// resolveLinuxVersion determines the IDE version on Linux.
305- // Tries: binary --version, then product-info.json (flat layout), then .eclipseproduct.
305+ // Prefers the binary within installDir to avoid version mismatch with a different
306+ // install found via PATH. Falls back to PATH lookup, then metadata files.
306307func (d * IDEDetector ) resolveLinuxVersion (ctx context.Context , spec ideSpec , installDir string ) string {
307- // Try binary --version from PATH first (most reliable on Linux)
308308 if spec .LinuxBinary != "" && spec .VersionFlag != "" {
309+ // Try binary inside the detected install directory first
310+ for _ , relBin := range []string {
311+ filepath .Join ("bin" , spec .LinuxBinary ),
312+ spec .LinuxBinary ,
313+ } {
314+ localBin := filepath .Join (installDir , relBin )
315+ if d .exec .FileExists (localBin ) {
316+ if v := runVersionCmd (ctx , d .exec , localBin , spec .VersionFlag ); v != "unknown" {
317+ return v
318+ }
319+ }
320+ }
321+
322+ // Fall back to PATH (may be a different install, but better than unknown)
309323 if binPath , err := d .exec .LookPath (spec .LinuxBinary ); err == nil {
310324 if v := runVersionCmd (ctx , d .exec , binPath , spec .VersionFlag ); v != "unknown" {
311325 return v
@@ -572,46 +586,55 @@ func (d *IDEDetector) discoverViaDesktopFile(spec ideSpec, homeDir string) (stri
572586}
573587
574588// parseDesktopExec extracts the binary path from the first Exec= line in a .desktop file.
575- // Strips field codes (%F, %U, etc.) and flags (--new-window, etc.).
589+ // Handles wrapper commands like "env VAR=val /snap/bin/code %U" by scanning tokens
590+ // for the first absolute path.
576591func parseDesktopExec (content string ) string {
577592 for _ , line := range strings .Split (content , "\n " ) {
578593 line = strings .TrimSpace (line )
579594 if ! strings .HasPrefix (line , "Exec=" ) {
580595 continue
581596 }
582597 execValue := strings .TrimPrefix (line , "Exec=" )
583- // The first token is the binary path; the rest are arguments
584598 parts := strings .Fields (execValue )
585- if len (parts ) == 0 {
586- continue
587- }
588- binPath := parts [0 ]
589- // Skip entries that are just bare command names (no path)
590- if ! strings .Contains (binPath , "/" ) {
591- continue
599+
600+ // Find the first token that looks like an absolute path
601+ for _ , token := range parts {
602+ // Skip field codes (%F, %U, etc.) and flags (--foo)
603+ if strings .HasPrefix (token , "%" ) || strings .HasPrefix (token , "-" ) {
604+ continue
605+ }
606+ // Skip env var assignments (VAR=val)
607+ if strings .Contains (token , "=" ) && ! strings .HasPrefix (token , "/" ) {
608+ continue
609+ }
610+ if strings .HasPrefix (token , "/" ) {
611+ return token
612+ }
592613 }
593- return binPath
594614 }
595615 return ""
596616}
597617
598618// resolveInstallDirFromBinary walks up from a binary path to find the app root directory.
599- // Strips known bin subdirectories: /bin/, /resources/app/bin/.
619+ // Handles two common layouts:
620+ // - /usr/share/code/bin/code -> /usr/share/code
621+ // - /usr/share/cursor/resources/app/bin/cursor -> /usr/share/cursor
600622func resolveInstallDirFromBinary (binPath string ) string {
601623 dir := filepath .Dir (binPath )
602624 base := filepath .Base (dir )
603625
604- // /usr/share/code/bin/code -> /usr/share/code
605- // /opt/Windsurf/bin/windsurf -> /opt/Windsurf
606- if base == "bin" {
607- return filepath .Dir (dir )
626+ if base != "bin" {
627+ return dir
608628 }
629+
630+ parent := filepath .Dir (dir )
631+
632+ // Check for resources/app/bin layout (Electron apps like Cursor)
609633 // /usr/share/cursor/resources/app/bin/cursor -> /usr/share/cursor
610- if base == "bin" || filepath .Base (filepath .Dir (dir )) == "app" {
611- candidate := filepath .Dir (filepath .Dir (filepath .Dir (dir )))
612- if candidate != "" && candidate != "." {
613- return candidate
614- }
634+ if filepath .Base (parent ) == "app" && filepath .Base (filepath .Dir (parent )) == "resources" {
635+ return filepath .Dir (filepath .Dir (parent ))
615636 }
616- return dir
637+
638+ // Simple /bin/ layout: /usr/share/code/bin/code -> /usr/share/code
639+ return parent
617640}
0 commit comments