fix(desktop): honor default project directory for new sessions (#43234)

* fix(desktop): honor default project directory for new sessions

The Settings picker persisted project-dir.json but the renderer kept
seeding new chats from sticky localStorage home. Prefer the configured
default on boot and session.create, pin TERMINAL_CWD at backend spawn,
and reject packaged install-dir paths that regressed after #37536.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(desktop): address review on default project dir PR

Add workspace cwd precedence tests, extract isPackagedInstallPath for
platform test coverage, and stop rewriting live $currentCwd when a
session is already active (cache-only until the next new chat).

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Austin Pickett
2026-06-09 23:28:59 -05:00
committed by GitHub
co-authored by Cursor
parent 8f73d0d945
commit 1770263ccc
12 changed files with 263 additions and 12 deletions
@@ -29,6 +29,7 @@ import {
$connection,
$sessions,
$workingSessionIds,
ensureDefaultWorkspaceCwd,
setConnection,
setSessionsLoading
} from '@/store/session'
@@ -351,6 +352,7 @@ export function useGatewayBoot({
message: translateNow('boot.steps.loadingSettings'),
progress: 97
})
await ensureDefaultWorkspaceCwd()
await callbacksRef.current.refreshHermesConfig()
if (cancelled) {
@@ -20,6 +20,7 @@ import {
$sessions,
$yoloActive,
getRememberedWorkspaceCwd,
workspaceCwdForNewSession,
sessionPinId,
setActiveSessionId,
setAwaitingResponse,
@@ -311,8 +312,9 @@ export function useSessionActions({
})
setSessionStartedAt(null)
setTurnStartedAt(null)
// New chats inherit the current workspace.
setCurrentCwd(getRememberedWorkspaceCwd())
// New chats start in the configured default project dir when set,
// otherwise the sticky last-used workspace (PR #37586).
setCurrentCwd(workspaceCwdForNewSession())
setCurrentBranch('')
clearComposerDraft()
clearComposerAttachments()
@@ -333,7 +335,7 @@ export function useSessionActions({
// Route the new chat to the chosen profile's backend (null = primary,
// so single-profile users are unaffected).
await ensureGatewayProfile($newChatProfile.get())
const cwd = $currentCwd.get().trim() || getRememberedWorkspaceCwd()
const cwd = $currentCwd.get().trim() || workspaceCwdForNewSession()
// Pass the owning profile so a new chat under a non-launch profile (global
// remote mode) builds its agent + persists against THAT profile's home/db.
const newChatProfile = $newChatProfile.get()
@@ -8,7 +8,7 @@ import { sessionTitle } from '@/lib/chat-runtime'
import { triggerHaptic } from '@/lib/haptics'
import { Archive, ArchiveOff, FolderOpen, Loader2, Trash2 } from '@/lib/icons'
import { notify, notifyError } from '@/store/notifications'
import { setSessions } from '@/store/session'
import { applyConfiguredDefaultProjectDir, ensureDefaultWorkspaceCwd, setSessions } from '@/store/session'
import type { SessionInfo } from '@/types/hermes'
import { EmptyState, ListRow, LoadingState, SectionHeading, SettingsContent } from './primitives'
@@ -196,6 +196,7 @@ function DefaultProjectDirSetting() {
setDir(result.dir)
setFallback(result.defaultLabel)
applyConfiguredDefaultProjectDir(result.dir)
})
return () => {
@@ -221,7 +222,8 @@ function DefaultProjectDirSetting() {
const result = await settings.setDefaultProjectDir(picked.dir)
setDir(result.dir)
notify({ durationMs: 2_000, kind: 'success', message: s.defaultDirUpdated })
applyConfiguredDefaultProjectDir(result.dir)
notify({ durationMs: 4_000, kind: 'success', message: s.defaultDirUpdated })
} catch (err) {
notifyError(err, s.updateDirFailed)
} finally {
@@ -241,6 +243,8 @@ function DefaultProjectDirSetting() {
try {
await settings.setDefaultProjectDir(null)
setDir(null)
applyConfiguredDefaultProjectDir(null)
await ensureDefaultWorkspaceCwd()
} catch (err) {
notifyError(err, s.clearDirFailed)
} finally {
@@ -268,7 +272,7 @@ function DefaultProjectDirSetting() {
)}
</div>
}
description={dir || s.defaultsTo(fallback || '~/hermes-projects')}
description={dir || s.defaultsTo(fallback || '~')}
title={dir ? dir : s.notSet}
/>
</div>