( kind: 'ripgrep' | 'fzf', paths: Required<VaultTextSearchToolPaths> )
| 1910 | } |
| 1911 | |
| 1912 | async function searchExecutable( |
| 1913 | kind: 'ripgrep' | 'fzf', |
| 1914 | paths: Required<VaultTextSearchToolPaths> |
| 1915 | ): Promise<string | null> { |
| 1916 | const configured = kind === 'ripgrep' ? paths.ripgrepPath : paths.fzfPath |
| 1917 | if (!configured) { |
| 1918 | // Auto mode. GUI apps launched from Finder/Dock inherit only a minimal |
| 1919 | // PATH (/usr/bin:/bin:/usr/sbin:/sbin), so the bare command name often |
| 1920 | // isn't resolvable even when rg/fzf are installed in a non-standard place |
| 1921 | // (Homebrew, cargo, nix, …). Resolve through the user's login shell so the |
| 1922 | // absolute path flows into both detection and execution; fall back to the |
| 1923 | // bare name so an already-correct PATH (and Windows) keeps working. (#73) |
| 1924 | const command = kind === 'ripgrep' ? 'rg' : 'fzf' |
| 1925 | return (await resolveCommandViaLoginShell(command)) ?? command |
| 1926 | } |
| 1927 | if (!path.isAbsolute(configured)) return null |
| 1928 | |
| 1929 | const normalized = path.resolve(configured) |
| 1930 | const basename = path.basename(normalized).toLowerCase() |
| 1931 | if (!SEARCH_EXECUTABLE_NAMES[kind].has(basename)) return null |
| 1932 | |
| 1933 | try { |
| 1934 | const stat = await fs.stat(normalized) |
| 1935 | return stat.isFile() ? normalized : null |
| 1936 | } catch { |
| 1937 | return null |
| 1938 | } |
| 1939 | } |
| 1940 | |
| 1941 | async function commandAvailable(command: string): Promise<boolean> { |
| 1942 | try { |
no test coverage detected