diff --git a/packages/ui/src/components/chat/CommandAutocomplete.tsx b/packages/ui/src/components/chat/CommandAutocomplete.tsx index 6cf85536..355eca35 100644 --- a/packages/ui/src/components/chat/CommandAutocomplete.tsx +++ b/packages/ui/src/components/chat/CommandAutocomplete.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { cn, fuzzyMatch } from '@/lib/utils'; import { useSessionUIStore } from '@/sync/session-ui-store'; -import { useCommandsStore } from '@/stores/useCommandsStore'; -import { useSkillsStore } from '@/stores/useSkillsStore'; +import { selectCommandsForDirectory, useCommandsStore } from '@/stores/useCommandsStore'; +import { selectSkillsForDirectory, useSkillsStore } from '@/stores/useSkillsStore'; +import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { Icon } from "@/components/icon/Icon"; import { useI18n } from '@/lib/i18n'; @@ -73,10 +74,16 @@ export const CommandAutocomplete = React.forwardRef([]); const [loading, setLoading] = React.useState(false); - const commandsWithMetadata = useCommandsStore((s) => s.commands); - const refreshCommands = useCommandsStore((s) => s.loadCommands); - const skills = useSkillsStore((s) => s.skills); - const refreshSkills = useSkillsStore((s) => s.loadSkills); + // Commands and skills belong to the directory the composer sends to — the + // session's own directory, or the Chats root for a chat draft — not to the + // project the app was on last. + const effectiveDirectory = useEffectiveDirectory(); + const commandsWithMetadata = useCommandsStore((s) => selectCommandsForDirectory(s, effectiveDirectory)); + const loadCommandsForDirectory = useCommandsStore((s) => s.loadCommands); + const skills = useSkillsStore((s) => selectSkillsForDirectory(s, effectiveDirectory)); + const loadSkillsForDirectory = useSkillsStore((s) => s.loadSkills); + const refreshCommands = React.useCallback(() => loadCommandsForDirectory(effectiveDirectory), [effectiveDirectory, loadCommandsForDirectory]); + const refreshSkills = React.useCallback(() => loadSkillsForDirectory(effectiveDirectory), [effectiveDirectory, loadSkillsForDirectory]); const [selectedIndex, setSelectedIndex] = React.useState(0); const selectedIndexRef = React.useRef(0); const keyboardNavigationRef = React.useRef(false); diff --git a/packages/ui/src/components/chat/SkillAutocomplete.tsx b/packages/ui/src/components/chat/SkillAutocomplete.tsx index a5d05e4a..9a8d6088 100644 --- a/packages/ui/src/components/chat/SkillAutocomplete.tsx +++ b/packages/ui/src/components/chat/SkillAutocomplete.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { cn, fuzzyMatch } from '@/lib/utils'; -import { useSkillsStore } from '@/stores/useSkillsStore'; +import { selectSkillsForDirectory, useSkillsStore } from '@/stores/useSkillsStore'; +import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { useUIStore } from '@/stores/useUIStore'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { useMobileAutocompleteMaxHeight } from './useMobileAutocompleteMaxHeight'; @@ -38,13 +39,16 @@ export const SkillAutocomplete = React.forwardRef([]); const itemRefs = React.useRef<(HTMLDivElement | null)[]>([]); - const skills = useSkillsStore((s) => s.skills); + // Skills of the directory the composer sends to (session directory, or the + // Chats root for a chat draft), not of the project the app was on last. + const effectiveDirectory = useEffectiveDirectory(); + const skills = useSkillsStore((s) => selectSkillsForDirectory(s, effectiveDirectory)); const loadSkills = useSkillsStore((s) => s.loadSkills); React.useEffect(() => { - // Always trigger loadSkills when autocomplete opens to ensure project context is fresh - void loadSkills(); - }, [loadSkills]); + // Always trigger loadSkills when autocomplete opens to ensure the directory's skills are fresh + void loadSkills(effectiveDirectory); + }, [effectiveDirectory, loadSkills]); React.useEffect(() => { const normalizedQuery = searchQuery.trim(); diff --git a/packages/ui/src/hooks/useEffectiveDirectory.ts b/packages/ui/src/hooks/useEffectiveDirectory.ts index 1b783536..d5c6de23 100644 --- a/packages/ui/src/hooks/useEffectiveDirectory.ts +++ b/packages/ui/src/hooks/useEffectiveDirectory.ts @@ -3,6 +3,7 @@ import { useSessionWorktreeStore } from '@/sync/session-worktree-store'; import { getAttachedSessionDirectory } from '@/sync/session-worktree-contract'; import { useSessionDirectory } from '@/sync/sync-context'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; +import { getChatsRootForHome } from '@/lib/chatDirectories'; /** * Hook that resolves the effective working directory for tabs (Git, Diff, Files, Terminal). @@ -11,7 +12,10 @@ import { useDirectoryStore } from '@/stores/useDirectoryStore'; * 1. Worktree metadata path (for worktree sessions) * 2. Session directory (for active sessions) * 3. Draft session directoryOverride (when creating a new session) - * 4. Fallback directory from DirectoryStore + * 4. For a Chat draft, the prepared chat directory or the managed Chats root — + * never the project the app was on before, which would leak that + * project's files, commands, and skills into the chat + * 5. Fallback directory from DirectoryStore * * This ensures that tabs show content from the correct project directory * even when a draft session is being created. @@ -23,6 +27,7 @@ export const useEffectiveDirectory = (): string | undefined => { const worktreeAttachment = useSessionWorktreeStore((s) => currentSessionId ? s.getAttachment(currentSessionId) : undefined); const worktreeMap = useSessionUIStore((s) => s.worktreeMetadata); const fallbackDirectory = useDirectoryStore((s) => s.currentDirectory); + const homeDirectory = useDirectoryStore((s) => s.homeDirectory); // If we have an active session, use its directory if (currentSessionId) { @@ -44,6 +49,11 @@ export const useEffectiveDirectory = (): string | undefined => { return (newSessionDraft.bootstrapPendingDirectory || newSessionDraft.directoryOverride) ?? undefined; } + if (newSessionDraft?.open && newSessionDraft.target === 'chat') { + const chatDirectory = newSessionDraft.preparedChatDirectory ?? getChatsRootForHome(homeDirectory); + if (chatDirectory) return chatDirectory; + } + // Fall back to the global directory return fallbackDirectory ?? undefined; };