diff --git a/packages/ui/src/components/session/sidebar/sessions/SessionNodeItem.tsx b/packages/ui/src/components/session/sidebar/sessions/SessionNodeItem.tsx index 08b548dc..79f69a20 100644 --- a/packages/ui/src/components/session/sidebar/sessions/SessionNodeItem.tsx +++ b/packages/ui/src/components/session/sidebar/sessions/SessionNodeItem.tsx @@ -26,7 +26,7 @@ import { useGlobalSessionStatus, useSessionPermissions, useSessionQuestionCount import { useSessionMessageRecordsForExport } from '@/sync/use-sync'; import { useViewportStore, viewportSessionKey } from '@/sync/viewport-store'; import { DraggableSessionRow } from '../folders/sessionFolderDnd'; -import { getSessionWorktreeMenuDisabled, nodeContainsSessionId, nodeHasPinnedMembershipChange, selectQuestionBadgeSessionScopes } from './sessionNodeItemUtils'; +import { canShowSessionWorktreeMenu, getSessionWorktreeMenuDisabled, nodeContainsSessionId, nodeHasPinnedMembershipChange, selectQuestionBadgeSessionScopes } from './sessionNodeItemUtils'; import type { SessionNode } from '../types'; import { formatProjectLabel, formatSessionCompactDateLabel, formatSessionDateLabel, normalizePath, renderHighlightedText } from '../utils'; import { useProjectsStore } from '@/stores/useProjectsStore'; @@ -42,7 +42,7 @@ import { getSessionGoal } from '@/lib/sessionGoalMetadata'; import { sessionGoalStatusColor, sessionGoalStatusLabelKey } from '@/lib/sessionGoalPresentation'; import { getRuntimeBearerTokenSync } from '@/lib/runtime-auth'; import { getRuntimeApiBaseUrl } from '@/lib/runtime-switch'; -import { getChatsRootFromDirectory, isChatDirectoryPath } from '@/lib/chatDirectories'; +import { getChatsRootFromDirectory } from '@/lib/chatDirectories'; import { parseMultiRunSessionTitle } from '@/lib/multirun/title'; import { MultiRunFusionDialog } from '@/components/multirun/MultiRunFusionDialog'; import { FusionIcon } from '@/components/icons/FusionIcon'; @@ -991,7 +991,7 @@ function SessionNodeItemComponent(props: SessionNodeItemProps): React.ReactNode {t('sessions.sidebar.session.menu.exportMarkdown')} -{!isSubtaskSession && !archivedBucket && !isVSCode ? (() => { + {canShowSessionWorktreeMenu({ isSubtaskSession, archivedBucket: Boolean(archivedBucket), isVSCode, sessionDirectory }) ? (() => { const isWorktreeMenuDisabled = getSessionWorktreeMenuDisabled({ sessionDirectory, isStreaming, diff --git a/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.test.ts b/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.test.ts index 07c136b5..93128ec0 100644 --- a/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.test.ts +++ b/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.test.ts @@ -4,6 +4,7 @@ import { getRuntimeKey } from '@/lib/runtime-switch'; import { getPinnedSessionKey } from '@/stores/useSessionPinnedStore'; import { computeNodeStructureKey, + canShowSessionWorktreeMenu, getSessionWorktreeMenuDisabled, nodeHasPinnedMembershipChange, selectFolderRootNodes, @@ -192,3 +193,21 @@ describe('getSessionWorktreeMenuDisabled', () => { })).toBe(true); }); }); + +describe('canShowSessionWorktreeMenu', () => { + test('hides worktree moves for managed Chat directories', () => { + expect(canShowSessionWorktreeMenu({ + isSubtaskSession: false, + archivedBucket: false, + isVSCode: false, + sessionDirectory: '/home/test/.config/openchamber/chats/2026-08-25/session-1', + })).toBe(false); + + expect(canShowSessionWorktreeMenu({ + isSubtaskSession: false, + archivedBucket: false, + isVSCode: false, + sessionDirectory: '/repo', + })).toBe(true); + }); +}); diff --git a/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.ts b/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.ts index 631faad3..171c36cb 100644 --- a/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.ts +++ b/packages/ui/src/components/session/sidebar/sessions/sessionNodeItemUtils.ts @@ -1,6 +1,7 @@ import { getRuntimeKey } from '@/lib/runtime-switch'; import { matchesRankQuery } from '@/lib/search/fuzzySearch'; import { normalizePath } from '@/lib/pathNormalization'; +import { isChatDirectoryPath } from '@/lib/chatDirectories'; import { resolveGlobalSessionDirectory } from '@/stores/useGlobalSessionsStore'; import { getPinnedSessionKey } from '@/stores/useSessionPinnedStore'; import type { SessionNode } from '../types'; @@ -78,6 +79,21 @@ export type QuestionBadgeSessionScope = { sessionIDs: string[]; }; +export const canShowSessionWorktreeMenu = ({ + isSubtaskSession, + archivedBucket, + isVSCode, + sessionDirectory, +}: { + isSubtaskSession: boolean; + archivedBucket: boolean; + isVSCode: boolean; + sessionDirectory: string | null; +}): boolean => !isSubtaskSession + && !archivedBucket + && !isVSCode + && !isChatDirectoryPath(sessionDirectory); + export const getSessionWorktreeMenuDisabled = ({ sessionDirectory, isStreaming,