From 006d0a87e8e9afafb2468e877491a91253323497 Mon Sep 17 00:00:00 2001 From: Max Novich Date: Thu, 28 Aug 2025 10:56:22 -0700 Subject: [PATCH] Enhance `convert_path_with_tilde_expansion` to handle Windows-specific `~` and `~\...` paths. --- .../src/recipe/read_recipe_file_content.rs | 14 +++++ ui/desktop/src/main.ts | 57 +++++++++++-------- ui/desktop/src/utils/pathUtils.ts | 11 ++++ 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/crates/goose/src/recipe/read_recipe_file_content.rs b/crates/goose/src/recipe/read_recipe_file_content.rs index 1aeac3bc2d57..61992b616457 100644 --- a/crates/goose/src/recipe/read_recipe_file_content.rs +++ b/crates/goose/src/recipe/read_recipe_file_content.rs @@ -36,11 +36,25 @@ pub fn read_recipe_file>(recipe_path: P) -> Result { fn convert_path_with_tilde_expansion(path: &Path) -> PathBuf { if let Some(path_str) = path.to_str() { + // Handle exact "~" (Windows only to avoid changing behavior on Unix) + if cfg!(windows) && path_str == "~" { + if let Some(home_dir) = dirs::home_dir() { + return home_dir; + } + } + // Handle Unix-style "~/..." if let Some(stripped) = path_str.strip_prefix("~/") { if let Some(home_dir) = dirs::home_dir() { return home_dir.join(stripped); } } + // Handle Windows-style "~\\..." (Windows only) + #[cfg(windows)] + if let Some(stripped) = path_str.strip_prefix("~\\") { + if let Some(home_dir) = dirs::home_dir() { + return home_dir.join(stripped); + } + } } PathBuf::from(path) } diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 18658fbc723b..9ed1f91c9d86 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -1581,37 +1581,44 @@ ipcMain.handle('get-binary-path', (_event, binaryName) => { return getBinaryPath(app, binaryName); }); -ipcMain.handle('read-file', (_event, filePath) => { - return new Promise((resolve) => { - // Expand tilde to home directory +ipcMain.handle('read-file', async (_event, filePath) => { + try { const expandedPath = expandTilde(filePath); + if (process.platform === 'win32') { + const buffer = await fs.readFile(expandedPath); + return { file: buffer.toString('utf8'), filePath: expandedPath, error: null, found: true }; + } + // Non-Windows: keep previous behavior via cat for parity + return await new Promise((resolve) => { + const cat = spawn('cat', [expandedPath]); + let output = ''; + let errorOutput = ''; - const cat = spawn('cat', [expandedPath]); - let output = ''; - let errorOutput = ''; - - cat.stdout.on('data', (data) => { - output += data.toString(); - }); + cat.stdout.on('data', (data) => { + output += data.toString(); + }); - cat.stderr.on('data', (data) => { - errorOutput += data.toString(); - }); + cat.stderr.on('data', (data) => { + errorOutput += data.toString(); + }); - cat.on('close', (code) => { - if (code !== 0) { - // File not found or error - resolve({ file: '', filePath: expandedPath, error: errorOutput || null, found: false }); - return; - } - resolve({ file: output, filePath: expandedPath, error: null, found: true }); - }); + cat.on('close', (code) => { + if (code !== 0) { + resolve({ file: '', filePath: expandedPath, error: errorOutput || null, found: false }); + return; + } + resolve({ file: output, filePath: expandedPath, error: null, found: true }); + }); - cat.on('error', (error) => { - console.error('Error reading file:', error); - resolve({ file: '', filePath: expandedPath, error, found: false }); + cat.on('error', (error) => { + console.error('Error reading file:', error); + resolve({ file: '', filePath: expandedPath, error, found: false }); + }); }); - }); + } catch (error) { + console.error('Error reading file:', error); + return { file: '', filePath: expandTilde(filePath), error, found: false }; + } }); ipcMain.handle('write-file', async (_event, filePath, content) => { diff --git a/ui/desktop/src/utils/pathUtils.ts b/ui/desktop/src/utils/pathUtils.ts index d13e91cd122a..8eb3031dea2a 100644 --- a/ui/desktop/src/utils/pathUtils.ts +++ b/ui/desktop/src/utils/pathUtils.ts @@ -119,7 +119,18 @@ const addPaths = ( * @returns The expanded path with tilde replaced by home directory */ export function expandTilde(filePath: string): string { + if (!filePath || typeof filePath !== 'string') return filePath; + // Support "~", "~/..." and "~\\..." on Windows + if (filePath === '~') { + return os.homedir(); + } + if (filePath.startsWith('~/') || (process.platform === 'win32' && filePath.startsWith('~\\'))) { + // Remove the leading "~" and any separator that follows, then join + const remainder = filePath.slice(2); + return path.join(os.homedir(), remainder); + } if (filePath.startsWith('~')) { + // Generic fallback: replace only the first leading tilde return path.join(os.homedir(), filePath.slice(1)); } return filePath;