diff --git a/packages/desktop/src-tauri/src/commands/files.rs b/packages/desktop/src-tauri/src/commands/files.rs index 9969a690..ca1e143d 100644 --- a/packages/desktop/src-tauri/src/commands/files.rs +++ b/packages/desktop/src-tauri/src/commands/files.rs @@ -1,4 +1,5 @@ use crate::{DesktopRuntime, SettingsStore}; +use crate::path_utils::expand_tilde_path; use serde::Serialize; use std::{ collections::{HashSet, VecDeque}, @@ -346,7 +347,7 @@ async fn resolve_sandboxed_path( .filter(|value| !value.is_empty()); let candidate_path = match (candidate_input, workspace_root) { - (Some(value), _) => PathBuf::from(value), + (Some(value), _) => expand_tilde_path(value), (None, Some(root)) => root.clone(), (None, None) => default_home_directory(), }; @@ -376,7 +377,7 @@ async fn resolve_creatable_path( path: &str, workspace_root: Option<&PathBuf>, ) -> Result { - let candidate = PathBuf::from(path); + let candidate = expand_tilde_path(path); if candidate.as_os_str().is_empty() { return Err(FsCommandError::Other("Path is required".to_string())); } diff --git a/packages/desktop/src-tauri/src/commands/git.rs b/packages/desktop/src-tauri/src/commands/git.rs index 07c61b75..4356ec12 100644 --- a/packages/desktop/src-tauri/src/commands/git.rs +++ b/packages/desktop/src-tauri/src/commands/git.rs @@ -1,4 +1,5 @@ use crate::{DesktopRuntime, SettingsStore}; +use crate::path_utils::expand_tilde_path; use anyhow::{anyhow, Context, Result}; use log::{error, info, warn}; use regex::Regex; @@ -357,7 +358,7 @@ fn append_git_option_map(args: &mut Vec, map: &serde_json::Map Result { - let path_buf = PathBuf::from(path); + let path_buf = expand_tilde_path(path); if !path_buf.exists() { return Err(anyhow!("Directory does not exist: {}", path)); } diff --git a/packages/desktop/src-tauri/src/commands/permissions.rs b/packages/desktop/src-tauri/src/commands/permissions.rs index 0ed35905..754cc8d0 100644 --- a/packages/desktop/src-tauri/src/commands/permissions.rs +++ b/packages/desktop/src-tauri/src/commands/permissions.rs @@ -4,6 +4,7 @@ use tauri::AppHandle; use tauri::State; use crate::DesktopRuntime; +use crate::path_utils::expand_tilde_path; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -34,10 +35,12 @@ pub async fn process_directory_selection( path: String, state: State<'_, DesktopRuntime>, ) -> Result { - use std::path::PathBuf; - // Validate directory exists - let path_buf = PathBuf::from(&path); + let mut path_buf = expand_tilde_path(&path); + if let Ok(canonicalized) = std::fs::canonicalize(&path_buf) { + path_buf = canonicalized; + } + let normalized_path = path_buf.to_string_lossy().to_string(); if !path_buf.exists() { return Ok(DirectoryPermissionResult { success: false, @@ -64,7 +67,7 @@ pub async fn process_directory_selection( if let Some(obj) = settings.as_object_mut() { obj.insert( "lastDirectory".to_string(), - serde_json::Value::String(path.clone()), + serde_json::Value::String(normalized_path.clone()), ); } @@ -76,12 +79,12 @@ pub async fn process_directory_selection( info!( "[permissions] Updated settings with lastDirectory: {}", - path + normalized_path ); Ok(DirectoryPermissionResult { success: true, - path: Some(path), + path: Some(normalized_path), error: None, }) } @@ -110,7 +113,11 @@ pub async fn request_directory_access( ) -> Result { let path = request.path; - let path_buf = std::path::PathBuf::from(&path); + let mut path_buf = expand_tilde_path(&path); + if let Ok(canonicalized) = std::fs::canonicalize(&path_buf) { + path_buf = canonicalized; + } + let normalized_path = path_buf.to_string_lossy().to_string(); if !path_buf.exists() { return Ok(DirectoryPermissionResult { success: false, @@ -131,7 +138,7 @@ pub async fn request_directory_access( match std::fs::read_dir(&path_buf) { Ok(_) => Ok(DirectoryPermissionResult { success: true, - path: Some(path), + path: Some(normalized_path), error: None, }), Err(e) => Ok(DirectoryPermissionResult { diff --git a/packages/desktop/src-tauri/src/commands/settings.rs b/packages/desktop/src-tauri/src/commands/settings.rs index 0bfb2c4c..af4758c5 100644 --- a/packages/desktop/src-tauri/src/commands/settings.rs +++ b/packages/desktop/src-tauri/src/commands/settings.rs @@ -4,6 +4,7 @@ use std::collections::HashSet; use tauri::State; use crate::DesktopRuntime; +use crate::path_utils::expand_tilde_path; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -105,12 +106,14 @@ fn sanitize_settings_update(payload: &Value) -> Value { } if let Some(Value::String(s)) = obj.get("lastDirectory") { if !s.is_empty() { - result_obj.insert("lastDirectory".to_string(), json!(s)); + let expanded = expand_tilde_path(s).to_string_lossy().to_string(); + result_obj.insert("lastDirectory".to_string(), json!(expanded)); } } if let Some(Value::String(s)) = obj.get("homeDirectory") { if !s.is_empty() { - result_obj.insert("homeDirectory".to_string(), json!(s)); + let expanded = expand_tilde_path(s).to_string_lossy().to_string(); + result_obj.insert("homeDirectory".to_string(), json!(expanded)); } } if let Some(Value::String(s)) = obj.get("uiFont") { diff --git a/packages/desktop/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs index b445df94..4545bb7e 100644 --- a/packages/desktop/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -7,6 +7,7 @@ mod session_activity; mod opencode_config; mod opencode_manager; mod window_state; +mod path_utils; use std::{collections::HashMap, path::PathBuf, sync::Arc, time::{Duration, Instant}}; @@ -63,6 +64,7 @@ use tokio::{ }; use tower_http::cors::CorsLayer; use window_state::{load_window_state, persist_window_state, WindowStateManager}; +use path_utils::expand_tilde_path; #[cfg(target_os = "macos")] use std::sync::atomic::{AtomicBool, Ordering}; @@ -1485,7 +1487,10 @@ async fn change_directory_handler( return Err(StatusCode::BAD_REQUEST); } - let resolved_path = PathBuf::from(requested_path); + let mut resolved_path = expand_tilde_path(requested_path); + if !resolved_path.is_absolute() { + resolved_path = state.opencode.get_working_directory().join(resolved_path); + } // Validate directory exists and is accessible match fs::metadata(&resolved_path).await { @@ -1507,6 +1512,10 @@ async fn change_directory_handler( } } + if let Ok(canonicalized) = fs::canonicalize(&resolved_path).await { + resolved_path = canonicalized; + } + let current_dir = state.opencode.get_working_directory(); let is_running = state.opencode.current_port().is_some(); @@ -1682,7 +1691,7 @@ impl SettingsStore { .and_then(|value| value.as_str()) .map(str::trim) .filter(|value| !value.is_empty()) - .map(PathBuf::from); + .map(expand_tilde_path); Ok(candidate) } } diff --git a/packages/desktop/src-tauri/src/path_utils.rs b/packages/desktop/src-tauri/src/path_utils.rs new file mode 100644 index 00000000..0dadc2c7 --- /dev/null +++ b/packages/desktop/src-tauri/src/path_utils.rs @@ -0,0 +1,21 @@ +use std::path::PathBuf; + +pub fn expand_tilde_path(value: &str) -> PathBuf { + let trimmed = value.trim(); + if trimmed.is_empty() { + return PathBuf::from(trimmed); + } + + let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/")); + + if trimmed == "~" { + return home; + } + + if trimmed.starts_with("~/") || trimmed.starts_with("~\\") { + return home.join(&trimmed[2..]); + } + + PathBuf::from(trimmed) +} + diff --git a/packages/vscode/src/bridge.ts b/packages/vscode/src/bridge.ts index efb01a3b..ddc40c51 100644 --- a/packages/vscode/src/bridge.ts +++ b/packages/vscode/src/bridge.ts @@ -79,6 +79,34 @@ const persistSettings = async (changes: Record, ctx?: BridgeCon const normalizeFsPath = (value: string) => value.replace(/\\/g, '/'); +const expandTildePath = (value: string) => { + const trimmed = (value || '').trim(); + if (!trimmed) { + return trimmed; + } + + if (trimmed === '~') { + return os.homedir(); + } + + if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) { + return path.join(os.homedir(), trimmed.slice(2)); + } + + return trimmed; +}; + +const resolveUserPath = (value: string, baseDirectory: string) => { + const expanded = expandTildePath(value); + if (!expanded) { + return expanded; + } + if (path.isAbsolute(expanded)) { + return expanded; + } + return path.resolve(baseDirectory, expanded); +}; + const listDirectoryEntries = async (dirPath: string) => { const uri = vscode.Uri.file(dirPath); const entries = await vscode.workspace.fs.readDirectory(uri); @@ -187,7 +215,10 @@ const searchFilesystemFiles = async (rootPath: string, query: string, limit: num }; const searchDirectory = async (directory: string, query: string, limit = 60) => { - const rootPath = directory || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''; + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); + const rootPath = directory + ? resolveUserPath(directory, workspaceRoot) + : vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''; if (!rootPath) return []; const sanitizedQuery = query?.trim() || ''; @@ -327,14 +358,16 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo case 'files:list': { const { path: dirPath } = payload as { path: string }; - const uri = vscode.Uri.file(dirPath); + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); + const resolvedPath = resolveUserPath(dirPath, workspaceRoot); + const uri = vscode.Uri.file(resolvedPath); const entries = await vscode.workspace.fs.readDirectory(uri); const result: FileEntry[] = entries.map(([name, fileType]) => ({ name, path: vscode.Uri.joinPath(uri, name).fsPath, isDirectory: fileType === vscode.FileType.Directory, })); - return { id, type, success: true, data: { directory: dirPath, entries: result } }; + return { id, type, success: true, data: { directory: normalizeFsPath(resolvedPath), entries: result } }; } case 'files:search': { @@ -360,9 +393,12 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo } case 'api:fs:list': { - const target = (payload as { path?: string })?.path || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); - const entries = await listDirectoryEntries(target); - return { id, type, success: true, data: { entries, directory: target } }; + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); + const target = (payload as { path?: string })?.path || workspaceRoot; + const resolvedPath = resolveUserPath(target, workspaceRoot) || workspaceRoot; + const entries = await listDirectoryEntries(resolvedPath); + const normalized = normalizeFsPath(resolvedPath); + return { id, type, success: true, data: { entries, directory: normalized, path: normalized } }; } case 'api:fs:search': { @@ -376,8 +412,10 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo if (!target) { return { id, type, success: false, error: 'Path is required' }; } - await vscode.workspace.fs.createDirectory(vscode.Uri.file(target)); - return { id, type, success: true, data: { success: true, path: normalizeFsPath(target) } }; + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); + const resolvedPath = resolveUserPath(target, workspaceRoot); + await vscode.workspace.fs.createDirectory(vscode.Uri.file(resolvedPath)); + return { id, type, success: true, data: { success: true, path: normalizeFsPath(resolvedPath) } }; } case 'api:fs/home': { @@ -615,7 +653,10 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo if (!target) { return { id, type, success: false, error: 'Path is required' }; } - const result = await ctx?.manager?.setWorkingDirectory(target); + const baseDirectory = + ctx?.manager?.getWorkingDirectory() || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || os.homedir(); + const resolvedPath = resolveUserPath(target, baseDirectory); + const result = await ctx?.manager?.setWorkingDirectory(resolvedPath); if (!result) { return { id, type, success: false, error: 'OpenCode manager unavailable' }; } diff --git a/packages/web/server/index.js b/packages/web/server/index.js index 918f1d8a..cf6300ee 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -37,6 +37,27 @@ const FILE_SEARCH_EXCLUDED_DIRS = new Set([ 'logs' ]); +const normalizeDirectoryPath = (value) => { + if (typeof value !== 'string') { + return value; + } + + const trimmed = value.trim(); + if (!trimmed) { + return trimmed; + } + + if (trimmed === '~') { + return os.homedir(); + } + + if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) { + return path.join(os.homedir(), trimmed.slice(2)); + } + + return trimmed; +}; + const normalizeRelativeSearchPath = (rootPath, targetPath) => { const relative = path.relative(rootPath, targetPath) || path.basename(targetPath); return relative.split(path.sep).join('/') || targetPath; @@ -2675,8 +2696,11 @@ async function main(options = {}) { const worktrees = await getWorktrees(directory); res.json(worktrees); } catch (error) { - console.error('Failed to get worktrees:', error); - res.status(500).json({ error: error.message || 'Failed to get worktrees' }); + // Worktrees are an optional feature. Avoid repeated 500s (and repeated client retries) + // when the directory isn't a git repo or uses shell shorthand like "~/". + console.warn('Failed to get worktrees, returning empty list:', error?.message || error); + res.setHeader('X-OpenChamber-Warning', 'git worktrees unavailable'); + res.json([]); } }); @@ -2815,15 +2839,17 @@ async function main(options = {}) { return res.status(400).json({ error: 'Path is required' }); } - const normalizedPath = path.normalize(dirPath); + const expandedPath = normalizeDirectoryPath(dirPath); + const normalizedPath = path.normalize(expandedPath); if (normalizedPath.includes('..')) { return res.status(400).json({ error: 'Invalid path: path traversal not allowed' }); } - fs.mkdirSync(dirPath, { recursive: true }); - console.log(`Created directory: ${dirPath}`); + const resolvedPath = path.resolve(expandedPath); + fs.mkdirSync(resolvedPath, { recursive: true }); + console.log(`Created directory: ${resolvedPath}`); - res.json({ success: true, path: dirPath }); + res.json({ success: true, path: resolvedPath }); } catch (error) { console.error('Failed to create directory:', error); res.status(500).json({ error: error.message || 'Failed to create directory' }); @@ -2837,7 +2863,7 @@ async function main(options = {}) { return res.status(400).json({ error: 'Path is required' }); } - const resolvedPath = path.resolve(requestedPath); + const resolvedPath = path.resolve(normalizeDirectoryPath(requestedPath)); let stats; try { stats = await fsPromises.stat(resolvedPath); @@ -2883,7 +2909,7 @@ async function main(options = {}) { : os.homedir(); try { - const resolvedPath = path.resolve(rawPath); + const resolvedPath = path.resolve(normalizeDirectoryPath(rawPath)); const stats = await fsPromises.stat(resolvedPath); if (!stats.isDirectory()) { @@ -2948,7 +2974,7 @@ async function main(options = {}) { const limit = Math.max(1, Math.min(parsedLimit, MAX_FILE_SEARCH_LIMIT)); try { - const resolvedRoot = path.resolve(rawRoot); + const resolvedRoot = path.resolve(normalizeDirectoryPath(rawRoot)); const stats = await fsPromises.stat(resolvedRoot); if (!stats.isDirectory()) { return res.status(400).json({ error: 'Specified root is not a directory' }); diff --git a/packages/web/server/lib/git-service.js b/packages/web/server/lib/git-service.js index 433e7c92..0e194f03 100644 --- a/packages/web/server/lib/git-service.js +++ b/packages/web/server/lib/git-service.js @@ -1,27 +1,51 @@ import simpleGit from 'simple-git'; import fs from 'fs'; import path from 'path'; +import os from 'os'; import { execFile } from 'child_process'; import { promisify } from 'util'; const fsp = fs.promises; const execFileAsync = promisify(execFile); +const normalizeDirectoryPath = (value) => { + if (typeof value !== 'string') { + return value; + } + + const trimmed = value.trim(); + if (!trimmed) { + return trimmed; + } + + if (trimmed === '~') { + return os.homedir(); + } + + if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) { + return path.join(os.homedir(), trimmed.slice(2)); + } + + return trimmed; +}; + export async function isGitRepository(directory) { - if (!directory || !fs.existsSync(directory)) { + const directoryPath = normalizeDirectoryPath(directory); + if (!directoryPath || !fs.existsSync(directoryPath)) { return false; } - const gitDir = path.join(directory, '.git'); + const gitDir = path.join(directoryPath, '.git'); return fs.existsSync(gitDir); } export async function ensureOpenChamberIgnored(directory) { - if (!directory || !fs.existsSync(directory)) { + const directoryPath = normalizeDirectoryPath(directory); + if (!directoryPath || !fs.existsSync(directoryPath)) { return false; } - const gitDir = path.join(directory, '.git'); + const gitDir = path.join(directoryPath, '.git'); if (!fs.existsSync(gitDir)) { return false; } @@ -78,7 +102,7 @@ export async function getGlobalIdentity() { } export async function getCurrentIdentity(directory) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { @@ -110,7 +134,7 @@ export async function getCurrentIdentity(directory) { } export async function setLocalIdentity(directory, profile) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { @@ -134,7 +158,8 @@ export async function setLocalIdentity(directory, profile) { } export async function getStatus(directory) { - const git = simpleGit(directory); + const directoryPath = normalizeDirectoryPath(directory); + const git = simpleGit(directoryPath); try { // Use -uall to show all untracked files individually, not just directories @@ -194,7 +219,7 @@ export async function getStatus(directory) { return null; } - const absolutePath = path.join(directory, file.path); + const absolutePath = path.join(directoryPath, file.path); try { const stat = await fsp.stat(absolutePath); @@ -266,7 +291,7 @@ export async function getStatus(directory) { } export async function getDiff(directory, { path, staged = false, contextLines = 3 } = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const args = ['diff', '--no-color']; @@ -338,7 +363,8 @@ export async function getFileDiff(directory, { path: filePath, staged = false } throw new Error('directory and path are required for getFileDiff'); } - const git = simpleGit(directory); + const directoryPath = normalizeDirectoryPath(directory); + const git = simpleGit(directoryPath); const isImage = isImageFile(filePath); const mimeType = isImage ? getImageMimeType(filePath) : null; @@ -348,7 +374,7 @@ export async function getFileDiff(directory, { path: filePath, staged = false } // For images, use git show with raw output and convert to base64 try { const { stdout } = await execFileAsync('git', ['show', `HEAD:${filePath}`], { - cwd: directory, + cwd: directoryPath, encoding: 'buffer', maxBuffer: 50 * 1024 * 1024, // 50MB max }); @@ -365,7 +391,7 @@ export async function getFileDiff(directory, { path: filePath, staged = false } original = ''; } - const fullPath = path.join(directory, filePath); + const fullPath = path.join(directoryPath, filePath); let modified = ''; try { const stat = await fsp.stat(fullPath); @@ -395,8 +421,9 @@ export async function getFileDiff(directory, { path: filePath, staged = false } } export async function revertFile(directory, filePath) { - const git = simpleGit(directory); - const repoRoot = path.resolve(directory); + const directoryPath = normalizeDirectoryPath(directory); + const git = simpleGit(directoryPath); + const repoRoot = path.resolve(directoryPath); const absoluteTarget = path.resolve(repoRoot, filePath); if (!absoluteTarget.startsWith(repoRoot + path.sep) && absoluteTarget !== repoRoot) { @@ -460,7 +487,7 @@ export async function collectDiffs(directory, files = []) { } export async function pull(directory, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const result = await git.pull( @@ -483,7 +510,7 @@ export async function pull(directory, options = {}) { } export async function push(directory, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const result = await git.push( @@ -510,7 +537,7 @@ export async function deleteRemoteBranch(directory, options = {}) { throw new Error('branch is required to delete remote branch'); } - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); const targetBranch = branch.startsWith('refs/heads/') ? branch.substring('refs/heads/'.length) : branch; @@ -526,7 +553,7 @@ export async function deleteRemoteBranch(directory, options = {}) { } export async function fetch(directory, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { await git.fetch( @@ -543,7 +570,7 @@ export async function fetch(directory, options = {}) { } export async function commit(directory, message, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { @@ -573,7 +600,7 @@ export async function commit(directory, message, options = {}) { } export async function getBranches(directory) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const result = await git.branch(); @@ -627,7 +654,7 @@ async function filterActiveRemoteBranches(git, remoteBranches) { } export async function createBranch(directory, branchName, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { await git.checkoutBranch(branchName, options.startPoint || 'HEAD'); @@ -639,7 +666,7 @@ export async function createBranch(directory, branchName, options = {}) { } export async function checkoutBranch(directory, branchName) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { await git.checkout(branchName); @@ -651,7 +678,12 @@ export async function checkoutBranch(directory, branchName) { } export async function getWorktrees(directory) { - const git = simpleGit(directory); + const directoryPath = normalizeDirectoryPath(directory); + if (!directoryPath || !fs.existsSync(directoryPath) || !fs.existsSync(path.join(directoryPath, '.git'))) { + return []; + } + + const git = simpleGit(directoryPath); try { const result = await git.raw(['worktree', 'list', '--porcelain']); @@ -684,13 +716,13 @@ export async function getWorktrees(directory) { return worktrees; } catch (error) { - console.error('Failed to list worktrees:', error); - throw error; + console.warn('Failed to list worktrees, returning empty list:', error?.message || error); + return []; } } export async function addWorktree(directory, worktreePath, branch, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const args = ['worktree', 'add']; @@ -719,7 +751,7 @@ export async function addWorktree(directory, worktreePath, branch, options = {}) } export async function removeWorktree(directory, worktreePath, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const args = ['worktree', 'remove', worktreePath]; @@ -738,7 +770,7 @@ export async function removeWorktree(directory, worktreePath, options = {}) { } export async function deleteBranch(directory, branch, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const branchName = branch.startsWith('refs/heads/') @@ -754,7 +786,7 @@ export async function deleteBranch(directory, branch, options = {}) { } export async function getLog(directory, options = {}) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const maxCount = options.maxCount || 50; @@ -852,7 +884,7 @@ export async function getLog(directory, options = {}) { } export async function isLinkedWorktree(directory) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try { const [gitDir, gitCommonDir] = await Promise.all([ git.raw(['rev-parse', '--git-dir']).then((output) => output.trim()), @@ -866,7 +898,7 @@ export async function isLinkedWorktree(directory) { } export async function getCommitFiles(directory, commitHash) { - const git = simpleGit(directory); + const git = simpleGit(normalizeDirectoryPath(directory)); try {