From 62d20bf05f593237b86382011e3f5d9f39ff02aa Mon Sep 17 00:00:00 2001 From: halilc4 Date: Sun, 15 Feb 2026 10:46:54 +0100 Subject: [PATCH 1/2] Fix WSL2 working directory not being set (#10) Convert Unix CWD paths to UNC format (\wsl.localhost\\) for WSL profiles so Tabby's fsSync.existsSync() validation passes. Co-Authored-By: Claude Opus 4.6 --- src/components/paneEditor.component.pug | 2 +- src/services/workspaceEditor.service.ts | 40 +++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/components/paneEditor.component.pug b/src/components/paneEditor.component.pug index 13896fe..e102c99 100644 --- a/src/components/paneEditor.component.pug +++ b/src/components/paneEditor.component.pug @@ -18,7 +18,7 @@ input.form-control( type='text', [(ngModel)]='pane.cwd', - placeholder='C:\\path\\to\\project' + placeholder='C:\\path\\to\\project or /home/user/project' ) .form-group diff --git a/src/services/workspaceEditor.service.ts b/src/services/workspaceEditor.service.ts index 0f53424..96b4cdb 100644 --- a/src/services/workspaceEditor.service.ts +++ b/src/services/workspaceEditor.service.ts @@ -177,7 +177,7 @@ export class WorkspaceEditorService { restoreFromPTYID: false, command: baseProfile.options?.command || '', args: baseProfile.options?.args || [], - cwd: pane.cwd || baseProfile.options?.cwd || '', + cwd: this.resolveWslCwd(pane.cwd || baseProfile.options?.cwd || '', baseProfile), env: baseProfile.options?.env || {}, width: null, height: null, @@ -207,7 +207,7 @@ export class WorkspaceEditorService { // tabTitle: workspace name (what user sees) // tabCustomTitle: pane.id (for matching in StartupCommandService) // workspaceId: for duplicate detection after Tabby recovery - const cwd = pane.cwd || baseProfile.options?.cwd || '' + const cwd = this.resolveWslCwd(pane.cwd || baseProfile.options?.cwd || '', baseProfile) return { type: 'app:local-tab', profile, @@ -249,6 +249,42 @@ export class WorkspaceEditorService { || 'workspace' } + private isWslProfile(profile: TabbyProfile): boolean { + return profile.type?.startsWith('local:wsl') ?? false + } + + private getWslDistroName(profile: TabbyProfile): string | null { + // From type: 'local:wsl-Ubuntu-22.04' → 'Ubuntu-22.04' + if (profile.type?.startsWith('local:wsl-')) { + return profile.type.substring('local:wsl-'.length) + } + // From args: ['-d', 'Ubuntu'] + const args = profile.options?.args || [] + const dIdx = args.indexOf('-d') + if (dIdx >= 0 && dIdx + 1 < args.length) { + return args[dIdx + 1] + } + return null + } + + /** + * Converts Unix CWD paths to Windows UNC format for WSL profiles. + * Tabby validates CWD with fsSync.existsSync() which fails for Unix paths on Windows. + * UNC paths (\\wsl.localhost\\) are resolved by Windows via WSL filesystem. + */ + private resolveWslCwd(cwd: string, profile: TabbyProfile): string { + if (!cwd || !cwd.startsWith('/') || !this.isWslProfile(profile)) { + return cwd + } + const distro = this.getWslDistroName(profile) + if (!distro) { + return cwd + } + // /home/user/project → \\wsl.localhost\Ubuntu\home\user\project + const winPath = cwd.replace(/\//g, '\\') + return `\\\\wsl.localhost\\${distro}${winPath}` + } + private getProfileById(profileId: string): TabbyProfile | undefined { const isLocalType = (type: string) => type === 'local' || type?.startsWith('local:') From 2b0535388798a0da0e13ca8ba285c2e53b375df8 Mon Sep 17 00:00:00 2001 From: halilc4 Date: Sun, 15 Feb 2026 12:06:14 +0100 Subject: [PATCH 2/2] Fix WSL CWD: use --cd arg injection instead of UNC paths (#10) The previous UNC path approach (commit 62d20bf) failed because: - isWslProfile() checked profile.type but Tabby sets type='local' for ALL built-in shells (CMD, PowerShell, WSL). Detection must use profile.id. - UNC paths (\wsl.localhost\...) are unreliable: Tabby validates CWD with fs.existsSync() which can fail if WSL isn't fully ready, and wsl.exe doesn't guarantee translating Windows UNC CWD to Linux CWD. New approach: inject `--cd ` into wsl.exe args, which directly sets the Linux working directory. This bypasses fs.existsSync validation and works for all WSL profiles (default and named distros). Co-Authored-By: Claude Opus 4.6 --- src/services/workspaceEditor.service.ts | 48 ++++++------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/src/services/workspaceEditor.service.ts b/src/services/workspaceEditor.service.ts index 96b4cdb..2587a37 100644 --- a/src/services/workspaceEditor.service.ts +++ b/src/services/workspaceEditor.service.ts @@ -172,12 +172,18 @@ export class WorkspaceEditorService { } } + const rawCwd = pane.cwd || baseProfile.options?.cwd || '' + const isWsl = this.isWslProfile(baseProfile) + const baseArgs = baseProfile.options?.args || [] + // Build complete profile object like Tabby expects const options = { restoreFromPTYID: false, command: baseProfile.options?.command || '', - args: baseProfile.options?.args || [], - cwd: this.resolveWslCwd(pane.cwd || baseProfile.options?.cwd || '', baseProfile), + // WSL: inject --cd to set Linux CWD directly (bypasses fs.existsSync validation) + args: isWsl && rawCwd ? [...baseArgs, '--cd', rawCwd] : baseArgs, + // WSL: don't set cwd (Unix paths fail fs.existsSync on Windows) + cwd: isWsl ? '' : rawCwd, env: baseProfile.options?.env || {}, width: null, height: null, @@ -207,7 +213,6 @@ export class WorkspaceEditorService { // tabTitle: workspace name (what user sees) // tabCustomTitle: pane.id (for matching in StartupCommandService) // workspaceId: for duplicate detection after Tabby recovery - const cwd = this.resolveWslCwd(pane.cwd || baseProfile.options?.cwd || '', baseProfile) return { type: 'app:local-tab', profile, @@ -216,7 +221,7 @@ export class WorkspaceEditorService { tabCustomTitle: pane.id, workspaceId, disableDynamicTitle: true, - cwd, + cwd: isWsl ? '' : rawCwd, } } @@ -249,40 +254,9 @@ export class WorkspaceEditorService { || 'workspace' } + /** Detects WSL profiles by ID prefix (type is always 'local' for all built-in shells). */ private isWslProfile(profile: TabbyProfile): boolean { - return profile.type?.startsWith('local:wsl') ?? false - } - - private getWslDistroName(profile: TabbyProfile): string | null { - // From type: 'local:wsl-Ubuntu-22.04' → 'Ubuntu-22.04' - if (profile.type?.startsWith('local:wsl-')) { - return profile.type.substring('local:wsl-'.length) - } - // From args: ['-d', 'Ubuntu'] - const args = profile.options?.args || [] - const dIdx = args.indexOf('-d') - if (dIdx >= 0 && dIdx + 1 < args.length) { - return args[dIdx + 1] - } - return null - } - - /** - * Converts Unix CWD paths to Windows UNC format for WSL profiles. - * Tabby validates CWD with fsSync.existsSync() which fails for Unix paths on Windows. - * UNC paths (\\wsl.localhost\\) are resolved by Windows via WSL filesystem. - */ - private resolveWslCwd(cwd: string, profile: TabbyProfile): string { - if (!cwd || !cwd.startsWith('/') || !this.isWslProfile(profile)) { - return cwd - } - const distro = this.getWslDistroName(profile) - if (!distro) { - return cwd - } - // /home/user/project → \\wsl.localhost\Ubuntu\home\user\project - const winPath = cwd.replace(/\//g, '\\') - return `\\\\wsl.localhost\\${distro}${winPath}` + return profile.id?.startsWith('local:wsl') ?? false } private getProfileById(profileId: string): TabbyProfile | undefined {