diff --git a/docs/AGENTS.md b/docs/AGENTS.md
index f59c2460aa..621021b115 100644
--- a/docs/AGENTS.md
+++ b/docs/AGENTS.md
@@ -116,9 +116,9 @@ Mobile app tests live in `mobile/src/**/*.test.ts` and use Bun's built-in test r
## Styling
- Never use emoji characters as UI icons or status indicators; emoji rendering varies across platforms and fonts.
-- Prefer SVG icons (usually from `lucide-react`) or shared icon components under `src/browser/components/icons/`.
+- Prefer SVG icons (usually from `lucide-react`) or shared icon components under `src/browser/components/Icons/`.
- For tool call headers, use `ToolIcon` from `src/browser/components/tools/shared/ToolPrimitives.tsx`.
-- If a tool/agent provides an emoji string (e.g., `status_set` or `displayStatus`), render via `EmojiIcon` (`src/browser/components/icons/EmojiIcon.tsx`) instead of rendering the emoji.
+- If a tool/agent provides an emoji string (e.g., `status_set` or `displayStatus`), render via `EmojiIcon` (`src/browser/components/Icons/EmojiIcon.tsx`) instead of rendering the emoji.
- If a new emoji appears in tool output, extend `EmojiIcon` to map it to an SVG icon.
- Colors defined in `src/browser/styles/globals.css` (`:root @theme` block). Reference via CSS variables (e.g., `var(--color-plan-mode)`), never hardcode hex values.
diff --git a/src/browser/App.tsx b/src/browser/App.tsx
index dba6636336..eef7880fdd 100644
--- a/src/browser/App.tsx
+++ b/src/browser/App.tsx
@@ -6,10 +6,10 @@ import "./styles/globals.css";
import { useWorkspaceContext, toWorkspaceSelection } from "./contexts/WorkspaceContext";
import { MUX_HELP_CHAT_WORKSPACE_ID } from "@/common/constants/muxChat";
import { useProjectContext } from "./contexts/ProjectContext";
-import type { WorkspaceSelection } from "./components/ProjectSidebar/ProjectSidebar";
+import type { WorkspaceSelection } from "./features/Project/ProjectSidebar/ProjectSidebar";
import { LeftSidebar } from "./components/LeftSidebar/LeftSidebar";
import { ProjectCreateModal } from "./components/ProjectCreateModal/ProjectCreateModal";
-import { AIView } from "./components/AIView/AIView";
+import { AIView } from "./features/Chat/AIView/AIView";
import { ErrorBoundary } from "./components/ErrorBoundary/ErrorBoundary";
import {
usePersistedState,
@@ -29,7 +29,7 @@ import { CommandRegistryProvider, useCommandRegistry } from "./contexts/CommandR
import { useOpenTerminal } from "./hooks/useOpenTerminal";
import type { CommandAction } from "./contexts/CommandRegistryContext";
import { useTheme, type ThemeMode } from "./contexts/ThemeContext";
-import { CommandPalette } from "./components/CommandPalette/CommandPalette";
+import { CommandPalette } from "./features/CommandPalette/CommandPalette/CommandPalette";
import { buildCoreSources, type BuildSourcesParams } from "./utils/commands/sources";
import { THINKING_LEVELS, type ThinkingLevel } from "@/common/types/thinking";
@@ -81,7 +81,7 @@ import { FeatureFlagsProvider } from "./contexts/FeatureFlagsContext";
import { ExperimentsProvider } from "./contexts/ExperimentsContext";
import { ProviderOptionsProvider } from "./contexts/ProviderOptionsContext";
import { getWorkspaceSidebarKey } from "./utils/workspace";
-import { WindowsToolchainBanner } from "./components/WindowsToolchainBanner/WindowsToolchainBanner";
+import { WindowsToolchainBanner } from "./features/AppShell/WindowsToolchainBanner/WindowsToolchainBanner";
import { RosettaBanner } from "./components/RosettaBanner/RosettaBanner";
import { isDesktopMode } from "./hooks/useDesktopTitlebar";
import { cn } from "@/common/lib/utils";
diff --git a/src/browser/assets/file-icons/seti-icon-theme.json b/src/browser/assets/file-icons/seti-icon-theme.json
index 2610c1a3a6..fe0e806c1c 100644
--- a/src/browser/assets/file-icons/seti-icon-theme.json
+++ b/src/browser/assets/file-icons/seti-icon-theme.json
@@ -3,7 +3,7 @@
"This file has been generated from data in https://github.com/jesseweed/seti-ui",
"- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less",
"- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less",
- "- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less",
+ "- file associations: https://github.com/jesseweed/seti-ui/tree/master/styles/components/icons",
"If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.",
"Once accepted there, we are happy to receive an update request."
],
diff --git a/src/browser/components/AppLoader/AppLoader.auth.test.tsx b/src/browser/components/AppLoader/AppLoader.auth.test.tsx
deleted file mode 100644
index 6e3bcc3d8e..0000000000
--- a/src/browser/components/AppLoader/AppLoader.auth.test.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import "../../../../tests/ui/dom";
-
-import React from "react";
-import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
-import { cleanup, render } from "@testing-library/react";
-import { useTheme } from "../../contexts/ThemeContext";
-import { installDom } from "../../../../tests/ui/dom";
-
-let cleanupDom: (() => void) | null = null;
-
-let apiStatus: "auth_required" | "connecting" | "error" = "auth_required";
-let apiError: string | null = "Authentication required";
-
-// AppLoader imports App, which pulls in Lottie-based components. In happy-dom,
-// lottie-web's canvas bootstrap can throw during module evaluation.
-void mock.module("lottie-react", () => ({
- __esModule: true,
- default: () =>
+ ),
+}));
+
+void mock.module("@/browser/components/AuthTokenModal/AuthTokenModal", () => ({
+ // Note: Module mocks leak between bun test files.
+ // Export all commonly-used symbols to avoid cross-test import errors.
+ AuthTokenModal: (props: { error?: string | null }) => (
+
{props.error ?? "no-error"}
+ ),
+ getStoredAuthToken: () => null,
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ setStoredAuthToken: () => {},
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ clearStoredAuthToken: () => {},
+}));
+
+// eslint-disable-next-line no-restricted-syntax -- AppLoader imports react-dnd, which requires DOM globals during module evaluation.
+const { AppLoader } = await import("./AppLoader");
+
+describe("AppLoader", () => {
+ beforeEach(() => {
+ cleanupDom = installDom();
+ });
+
+ afterEach(() => {
+ cleanup();
+ cleanupDom?.();
+ cleanupDom = null;
+ });
+
+ test("renders AuthTokenModal when API status is auth_required (before workspaces load)", () => {
+ apiStatus = "auth_required";
+ apiError = "Authentication required";
+
+ const { getByTestId, queryByText } = render();
+
+ expect(queryByText("Loading Mux")).toBeNull();
+ expect(getByTestId("AuthTokenModalMock").textContent).toContain("Authentication required");
+ });
+
+ test("renders StartupConnectionError when API status is error (before workspaces load)", () => {
+ apiStatus = "error";
+ apiError = "Connection error";
+
+ const { getByTestId, queryByTestId } = render();
+
+ expect(queryByTestId("LoadingScreenMock")).toBeNull();
+ expect(queryByTestId("AuthTokenModalMock")).toBeNull();
+ expect(getByTestId("StartupConnectionErrorMock").textContent).toContain("Connection error");
+ });
+
+ test("wraps LoadingScreen in ThemeProvider", () => {
+ apiStatus = "connecting";
+ apiError = null;
+
+ const { getByTestId } = render();
+
+ // If ThemeProvider is missing, useTheme() will throw.
+ expect(getByTestId("LoadingScreenMock").textContent).toBeTruthy();
+ });
+});
diff --git a/src/browser/components/AppLoader/AppLoader.tsx b/src/browser/features/AppShell/AppLoader/AppLoader.tsx
similarity index 83%
rename from src/browser/components/AppLoader/AppLoader.tsx
rename to src/browser/features/AppShell/AppLoader/AppLoader.tsx
index 0e1517e56c..868eb57917 100644
--- a/src/browser/components/AppLoader/AppLoader.tsx
+++ b/src/browser/features/AppShell/AppLoader/AppLoader.tsx
@@ -1,23 +1,23 @@
import { useState, useEffect } from "react";
import { AnimatePresence, motion } from "motion/react";
-import App from "../../App";
-import { AuthTokenModal } from "../AuthTokenModal/AuthTokenModal";
-import { ThemeProvider } from "../../contexts/ThemeContext";
-import { LoadingScreen } from "../LoadingScreen/LoadingScreen";
-import { StartupConnectionError } from "../StartupConnectionError/StartupConnectionError";
-import { useReducedMotion } from "../../hooks/useReducedMotion";
-import { useWorkspaceStoreRaw, workspaceStore } from "../../stores/WorkspaceStore";
-import { useGitStatusStoreRaw } from "../../stores/GitStatusStore";
-import { useBackgroundBashStoreRaw } from "../../stores/BackgroundBashStore";
-import { getPRStatusStoreInstance } from "../../stores/PRStatusStore";
-import { ProjectProvider, useProjectContext } from "../../contexts/ProjectContext";
+import App from "@/browser/App";
+import { AuthTokenModal } from "@/browser/components/AuthTokenModal/AuthTokenModal";
+import { ThemeProvider } from "@/browser/contexts/ThemeContext";
+import { LoadingScreen } from "@/browser/components/LoadingScreen/LoadingScreen";
+import { StartupConnectionError } from "@/browser/components/StartupConnectionError/StartupConnectionError";
+import { useReducedMotion } from "@/browser/hooks/useReducedMotion";
+import { useWorkspaceStoreRaw, workspaceStore } from "@/browser/stores/WorkspaceStore";
+import { useGitStatusStoreRaw } from "@/browser/stores/GitStatusStore";
+import { useBackgroundBashStoreRaw } from "@/browser/stores/BackgroundBashStore";
+import { getPRStatusStoreInstance } from "@/browser/stores/PRStatusStore";
+import { ProjectProvider, useProjectContext } from "@/browser/contexts/ProjectContext";
import { PolicyProvider, usePolicy } from "@/browser/contexts/PolicyContext";
import { PolicyBlockedScreen } from "@/browser/components/PolicyBlockedScreen/PolicyBlockedScreen";
import { APIProvider, useAPI, type APIClient } from "@/browser/contexts/API";
-import { WorkspaceProvider, useWorkspaceContext } from "../../contexts/WorkspaceContext";
-import { RouterProvider } from "../../contexts/RouterContext";
-import { TelemetryEnabledProvider } from "../../contexts/TelemetryEnabledContext";
-import { TerminalRouterProvider } from "../../terminal/TerminalRouterContext";
+import { WorkspaceProvider, useWorkspaceContext } from "@/browser/contexts/WorkspaceContext";
+import { RouterProvider } from "@/browser/contexts/RouterContext";
+import { TelemetryEnabledProvider } from "@/browser/contexts/TelemetryEnabledContext";
+import { TerminalRouterProvider } from "@/browser/terminal/TerminalRouterContext";
interface AppLoaderProps {
/** Optional pre-created ORPC api?. If provided, skips internal connection setup. */
diff --git a/src/browser/components/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx b/src/browser/features/AppShell/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx
similarity index 97%
rename from src/browser/components/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx
rename to src/browser/features/AppShell/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx
index ede9c0dca4..75c71b5905 100644
--- a/src/browser/components/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx
+++ b/src/browser/features/AppShell/BackgroundProcessesBanner/BackgroundProcessesBanner.tsx
@@ -1,8 +1,8 @@
import React, { useState, useCallback, useEffect } from "react";
import { Terminal, X, ChevronDown, ChevronRight, Loader2, FileText } from "lucide-react";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
import { cn } from "@/common/lib/utils";
-import { BackgroundBashOutputDialog } from "../BackgroundBashOutputDialog/BackgroundBashOutputDialog";
+import { BackgroundBashOutputDialog } from "@/browser/components/BackgroundBashOutputDialog/BackgroundBashOutputDialog";
import { formatDuration } from "@/browser/features/Tools/Shared/toolUtils";
import {
useBackgroundBashTerminatingIds,
diff --git a/src/browser/components/ConnectionStatusToast/ConnectionStatusToast.tsx b/src/browser/features/AppShell/ConnectionStatusToast/ConnectionStatusToast.tsx
similarity index 100%
rename from src/browser/components/ConnectionStatusToast/ConnectionStatusToast.tsx
rename to src/browser/features/AppShell/ConnectionStatusToast/ConnectionStatusToast.tsx
diff --git a/src/browser/components/WindowsToolchainBanner/WindowsToolchainBanner.tsx b/src/browser/features/AppShell/WindowsToolchainBanner/WindowsToolchainBanner.tsx
similarity index 100%
rename from src/browser/components/WindowsToolchainBanner/WindowsToolchainBanner.tsx
rename to src/browser/features/AppShell/WindowsToolchainBanner/WindowsToolchainBanner.tsx
diff --git a/src/browser/components/AIView/AIView.tsx b/src/browser/features/Chat/AIView/AIView.tsx
similarity index 96%
rename from src/browser/components/AIView/AIView.tsx
rename to src/browser/features/Chat/AIView/AIView.tsx
index bbd2644d78..029dbe50fa 100644
--- a/src/browser/components/AIView/AIView.tsx
+++ b/src/browser/features/Chat/AIView/AIView.tsx
@@ -6,7 +6,7 @@ import { ThinkingProvider } from "@/browser/contexts/ThinkingContext";
import { WorkspaceModeAISync } from "@/browser/components/WorkspaceModeAISync/WorkspaceModeAISync";
import { AgentProvider } from "@/browser/contexts/AgentContext";
import { BackgroundBashProvider } from "@/browser/contexts/BackgroundBashContext";
-import { WorkspaceShell } from "../WorkspaceShell/WorkspaceShell";
+import { WorkspaceShell } from "@/browser/features/Workspace/WorkspaceShell/WorkspaceShell";
interface AIViewProps {
workspaceId: string;
diff --git a/src/browser/components/AgentModePicker/AgentModePicker.test.tsx b/src/browser/features/Chat/AgentModePicker/AgentModePicker.test.tsx
similarity index 99%
rename from src/browser/components/AgentModePicker/AgentModePicker.test.tsx
rename to src/browser/features/Chat/AgentModePicker/AgentModePicker.test.tsx
index 570ecf9b74..bafe400d0d 100644
--- a/src/browser/components/AgentModePicker/AgentModePicker.test.tsx
+++ b/src/browser/features/Chat/AgentModePicker/AgentModePicker.test.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { cleanup, fireEvent, render, waitFor } from "@testing-library/react";
-import { installDom } from "../../../../tests/ui/dom";
+import { installDom } from "../../../../../tests/ui/dom";
import { AgentProvider } from "@/browser/contexts/AgentContext";
import { TooltipProvider } from "@/browser/components/Tooltip/Tooltip";
diff --git a/src/browser/components/AgentModePicker/AgentModePicker.tsx b/src/browser/features/Chat/AgentModePicker/AgentModePicker.tsx
similarity index 100%
rename from src/browser/components/AgentModePicker/AgentModePicker.tsx
rename to src/browser/features/Chat/AgentModePicker/AgentModePicker.tsx
diff --git a/src/browser/components/ChatPane/ChatPane.tsx b/src/browser/features/Chat/ChatPane/ChatPane.tsx
similarity index 98%
rename from src/browser/components/ChatPane/ChatPane.tsx
rename to src/browser/features/Chat/ChatPane/ChatPane.tsx
index 8e4cd0438d..83035c6eec 100644
--- a/src/browser/components/ChatPane/ChatPane.tsx
+++ b/src/browser/features/Chat/ChatPane/ChatPane.tsx
@@ -14,7 +14,10 @@ import {
getTranscriptContextMenuText,
} from "@/browser/utils/messages/transcriptContextMenu";
import { useContextMenuPosition } from "@/browser/hooks/useContextMenuPosition";
-import { PositionedMenu, PositionedMenuItem } from "../PositionedMenu/PositionedMenu";
+import {
+ PositionedMenu,
+ PositionedMenuItem,
+} from "@/browser/components/PositionedMenu/PositionedMenu";
import { MessageListProvider } from "@/browser/features/Messages/MessageListContext";
import { cn } from "@/common/lib/utils";
import { MessageRenderer } from "@/browser/features/Messages/MessageRenderer";
@@ -24,7 +27,7 @@ import { InterruptedBarrier } from "@/browser/features/Messages/ChatBarrier/Inte
import { EditCutoffBarrier } from "@/browser/features/Messages/ChatBarrier/EditCutoffBarrier";
import { StreamingBarrier } from "@/browser/features/Messages/ChatBarrier/StreamingBarrier";
import { RetryBarrier } from "@/browser/features/Messages/ChatBarrier/RetryBarrier";
-import { PinnedTodoList } from "../PinnedTodoList/PinnedTodoList";
+import { PinnedTodoList } from "@/browser/features/Workspace/PinnedTodoList/PinnedTodoList";
import { VIM_ENABLED_KEY } from "@/common/constants/storage";
import { ChatInput, type ChatInputAPI } from "@/browser/features/ChatInput/index";
import type { QueueDispatchMode } from "@/browser/features/ChatInput/types";
@@ -50,28 +53,28 @@ import {
useWorkspaceStoreRaw,
type WorkspaceState,
} from "@/browser/stores/WorkspaceStore";
-import { WorkspaceMenuBar } from "../WorkspaceMenuBar/WorkspaceMenuBar";
+import { WorkspaceMenuBar } from "@/browser/features/Workspace/WorkspaceMenuBar/WorkspaceMenuBar";
import type { DisplayedMessage, QueuedMessage as QueuedMessageData } from "@/common/types/message";
import type { RuntimeConfig } from "@/common/types/runtime";
import { getRuntimeTypeForTelemetry } from "@/common/telemetry";
import { useAIViewKeybinds } from "@/browser/hooks/useAIViewKeybinds";
import { QueuedMessage } from "@/browser/features/Messages/QueuedMessage";
-import { CompactionWarning } from "../CompactionWarning/CompactionWarning";
-import { ContextSwitchWarning as ContextSwitchWarningBanner } from "../ContextSwitchWarning/ContextSwitchWarning";
-import { ConcurrentLocalWarning } from "../ConcurrentLocalWarning/ConcurrentLocalWarning";
-import { BackgroundProcessesBanner } from "../BackgroundProcessesBanner/BackgroundProcessesBanner";
+import { CompactionWarning } from "@/browser/components/CompactionWarning/CompactionWarning";
+import { ContextSwitchWarning as ContextSwitchWarningBanner } from "@/browser/components/ContextSwitchWarning/ContextSwitchWarning";
+import { ConcurrentLocalWarning } from "@/browser/features/Workspace/ConcurrentLocalWarning/ConcurrentLocalWarning";
+import { BackgroundProcessesBanner } from "@/browser/features/AppShell/BackgroundProcessesBanner/BackgroundProcessesBanner";
import { checkAutoCompaction } from "@/common/utils/compaction/autoCompactionCheck";
import { cancelCompaction } from "@/browser/utils/compaction/handler";
import type { ContextSwitchWarning } from "@/browser/utils/compaction/contextSwitchCheck";
import { useProviderOptions } from "@/browser/hooks/useProviderOptions";
-import { useAutoCompactionSettings } from "../../hooks/useAutoCompactionSettings";
+import { useAutoCompactionSettings } from "@/browser/hooks/useAutoCompactionSettings";
import { useContextSwitchWarning } from "@/browser/hooks/useContextSwitchWarning";
import { useProvidersConfig } from "@/browser/hooks/useProvidersConfig";
import { useSendMessageOptions } from "@/browser/hooks/useSendMessageOptions";
import type { TerminalSessionCreateOptions } from "@/browser/utils/terminal";
import { useAPI } from "@/browser/contexts/API";
import { useReviews } from "@/browser/hooks/useReviews";
-import { ReviewsBanner } from "../ReviewsBanner/ReviewsBanner";
+import { ReviewsBanner } from "@/browser/components/ReviewsBanner/ReviewsBanner";
import type { ReviewNoteData } from "@/common/types/review";
import { useWorkspaceContext } from "@/browser/contexts/WorkspaceContext";
import {
diff --git a/src/browser/features/ChatInput/CreationCenterContent.tsx b/src/browser/features/ChatInput/CreationCenterContent.tsx
index a87d352dd3..744079ebbe 100644
--- a/src/browser/features/ChatInput/CreationCenterContent.tsx
+++ b/src/browser/features/ChatInput/CreationCenterContent.tsx
@@ -1,5 +1,5 @@
import { useTheme } from "@/browser/contexts/ThemeContext";
-import { Shimmer } from "@/browser/features/AIElements/Shimmer";
+import { Shimmer } from "@/browser/components/Shimmer/Shimmer";
import { LoadingAnimation } from "@/browser/components/LoadingAnimation/LoadingAnimation";
interface CreationCenterContentProps {
diff --git a/src/browser/features/ChatInput/CreationControls.tsx b/src/browser/features/ChatInput/CreationControls.tsx
index bc8062fb27..4e1dd24643 100644
--- a/src/browser/features/ChatInput/CreationControls.tsx
+++ b/src/browser/features/ChatInput/CreationControls.tsx
@@ -48,7 +48,7 @@ import {
resolveCoderAvailability,
type CoderAvailabilityState,
type CoderControlsProps,
-} from "../Runtime/CoderControls";
+} from "@/browser/components/CoderControls/CoderControls";
/**
* Shared styling for inline form controls in the creation UI.
diff --git a/src/browser/features/ChatInput/index.tsx b/src/browser/features/ChatInput/index.tsx
index 5457909dd6..03ce3943a6 100644
--- a/src/browser/features/ChatInput/index.tsx
+++ b/src/browser/features/ChatInput/index.tsx
@@ -5,7 +5,7 @@ import {
FILE_SUGGESTION_KEYS,
} from "@/browser/features/ChatInput/CommandSuggestions";
import type { Toast } from "@/browser/features/ChatInput/ChatInputToast";
-import { ConnectionStatusToast } from "@/browser/components/ConnectionStatusToast/ConnectionStatusToast";
+import { ConnectionStatusToast } from "@/browser/features/AppShell/ConnectionStatusToast/ConnectionStatusToast";
import { ChatInputToast } from "@/browser/features/ChatInput/ChatInputToast";
import type { SendMessageError } from "@/common/types/errors";
import { createErrorToast } from "@/browser/features/ChatInput/ChatInputToasts";
@@ -65,7 +65,7 @@ import {
type SlashSuggestion,
} from "@/browser/utils/slashCommands/suggestions";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
-import { AgentModePicker } from "@/browser/components/AgentModePicker/AgentModePicker";
+import { AgentModePicker } from "@/browser/features/Chat/AgentModePicker/AgentModePicker";
import { ContextUsageIndicatorButton } from "@/browser/components/ContextUsageIndicatorButton/ContextUsageIndicatorButton";
import { useWorkspaceUsage } from "@/browser/stores/WorkspaceStore";
import { useProviderOptions } from "@/browser/hooks/useProviderOptions";
diff --git a/src/browser/components/CommandPalette/CommandPalette.test.ts b/src/browser/features/CommandPalette/CommandPalette/CommandPalette.test.ts
similarity index 100%
rename from src/browser/components/CommandPalette/CommandPalette.test.ts
rename to src/browser/features/CommandPalette/CommandPalette/CommandPalette.test.ts
diff --git a/src/browser/components/CommandPalette/CommandPalette.tsx b/src/browser/features/CommandPalette/CommandPalette/CommandPalette.tsx
similarity index 100%
rename from src/browser/components/CommandPalette/CommandPalette.tsx
rename to src/browser/features/CommandPalette/CommandPalette/CommandPalette.tsx
diff --git a/src/browser/features/Messages/CodeBlockSSR.tsx b/src/browser/features/Messages/CodeBlockSSR.tsx
index 45e74a626a..964ea47734 100644
--- a/src/browser/features/Messages/CodeBlockSSR.tsx
+++ b/src/browser/features/Messages/CodeBlockSSR.tsx
@@ -5,7 +5,7 @@
*/
import React from "react";
-import { CopyIcon } from "@/browser/components/icons/CopyIcon/CopyIcon";
+import { CopyIcon } from "@/browser/components/Icons/CopyIcon/CopyIcon";
interface CodeBlockSSRProps {
code: string;
diff --git a/src/browser/features/Messages/InitMessage.tsx b/src/browser/features/Messages/InitMessage.tsx
index 54e7adbd3c..db7c53e5c0 100644
--- a/src/browser/features/Messages/InitMessage.tsx
+++ b/src/browser/features/Messages/InitMessage.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useRef } from "react";
import { cn } from "@/common/lib/utils";
import type { DisplayedMessage } from "@/common/types/message";
import { Loader2, Wrench, CheckCircle2, AlertCircle } from "lucide-react";
-import { Shimmer } from "../AIElements/Shimmer";
+import { Shimmer } from "@/browser/components/Shimmer/Shimmer";
import { formatDuration } from "@/common/utils/formatDuration";
interface InitMessageProps {
diff --git a/src/browser/features/Messages/ReasoningMessage.tsx b/src/browser/features/Messages/ReasoningMessage.tsx
index fb38d1603e..be067e0f51 100644
--- a/src/browser/features/Messages/ReasoningMessage.tsx
+++ b/src/browser/features/Messages/ReasoningMessage.tsx
@@ -4,7 +4,7 @@ import { MarkdownRenderer } from "./MarkdownRenderer";
import { TypewriterMarkdown } from "./TypewriterMarkdown";
import { normalizeReasoningMarkdown } from "./MarkdownStyles";
import { cn } from "@/common/lib/utils";
-import { Shimmer } from "../AIElements/Shimmer";
+import { Shimmer } from "@/browser/components/Shimmer/Shimmer";
import { Lightbulb } from "lucide-react";
interface ReasoningMessageProps {
diff --git a/src/browser/components/BranchSelector/BranchSelector.tsx b/src/browser/features/Project/BranchSelector/BranchSelector.tsx
similarity index 99%
rename from src/browser/components/BranchSelector/BranchSelector.tsx
rename to src/browser/features/Project/BranchSelector/BranchSelector.tsx
index db00d87097..5d3ad1772f 100644
--- a/src/browser/components/BranchSelector/BranchSelector.tsx
+++ b/src/browser/features/Project/BranchSelector/BranchSelector.tsx
@@ -2,8 +2,8 @@ import React, { useState, useCallback, useEffect, useRef } from "react";
import { GitBranch, Loader2, Check, Copy, Globe, ChevronRight } from "lucide-react";
import { cn } from "@/common/lib/utils";
import { useAPI } from "@/browser/contexts/API";
-import { Popover, PopoverContent, PopoverTrigger } from "../Popover/Popover";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
+import { Popover, PopoverContent, PopoverTrigger } from "@/browser/components/Popover/Popover";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
import { useCopyToClipboard } from "@/browser/hooks/useCopyToClipboard";
import { invalidateGitStatus, useGitStatus } from "@/browser/stores/GitStatusStore";
import { createLRUCache } from "@/browser/utils/lruCache";
diff --git a/src/browser/components/DirectoryPickerModal/DirectoryPickerModal.tsx b/src/browser/features/Project/DirectoryPickerModal/DirectoryPickerModal.tsx
similarity index 98%
rename from src/browser/components/DirectoryPickerModal/DirectoryPickerModal.tsx
rename to src/browser/features/Project/DirectoryPickerModal/DirectoryPickerModal.tsx
index f316838310..8571f26b0c 100644
--- a/src/browser/components/DirectoryPickerModal/DirectoryPickerModal.tsx
+++ b/src/browser/features/Project/DirectoryPickerModal/DirectoryPickerModal.tsx
@@ -8,10 +8,10 @@ import {
DialogFooter,
} from "@/browser/components/Dialog/Dialog";
import { Button } from "@/browser/components/Button/Button";
-import { Checkbox } from "../Checkbox/Checkbox";
+import { Checkbox } from "@/browser/components/Checkbox/Checkbox";
import { Input } from "@/browser/components/Input/Input";
import type { FileTreeNode } from "@/common/utils/git/numstatParser";
-import { DirectoryTree } from "../DirectoryTree/DirectoryTree";
+import { DirectoryTree } from "@/browser/components/DirectoryTree/DirectoryTree";
import { useAPI } from "@/browser/contexts/API";
import { formatKeybind, isMac } from "@/browser/utils/ui/keybinds";
import { getErrorMessage } from "@/common/utils/errors";
diff --git a/src/browser/components/GitInitBanner/GitInitBanner.tsx b/src/browser/features/Project/GitInitBanner/GitInitBanner.tsx
similarity index 100%
rename from src/browser/components/GitInitBanner/GitInitBanner.tsx
rename to src/browser/features/Project/GitInitBanner/GitInitBanner.tsx
diff --git a/src/browser/components/GitStatusIndicator/GitStatusIndicator.tsx b/src/browser/features/Project/GitStatus/GitStatusIndicator/GitStatusIndicator.tsx
similarity index 97%
rename from src/browser/components/GitStatusIndicator/GitStatusIndicator.tsx
rename to src/browser/features/Project/GitStatus/GitStatusIndicator/GitStatusIndicator.tsx
index 00192bf965..798c8ad0ec 100644
--- a/src/browser/components/GitStatusIndicator/GitStatusIndicator.tsx
+++ b/src/browser/features/Project/GitStatus/GitStatusIndicator/GitStatusIndicator.tsx
@@ -8,7 +8,7 @@ import {
GitStatusIndicatorView,
type GitStatusIndicatorMode,
} from "../GitStatusIndicatorView/GitStatusIndicatorView";
-import { useGitBranchDetails } from "@/browser/features/Hooks/useGitBranchDetails";
+import { useGitBranchDetails } from "@/browser/hooks/useGitBranchDetails";
interface GitStatusIndicatorProps {
gitStatus: GitStatus | null;
diff --git a/src/browser/components/GitStatusIndicatorView/GitStatusIndicatorView.tsx b/src/browser/features/Project/GitStatus/GitStatusIndicatorView/GitStatusIndicatorView.tsx
similarity index 98%
rename from src/browser/components/GitStatusIndicatorView/GitStatusIndicatorView.tsx
rename to src/browser/features/Project/GitStatus/GitStatusIndicatorView/GitStatusIndicatorView.tsx
index 40688ef480..3aab0b0565 100644
--- a/src/browser/components/GitStatusIndicatorView/GitStatusIndicatorView.tsx
+++ b/src/browser/features/Project/GitStatus/GitStatusIndicatorView/GitStatusIndicatorView.tsx
@@ -3,8 +3,16 @@ import type { GitStatus } from "@/common/types/workspace";
import type { GitCommit, GitBranchHeader } from "@/common/utils/git/parseGitLog";
import { cn } from "@/common/lib/utils";
import { stopKeyboardPropagation } from "@/browser/utils/events";
-import { ToggleGroup, ToggleGroupItem } from "../ToggleGroupPrimitive/ToggleGroupPrimitive";
-import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../Dialog/Dialog";
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/browser/components/ToggleGroupPrimitive/ToggleGroupPrimitive";
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+} from "@/browser/components/Dialog/Dialog";
import { BaseSelectorPopover } from "@/browser/features/RightSidebar/CodeReview/BaseSelectorPopover";
// Helper for indicator colors
diff --git a/src/browser/components/ProjectSidebar/ProjectSidebar.tsx b/src/browser/features/Project/ProjectSidebar/ProjectSidebar.tsx
similarity index 98%
rename from src/browser/components/ProjectSidebar/ProjectSidebar.tsx
rename to src/browser/features/Project/ProjectSidebar/ProjectSidebar.tsx
index da5a89467a..5bb1c4b742 100644
--- a/src/browser/components/ProjectSidebar/ProjectSidebar.tsx
+++ b/src/browser/features/Project/ProjectSidebar/ProjectSidebar.tsx
@@ -51,13 +51,16 @@ import {
getSectionTierKey,
sortSectionsByLinkedList,
} from "@/browser/utils/ui/workspaceFiltering";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
-import { SidebarCollapseButton } from "../SidebarCollapseButton/SidebarCollapseButton";
-import { ConfirmationModal } from "../ConfirmationModal/ConfirmationModal";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
+import { SidebarCollapseButton } from "@/browser/components/SidebarCollapseButton/SidebarCollapseButton";
+import { ConfirmationModal } from "@/browser/components/ConfirmationModal/ConfirmationModal";
import { useSettings } from "@/browser/contexts/SettingsContext";
-import { WorkspaceListItem, type WorkspaceSelection } from "../WorkspaceListItem/WorkspaceListItem";
-import { WorkspaceStatusIndicator } from "../WorkspaceStatusIndicator/WorkspaceStatusIndicator";
+import {
+ WorkspaceListItem,
+ type WorkspaceSelection,
+} from "@/browser/features/Workspace/WorkspaceListItem/WorkspaceListItem";
+import { WorkspaceStatusIndicator } from "@/browser/features/Workspace/WorkspaceStatusIndicator/WorkspaceStatusIndicator";
import { TitleEditProvider, useTitleEdit } from "@/browser/contexts/WorkspaceTitleEditContext";
import { useConfirmDialog } from "@/browser/contexts/ConfirmDialogContext";
import { useProjectContext } from "@/browser/contexts/ProjectContext";
@@ -67,19 +70,19 @@ import { useWorkspaceActions } from "@/browser/contexts/WorkspaceContext";
import { useRouter } from "@/browser/contexts/RouterContext";
import { usePopoverError } from "@/browser/hooks/usePopoverError";
import { forkWorkspace } from "@/browser/utils/chatCommands";
-import { PopoverError } from "../PopoverError/PopoverError";
-import { SectionHeader } from "../SectionHeader/SectionHeader";
-import { AddSectionButton } from "../AddSectionButton/AddSectionButton";
-import { WorkspaceSectionDropZone } from "../WorkspaceSectionDropZone/WorkspaceSectionDropZone";
-import { WorkspaceDragLayer } from "../WorkspaceDragLayer/WorkspaceDragLayer";
-import { SectionDragLayer } from "../SectionDragLayer/SectionDragLayer";
-import { DraggableSection } from "../DraggableSection/DraggableSection";
+import { PopoverError } from "@/browser/components/PopoverError/PopoverError";
+import { SectionHeader } from "@/browser/components/SectionHeader/SectionHeader";
+import { AddSectionButton } from "@/browser/components/AddSectionButton/AddSectionButton";
+import { WorkspaceSectionDropZone } from "@/browser/components/WorkspaceSectionDropZone/WorkspaceSectionDropZone";
+import { WorkspaceDragLayer } from "@/browser/components/WorkspaceDragLayer/WorkspaceDragLayer";
+import { SectionDragLayer } from "@/browser/components/SectionDragLayer/SectionDragLayer";
+import { DraggableSection } from "@/browser/components/DraggableSection/DraggableSection";
import type { SectionConfig } from "@/common/types/project";
import { getErrorMessage } from "@/common/utils/errors";
import { getProjectWorkspaceCounts } from "@/common/utils/projectRemoval";
// Re-export WorkspaceSelection for backwards compatibility
-export type { WorkspaceSelection } from "../WorkspaceListItem/WorkspaceListItem";
+export type { WorkspaceSelection } from "@/browser/features/Workspace/WorkspaceListItem/WorkspaceListItem";
// Draggable project item moved to module scope to avoid remounting on every parent render.
// Defining components inside another component causes a new function identity each render,
diff --git a/src/browser/features/Settings/Sections/RuntimesSection.tsx b/src/browser/features/Settings/Sections/RuntimesSection.tsx
index 931f117e0a..cd85b2ecd1 100644
--- a/src/browser/features/Settings/Sections/RuntimesSection.tsx
+++ b/src/browser/features/Settings/Sections/RuntimesSection.tsx
@@ -4,7 +4,7 @@ import { AlertTriangle, Loader2 } from "lucide-react";
import {
CoderWorkspaceForm,
resolveCoderAvailability,
-} from "@/browser/features/Runtime/CoderControls";
+} from "@/browser/components/CoderControls/CoderControls";
import { RuntimeConfigInput } from "@/browser/components/RuntimeConfigInput/RuntimeConfigInput";
import {
Select,
diff --git a/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx b/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
index ff4c3c29cd..82875b5c07 100644
--- a/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
+++ b/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
@@ -18,7 +18,7 @@ import {
LocalIcon,
SSHIcon,
WorktreeIcon,
-} from "@/browser/components/icons/RuntimeIcons/RuntimeIcons";
+} from "@/browser/components/Icons/RuntimeIcons/RuntimeIcons";
import {
ProjectAddForm,
type ProjectAddFormHandle,
diff --git a/src/browser/features/Tools/Shared/ToolPrimitives.tsx b/src/browser/features/Tools/Shared/ToolPrimitives.tsx
index 1ebcf4eef1..d6013124be 100644
--- a/src/browser/features/Tools/Shared/ToolPrimitives.tsx
+++ b/src/browser/features/Tools/Shared/ToolPrimitives.tsx
@@ -16,7 +16,7 @@ import {
Square,
Wrench,
} from "lucide-react";
-import { EmojiIcon } from "@/browser/components/icons/EmojiIcon/EmojiIcon";
+import { EmojiIcon } from "@/browser/components/Icons/EmojiIcon/EmojiIcon";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
/**
diff --git a/src/browser/features/Tools/SwitchAgentToolCall.tsx b/src/browser/features/Tools/SwitchAgentToolCall.tsx
index 758f4a0574..1e93f38fe0 100644
--- a/src/browser/features/Tools/SwitchAgentToolCall.tsx
+++ b/src/browser/features/Tools/SwitchAgentToolCall.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { formatAgentIdLabel } from "@/browser/components/AgentModePicker/AgentModePicker";
+import { formatAgentIdLabel } from "@/browser/features/Chat/AgentModePicker/AgentModePicker";
import {
ToolContainer,
ToolHeader,
diff --git a/src/browser/components/ArchivedWorkspaces/ArchivedWorkspaces.tsx b/src/browser/features/Workspace/ArchivedWorkspaces/ArchivedWorkspaces.tsx
similarity index 98%
rename from src/browser/components/ArchivedWorkspaces/ArchivedWorkspaces.tsx
rename to src/browser/features/Workspace/ArchivedWorkspaces/ArchivedWorkspaces.tsx
index 976ae62f13..c89725415a 100644
--- a/src/browser/components/ArchivedWorkspaces/ArchivedWorkspaces.tsx
+++ b/src/browser/features/Workspace/ArchivedWorkspaces/ArchivedWorkspaces.tsx
@@ -7,10 +7,13 @@ import { usePersistedState } from "@/browser/hooks/usePersistedState";
import { getArchivedWorkspacesExpandedKey } from "@/common/constants/storage";
import { useAPI } from "@/browser/contexts/API";
import { ChevronDown, ChevronRight, Loader2, Search, Trash2 } from "lucide-react";
-import { ArchiveIcon, ArchiveRestoreIcon } from "../icons/ArchiveIcon/ArchiveIcon";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
-import { RuntimeBadge } from "../RuntimeBadge/RuntimeBadge";
-import { Skeleton } from "../Skeleton/Skeleton";
+import {
+ ArchiveIcon,
+ ArchiveRestoreIcon,
+} from "@/browser/components/Icons/ArchiveIcon/ArchiveIcon";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
+import { RuntimeBadge } from "@/browser/components/RuntimeBadge/RuntimeBadge";
+import { Skeleton } from "@/browser/components/Skeleton/Skeleton";
import {
Dialog,
DialogContent,
@@ -19,7 +22,7 @@ import {
DialogDescription,
DialogFooter,
} from "@/browser/components/Dialog/Dialog";
-import { ForceDeleteModal } from "../ForceDeleteModal/ForceDeleteModal";
+import { ForceDeleteModal } from "@/browser/components/ForceDeleteModal/ForceDeleteModal";
import { Button } from "@/browser/components/Button/Button";
import type { z } from "zod";
import type { SessionUsageFileSchema } from "@/common/orpc/schemas/chatStats";
diff --git a/src/browser/components/ConcurrentLocalWarning/ConcurrentLocalWarning.tsx b/src/browser/features/Workspace/ConcurrentLocalWarning/ConcurrentLocalWarning.tsx
similarity index 100%
rename from src/browser/components/ConcurrentLocalWarning/ConcurrentLocalWarning.tsx
rename to src/browser/features/Workspace/ConcurrentLocalWarning/ConcurrentLocalWarning.tsx
diff --git a/src/browser/components/PinnedTodoList/PinnedTodoList.tsx b/src/browser/features/Workspace/PinnedTodoList/PinnedTodoList.tsx
similarity index 96%
rename from src/browser/components/PinnedTodoList/PinnedTodoList.tsx
rename to src/browser/features/Workspace/PinnedTodoList/PinnedTodoList.tsx
index ff8e387d8c..fca41c1d4b 100644
--- a/src/browser/components/PinnedTodoList/PinnedTodoList.tsx
+++ b/src/browser/features/Workspace/PinnedTodoList/PinnedTodoList.tsx
@@ -1,5 +1,5 @@
import React, { useSyncExternalStore } from "react";
-import { TodoList } from "../TodoList/TodoList";
+import { TodoList } from "@/browser/components/TodoList/TodoList";
import { useWorkspaceStoreRaw } from "@/browser/stores/WorkspaceStore";
import { usePersistedState } from "@/browser/hooks/usePersistedState";
import { cn } from "@/common/lib/utils";
diff --git a/src/browser/components/WorkspaceLinks/WorkspaceLinks.tsx b/src/browser/features/Workspace/WorkspaceLinks/WorkspaceLinks.tsx
similarity index 86%
rename from src/browser/components/WorkspaceLinks/WorkspaceLinks.tsx
rename to src/browser/features/Workspace/WorkspaceLinks/WorkspaceLinks.tsx
index 8700f6dcb7..2b5cffafa0 100644
--- a/src/browser/components/WorkspaceLinks/WorkspaceLinks.tsx
+++ b/src/browser/features/Workspace/WorkspaceLinks/WorkspaceLinks.tsx
@@ -4,7 +4,7 @@
*/
import { useWorkspacePR } from "@/browser/stores/PRStatusStore";
-import { PRLinkBadge } from "../PRLinkBadge/PRLinkBadge";
+import { PRLinkBadge } from "@/browser/components/PRLinkBadge/PRLinkBadge";
interface WorkspaceLinksProps {
workspaceId: string;
diff --git a/src/browser/components/WorkspaceListItem/WorkspaceListItem.tsx b/src/browser/features/Workspace/WorkspaceListItem/WorkspaceListItem.tsx
similarity index 96%
rename from src/browser/components/WorkspaceListItem/WorkspaceListItem.tsx
rename to src/browser/features/Workspace/WorkspaceListItem/WorkspaceListItem.tsx
index b50036ffb5..f336f3cd62 100644
--- a/src/browser/components/WorkspaceListItem/WorkspaceListItem.tsx
+++ b/src/browser/features/Workspace/WorkspaceListItem/WorkspaceListItem.tsx
@@ -10,25 +10,33 @@ import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from "react";
import { useDrag } from "react-dnd";
import { getEmptyImage } from "react-dnd-html5-backend";
-import { GitStatusIndicator } from "../GitStatusIndicator/GitStatusIndicator";
+import { GitStatusIndicator } from "@/browser/features/Project/GitStatus/GitStatusIndicator/GitStatusIndicator";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
-import { Popover, PopoverContent, PopoverTrigger, PopoverAnchor } from "../Popover/Popover";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+ PopoverAnchor,
+} from "@/browser/components/Popover/Popover";
import { useContextMenuPosition } from "@/browser/hooks/useContextMenuPosition";
-import { PositionedMenu, PositionedMenuItem } from "../PositionedMenu/PositionedMenu";
+import {
+ PositionedMenu,
+ PositionedMenuItem,
+} from "@/browser/components/PositionedMenu/PositionedMenu";
import { Trash2, Ellipsis, Loader2, Sparkles } from "lucide-react";
import { WorkspaceStatusIndicator } from "../WorkspaceStatusIndicator/WorkspaceStatusIndicator";
-import { Shimmer } from "@/browser/features/AIElements/Shimmer";
-import { ArchiveIcon } from "../icons/ArchiveIcon/ArchiveIcon";
-import { WorkspaceTerminalIcon } from "../icons/WorkspaceTerminalIcon/WorkspaceTerminalIcon";
+import { Shimmer } from "@/browser/components/Shimmer/Shimmer";
+import { ArchiveIcon } from "@/browser/components/Icons/ArchiveIcon/ArchiveIcon";
+import { WorkspaceTerminalIcon } from "@/browser/components/Icons/WorkspaceTerminalIcon/WorkspaceTerminalIcon";
import {
WORKSPACE_DRAG_TYPE,
type WorkspaceDragItem,
-} from "../WorkspaceSectionDropZone/WorkspaceSectionDropZone";
+} from "@/browser/components/WorkspaceSectionDropZone/WorkspaceSectionDropZone";
import { useLinkSharingEnabled } from "@/browser/contexts/TelemetryEnabledContext";
import { formatKeybind, KEYBINDS } from "@/browser/utils/ui/keybinds";
-import { ShareTranscriptDialog } from "../ShareTranscriptDialog/ShareTranscriptDialog";
-import { WorkspaceActionsMenuContent } from "../WorkspaceActionsMenuContent/WorkspaceActionsMenuContent";
+import { ShareTranscriptDialog } from "@/browser/components/ShareTranscriptDialog/ShareTranscriptDialog";
+import { WorkspaceActionsMenuContent } from "@/browser/components/WorkspaceActionsMenuContent/WorkspaceActionsMenuContent";
import { useAPI } from "@/browser/contexts/API";
export interface WorkspaceSelection {
diff --git a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx b/src/browser/features/Workspace/WorkspaceMenuBar/WorkspaceMenuBar.tsx
similarity index 94%
rename from src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx
rename to src/browser/features/Workspace/WorkspaceMenuBar/WorkspaceMenuBar.tsx
index e83bd81b93..6357edd6a9 100644
--- a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx
+++ b/src/browser/features/Workspace/WorkspaceMenuBar/WorkspaceMenuBar.tsx
@@ -10,13 +10,13 @@ import {
getNotifyOnResponseKey,
getNotifyOnResponseAutoEnableKey,
} from "@/common/constants/storage";
-import { GitStatusIndicator } from "../GitStatusIndicator/GitStatusIndicator";
-import { RuntimeBadge } from "../RuntimeBadge/RuntimeBadge";
-import { BranchSelector } from "../BranchSelector/BranchSelector";
-import { WorkspaceMCPModal } from "../WorkspaceMCPModal/WorkspaceMCPModal";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
-import { Popover, PopoverTrigger, PopoverContent } from "../Popover/Popover";
-import { Checkbox } from "../Checkbox/Checkbox";
+import { GitStatusIndicator } from "@/browser/features/Project/GitStatus/GitStatusIndicator/GitStatusIndicator";
+import { RuntimeBadge } from "@/browser/components/RuntimeBadge/RuntimeBadge";
+import { BranchSelector } from "@/browser/features/Project/BranchSelector/BranchSelector";
+import { WorkspaceMCPModal } from "@/browser/components/WorkspaceMCPModal/WorkspaceMCPModal";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
+import { Popover, PopoverTrigger, PopoverContent } from "@/browser/components/Popover/Popover";
+import { Checkbox } from "@/browser/components/Checkbox/Checkbox";
import { formatKeybind, KEYBINDS, matchesKeybind } from "@/browser/utils/ui/keybinds";
import { useGitStatus } from "@/browser/stores/GitStatusStore";
import { useWorkspaceSidebarState } from "@/browser/stores/WorkspaceStore";
@@ -31,15 +31,15 @@ import { useOpenInEditor } from "@/browser/hooks/useOpenInEditor";
import { usePersistedState } from "@/browser/hooks/usePersistedState";
import { usePopoverError } from "@/browser/hooks/usePopoverError";
import { isDesktopMode, DESKTOP_TITLEBAR_HEIGHT_CLASS } from "@/browser/hooks/useDesktopTitlebar";
-import { DebugLlmRequestModal } from "../DebugLlmRequestModal/DebugLlmRequestModal";
+import { DebugLlmRequestModal } from "@/browser/components/DebugLlmRequestModal/DebugLlmRequestModal";
import { WorkspaceLinks } from "../WorkspaceLinks/WorkspaceLinks";
-import { ShareTranscriptDialog } from "../ShareTranscriptDialog/ShareTranscriptDialog";
-import { ConfirmationModal } from "../ConfirmationModal/ConfirmationModal";
-import { PopoverError } from "../PopoverError/PopoverError";
-import { WorkspaceActionsMenuContent } from "../WorkspaceActionsMenuContent/WorkspaceActionsMenuContent";
-import { WorkspaceTerminalIcon } from "../icons/WorkspaceTerminalIcon/WorkspaceTerminalIcon";
+import { ShareTranscriptDialog } from "@/browser/components/ShareTranscriptDialog/ShareTranscriptDialog";
+import { ConfirmationModal } from "@/browser/components/ConfirmationModal/ConfirmationModal";
+import { PopoverError } from "@/browser/components/PopoverError/PopoverError";
+import { WorkspaceActionsMenuContent } from "@/browser/components/WorkspaceActionsMenuContent/WorkspaceActionsMenuContent";
+import { WorkspaceTerminalIcon } from "@/browser/components/Icons/WorkspaceTerminalIcon/WorkspaceTerminalIcon";
-import { SkillIndicator } from "../SkillIndicator/SkillIndicator";
+import { SkillIndicator } from "@/browser/components/SkillIndicator/SkillIndicator";
import { useAPI } from "@/browser/contexts/API";
import { useAgent } from "@/browser/contexts/AgentContext";
diff --git a/src/browser/components/WorkspaceShell/WorkspaceShell.tsx b/src/browser/features/Workspace/WorkspaceShell/WorkspaceShell.tsx
similarity index 95%
rename from src/browser/components/WorkspaceShell/WorkspaceShell.tsx
rename to src/browser/features/Workspace/WorkspaceShell/WorkspaceShell.tsx
index 997f09ed2b..1a60b0f20f 100644
--- a/src/browser/components/WorkspaceShell/WorkspaceShell.tsx
+++ b/src/browser/features/Workspace/WorkspaceShell/WorkspaceShell.tsx
@@ -1,21 +1,21 @@
import type { TerminalSessionCreateOptions } from "@/browser/utils/terminal";
import React, { useCallback, useRef } from "react";
import { cn } from "@/common/lib/utils";
-import { LoadingAnimation } from "../LoadingAnimation/LoadingAnimation";
+import { LoadingAnimation } from "@/browser/components/LoadingAnimation/LoadingAnimation";
import { RIGHT_SIDEBAR_WIDTH_KEY, getReviewImmersiveKey } from "@/common/constants/storage";
import { useResizableSidebar } from "@/browser/hooks/useResizableSidebar";
import { useResizeObserver } from "@/browser/hooks/useResizeObserver";
import { useOpenTerminal } from "@/browser/hooks/useOpenTerminal";
import { usePersistedState } from "@/browser/hooks/usePersistedState";
import { RightSidebar } from "@/browser/features/RightSidebar/RightSidebar";
-import { PopoverError } from "../PopoverError/PopoverError";
+import { PopoverError } from "@/browser/components/PopoverError/PopoverError";
import type { RuntimeConfig } from "@/common/types/runtime";
import { useBackgroundBashError } from "@/browser/contexts/BackgroundBashContext";
import { useWorkspaceState } from "@/browser/stores/WorkspaceStore";
import { useReviews } from "@/browser/hooks/useReviews";
import type { ReviewNoteData } from "@/common/types/review";
-import { ConnectionStatusToast } from "../ConnectionStatusToast/ConnectionStatusToast";
-import { ChatPane } from "../ChatPane/ChatPane";
+import { ConnectionStatusToast } from "@/browser/features/AppShell/ConnectionStatusToast/ConnectionStatusToast";
+import { ChatPane } from "@/browser/features/Chat/ChatPane/ChatPane";
// ChatPane uses tailwind `min-w-96`.
const CHAT_PANE_MIN_WIDTH_PX = 384;
diff --git a/src/browser/components/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx b/src/browser/features/Workspace/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx
similarity index 95%
rename from src/browser/components/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx
rename to src/browser/features/Workspace/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx
index 1497714eba..dcabce3801 100644
--- a/src/browser/components/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx
+++ b/src/browser/features/Workspace/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx
@@ -1,9 +1,9 @@
import { useWorkspaceSidebarState } from "@/browser/stores/WorkspaceStore";
import { ModelDisplay } from "@/browser/features/Messages/ModelDisplay";
-import { EmojiIcon } from "@/browser/components/icons/EmojiIcon/EmojiIcon";
+import { EmojiIcon } from "@/browser/components/Icons/EmojiIcon/EmojiIcon";
import { CircleHelp, ExternalLinkIcon, Loader2 } from "lucide-react";
import { memo } from "react";
-import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
+import { Tooltip, TooltipTrigger, TooltipContent } from "@/browser/components/Tooltip/Tooltip";
export const WorkspaceStatusIndicator = memo<{
workspaceId: string;
diff --git a/src/browser/features/Hooks/useGitBranchDetails.ts b/src/browser/hooks/useGitBranchDetails.ts
similarity index 100%
rename from src/browser/features/Hooks/useGitBranchDetails.ts
rename to src/browser/hooks/useGitBranchDetails.ts
diff --git a/src/browser/hooks/useUnreadTracking.ts b/src/browser/hooks/useUnreadTracking.ts
index 72e57b5e51..c384844f00 100644
--- a/src/browser/hooks/useUnreadTracking.ts
+++ b/src/browser/hooks/useUnreadTracking.ts
@@ -1,5 +1,5 @@
import { useEffect, useCallback, useRef } from "react";
-import type { WorkspaceSelection } from "@/browser/components/ProjectSidebar/ProjectSidebar";
+import type { WorkspaceSelection } from "@/browser/features/Project/ProjectSidebar/ProjectSidebar";
import { getWorkspaceLastReadKey } from "@/common/constants/storage";
import { readPersistedState, updatePersistedState } from "./usePersistedState";
diff --git a/src/browser/main.tsx b/src/browser/main.tsx
index 287df55e93..96377a2b7f 100644
--- a/src/browser/main.tsx
+++ b/src/browser/main.tsx
@@ -1,7 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { installBrowserLogCapture } from "@/browser/utils/browserLog";
-import { AppLoader } from "@/browser/components/AppLoader/AppLoader";
+import { AppLoader } from "@/browser/features/AppShell/AppLoader/AppLoader";
import { initTelemetry, trackAppStarted } from "@/common/telemetry";
import { initTitlebarInsets } from "@/browser/hooks/useDesktopTitlebar";
diff --git a/src/browser/stories/App.readmeScreenshots.stories.tsx b/src/browser/stories/App.readmeScreenshots.stories.tsx
index a93a7e5bc0..860b6ff40d 100644
--- a/src/browser/stories/App.readmeScreenshots.stories.tsx
+++ b/src/browser/stories/App.readmeScreenshots.stories.tsx
@@ -273,10 +273,10 @@ export const CodeReview: AppStory = {
window.localStorage.setItem(RIGHT_SIDEBAR_WIDTH_KEY, "950");
window.localStorage.removeItem(getRightSidebarLayoutKey(workspaceId));
- const REVIEW_DIFF = `diff --git a/src/browser/components/WorkspaceShell.tsx b/src/browser/components/WorkspaceShell.tsx
+ const REVIEW_DIFF = `diff --git a/src/browser/features/Workspace/WorkspaceShell.tsx b/src/browser/features/Workspace/WorkspaceShell.tsx
index aaa1111..bbb2222 100644
---- a/src/browser/components/WorkspaceShell.tsx
-+++ b/src/browser/components/WorkspaceShell.tsx
+--- a/src/browser/features/Workspace/WorkspaceShell.tsx
++++ b/src/browser/features/Workspace/WorkspaceShell.tsx
@@ -1,8 +1,18 @@
import React from 'react';
+import { useRightSidebarLayout } from '../Hooks/useRightSidebarLayout';
@@ -328,7 +328,7 @@ index 0000000..def5678
+ return layout;
+}`;
- const REVIEW_NUMSTAT = `10\t2\tsrc/browser/components/WorkspaceShell.tsx
+ const REVIEW_NUMSTAT = `10\t2\tsrc/browser/features/Workspace/WorkspaceShell.tsx
12\t0\tsrc/browser/utils/layout.ts
18\t0\tsrc/browser/hooks/useRightSidebarLayout.ts`;
@@ -354,7 +354,7 @@ index 0000000..def5678
toolCalls: [
createFileReadTool(
"call-read-1",
- "src/browser/components/WorkspaceShell.tsx",
+ "src/browser/features/Workspace/WorkspaceShell.tsx",
'import React from \'react\';\nimport { useRightSidebarLayout } from \'../hooks/useRightSidebarLayout\';\nimport { clamp } from \'../utils/layout\';\n\nexport function WorkspaceShell(props: WorkspaceShellProps) {\n const layout = useRightSidebarLayout(props.workspaceId);\n const sidebarWidth = clamp(layout.width, 200, 800);\n return (\n
\n Mux\n \n \n
\n );\n}'
),
createFileReadTool(
diff --git a/src/browser/stories/App.sidebar.stories.tsx b/src/browser/stories/App.sidebar.stories.tsx
index ec4d383e06..df37f596e4 100644
--- a/src/browser/stories/App.sidebar.stories.tsx
+++ b/src/browser/stories/App.sidebar.stories.tsx
@@ -88,7 +88,10 @@ function createGitStatusExecutor(gitStatus?: Map) {
const dirtyFiles =
dirtyCount > 0
- ? [" M src/App.tsx", " M src/browser/components/GitStatusIndicatorView.tsx"].join("\n")
+ ? [
+ " M src/App.tsx",
+ " M src/browser/features/Project/GitStatus/GitStatusIndicatorView.tsx",
+ ].join("\n")
: "";
return [
diff --git a/src/browser/stories/meta.tsx b/src/browser/stories/meta.tsx
index 1be7f8e15a..1405cf93d2 100644
--- a/src/browser/stories/meta.tsx
+++ b/src/browser/stories/meta.tsx
@@ -8,7 +8,7 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import type { FC, ReactNode } from "react";
import { useRef } from "react";
-import { AppLoader } from "../components/AppLoader/AppLoader";
+import { AppLoader } from "../features/AppShell/AppLoader/AppLoader";
import { TooltipProvider } from "@/browser/components/Tooltip/Tooltip";
import type { APIClient } from "@/browser/contexts/API";
import { ThemeProvider } from "@/browser/contexts/ThemeContext";
diff --git a/src/browser/utils/runtimeUi.ts b/src/browser/utils/runtimeUi.ts
index 08638e3e2c..e9ad53fdc2 100644
--- a/src/browser/utils/runtimeUi.ts
+++ b/src/browser/utils/runtimeUi.ts
@@ -7,7 +7,7 @@ import {
DockerIcon,
DevcontainerIcon,
CoderIcon,
-} from "@/browser/components/icons/RuntimeIcons/RuntimeIcons";
+} from "@/browser/components/Icons/RuntimeIcons/RuntimeIcons";
export interface RuntimeIconProps {
size?: number;
diff --git a/src/node/services/agentSkills/builtInSkillContent.generated.ts b/src/node/services/agentSkills/builtInSkillContent.generated.ts
new file mode 100644
index 0000000000..90e16516ed
--- /dev/null
+++ b/src/node/services/agentSkills/builtInSkillContent.generated.ts
@@ -0,0 +1,6205 @@
+// AUTO-GENERATED - DO NOT EDIT
+// Run: bun scripts/gen_builtin_skills.ts
+// Source: src/node/builtinSkills/*.md and docs/
+
+export const BUILTIN_SKILL_FILES: Record> = {
+ init: {
+ "SKILL.md": [
+ "---",
+ "name: init",
+ "description: Bootstrap an AGENTS.md file in a new or existing project",
+ "---",
+ "",
+ "",
+ "Use your tools to create or improve an AGENTS.md file in the root of the workspace which will serve as a contribution guide for AI agents.",
+ "If an AGENTS.md file already exists, focus on additive improvement (preserve intent and useful information; refine, extend, and reorganize as needed) rather than replacing it wholesale.",
+ "Inspect the workspace layout, code, documentation and git history to ensure correctness and accuracy.",
+ "",
+ "Ensure the following preamble exists at the top of the file before any other sections. Do not include the surrounding code fence backticks; only include the text.",
+ "",
+ "```md",
+ "You are an experienced, pragmatic software engineering AI agent. Do not over-engineer a solution when a simple one is possible. Keep edits minimal. If you want an exception to ANY rule, you MUST stop and get permission first.",
+ "```",
+ "",
+ "Recommended sections:",
+ "",
+ "- Project Overview (mandatory)",
+ " - Basic details about the project (e.g., high-level overview and goals).",
+ " - Technology choices (e.g., languages, databases, frameworks, libraries, build tools).",
+ "- Reference (mandatory)",
+ " - List important code files.",
+ " - List important directories and basic code structure tips.",
+ " - Project architecture.",
+ "- Essential commands (mandatory)",
+ " - build",
+ " - format",
+ " - lint",
+ " - test",
+ " - clean",
+ " - development server",
+ " - other _important_ scripts (use `find -type f -name '*.sh'` or similar)",
+ "- Patterns (optional)",
+ " - List any important or uncommon patterns (compared to other similar codebases), with examples (e.g., how to authorize an HTTP request).",
+ " - List any important workflows and their steps (e.g., how to make a database migration).",
+ " - Testing patterns.",
+ "- Anti-patterns (optional)",
+ " - Search git history and comments to find recurring mistakes or forbidden patterns.",
+ " - List each pattern and its reason.",
+ "- Code style (optional)",
+ " - Style guide to follow (with link).",
+ "- Commit and Pull Request Guidelines (mandatory)",
+ " - Required steps for validating changes before committing.",
+ " - Commit message conventions (read `git log`, or use `type: message` by default).",
+ " - Pull request description requirements.",
+ "",
+ "You can add other sections if they are necessary.",
+ "If the information required for mandatory sections isn't available due to the workspace being empty or sparse, add TODO text in its place.",
+ "Optional sections should be scrapped if the information is too thin.",
+ "",
+ "Some investigation tips:",
+ "",
+ "- Read existing lint configs, tsconfig, and CI workflows to find any style or layout rules.",
+ '- Search for "TODO", "HACK", "FIXME", "don\'t", "never", "always" in comments.',
+ "- Examine test files for patterns.",
+ "- Read PR templates and issue templates if they exist.",
+ "- Check for existing CONTRIBUTING.md, CODE_OF_CONDUCT.md, or similar documentation files.",
+ "",
+ "Some writing tips:",
+ "",
+ '- Each "do X" should have a corresponding "don\'t Y" where applicable.',
+ "- Commands should be easily copy-pastable and tested.",
+ "- Terms or phrases specific to this project should be explained on first use.",
+ "- Anything that is against the norm should be explicitly highlighted and called out.",
+ "",
+ "Above all things:",
+ "",
+ "- The document must be clear and concise. Simple projects should need less than 400 words, but larger and more mature codebases will likely need 700+. Prioritize completeness over brevity.",
+ "- Don't include useless fluff.",
+ "- The document must be in Markdown format and use headings for structure.",
+ "- Give examples where necessary or helpful (commands, directory paths, naming patterns).",
+ "- Explanations and examples must be correct and specific to this codebase.",
+ "- Maintain a professional, instructional tone.",
+ "",
+ "If the workspace is empty or sparse, ask the user for more information. Avoid hallucinating important decisions. You can provide suggestions to the user for language/technology/tool choices, but always respect the user's decision.",
+ "",
+ "- Project description and goals.",
+ "- Language(s).",
+ "- Technologies (database?), frameworks, libraries.",
+ "- Tools.",
+ "- Any other questions as you deem necessary.",
+ "",
+ "For empty or sparse workspaces ONLY, when finished writing/updating AGENTS.md, ask the user if they would like you to do the following:",
+ "",
+ "- initialize git IF it's not already set up (e.g., `git init`, `git remote add`, etc.)",
+ "- write a concise README.md file",
+ "- generate the bare minimum project scaffolding (e.g., initializing the package manager, writing a minimal build tool config)",
+ " ",
+ "",
+ ].join("\n"),
+ },
+ "mux-diagram": {
+ "SKILL.md": [
+ "---",
+ "name: mux-diagram",
+ "description: Mermaid diagram best practices and text-based chart alternatives",
+ "---",
+ "",
+ "# Diagrams & Charts",
+ "",
+ "Use this skill when creating diagrams, flowcharts, or chart-like visualizations.",
+ "",
+ "## Mermaid (rendered)",
+ "",
+ "The app renders fenced `mermaid` code blocks as interactive diagrams.",
+ "",
+ "Best practices:",
+ "",
+ "- Avoid side-by-side subgraphs (they display too wide)",
+ "- For comparisons, use separate diagram blocks or single graph with visual separation",
+ "- When using custom fill colors, include contrasting color property (e.g., `style note fill:#ff6b6b,color:#fff`)",
+ "- Make good use of visual space: e.g. use inline commentary",
+ '- Wrap node labels containing brackets or special characters in quotes (e.g., `Display["Message[]"]` not `Display[Message[]]`)',
+ "",
+ "Supported diagram types: flowchart, sequence, class, state, ER, Gantt, pie, git graph, mindmap, timeline, sankey, and more. Choose the type that best fits the data.",
+ "",
+ "## Text-based alternatives (no rendering required)",
+ "",
+ "Not every visualization needs Mermaid. Prefer lightweight formats when they suffice:",
+ "",
+ "- **Markdown tables** — best for structured comparisons, feature matrices, or tabular data",
+ "- **Bulleted/numbered lists** — best for hierarchies, step sequences, or simple trees",
+ "- **Indented tree notation** — for directory structures or shallow hierarchies",
+ "- **ASCII/Unicode bars** — quick inline quantitative comparisons (e.g., `████░░ 67%`)",
+ "",
+ "Choose Mermaid when relationships, flow, or topology matter. Choose text when the data is tabular or sequential.",
+ "",
+ ].join("\n"),
+ },
+ "mux-docs": {
+ "references/docs/AGENTS.md": [
+ "---",
+ "title: AGENTS.md",
+ "description: Agent instructions for AI assistants working on the Mux codebase",
+ "---",
+ "",
+ "**Prime directive:** keep edits minimal and token-efficient—say only what conveys actionable signal.",
+ "",
+ "## Project Snapshot",
+ "",
+ "- `mux`: Electron + React desktop app for parallel agent workflows; UX must be fast, responsive, predictable.",
+ "- Minor breaking changes are expected, but critical flows must allow upgrade↔downgrade without friction; skip migrations when breakage is tightly scoped.",
+ "- **Before creating or updating any PR, commit, or public issue**, you **MUST** read the `pull-requests` skill (`agent_skill_read`) for attribution footer requirements and workflow conventions. Do not skip this step.",
+ "",
+ "## External Submissions",
+ "",
+ "- **Do not submit updates to the Terminal-Bench leaderboard repo directly.** Only provide the user with commands they can run themselves.",
+ "",
+ "## Repo Reference",
+ "",
+ "- Core files: `src/main.ts`, `src/preload.ts`, `src/App.tsx`, `src/config.ts`.",
+ "- Up-to-date model names: see `src/common/knownModels.ts` for current provider model IDs.",
+ "- Persistent data: `~/.mux/config.json`, `~/.mux/src//` (worktrees), `~/.mux/sessions//chat.jsonl`.",
+ "",
+ "## Documentation Rules",
+ "",
+ "- No free-floating Markdown. User docs live in `docs/` (read `docs/README.md`, add pages to `docs.json` navigation, use standard Markdown + mermaid). Developer notes belong inline as comments.",
+ " - Exception: the `rfc` folder contains human-written RFCs for implementation planning.",
+ "- For planning artifacts, use the `propose_plan` tool or inline comments instead of ad-hoc docs.",
+ "- Do not add new root-level docs without explicit request; during feature work rely on code + tests + inline comments.",
+ "- External API docs already live inside `/tmp/ai-sdk-docs/**.mdx`; never browse `https://sdk.vercel.ai/docs/ai-sdk-core` directly.",
+ "",
+ "### Code Comments",
+ "",
+ "- When delivering a user's request, leave their rationale in the code as comments.",
+ '- Generally, prefer code comments that explain the "why" behind a change.',
+ '- Still explain the "what" if the code is opaque, surprising, confusing, etc.',
+ "",
+ "## Key Features & Performance",
+ "",
+ "- Core UX: projects sidebar (left panel), workspace management (local git worktrees or SSH clones), config stored in `~/.mux/config.json`.",
+ "- Fetch bulk data in one IPC call—no O(n) frontend→backend loops.",
+ "- **React Compiler enabled** — auto-memoization handles components/hooks; do not add manual `React.memo()`, `useMemo`, or `useCallback` for memoization purposes. Focus instead on fixing unstable object references that the compiler cannot optimize (e.g., `new Set()` in state setters, inline object literals as props).",
+ "- **useEffect** — Before adding effects, consult the `react-effects` skill. Most effects for derived state, prop resets, or event-triggered logic are anti-patterns.",
+ "",
+ "## Tooling & Commands",
+ "",
+ "- Package manager: bun only. Use `bun install`, `bun add`, `bun run` (which proxies to Make when relevant). Run `bun install` if modules/types go missing.",
+ "- Makefile is source of truth (new commands land there, not `package.json`).",
+ "- Primary targets: `make dev|start|build|lint|lint-fix|fmt|fmt-check|typecheck|test|test-integration|clean|help`.",
+ "- Full `static-check` includes docs link checking via `mintlify broken-links`.",
+ "- `.mux/tool_env` is sourced before every `bash` tool call. Use `run_and_report ` when running multiple validation steps in one call.",
+ "- Do not pipe/redirect/wrap `run_and_report` output; keep helper markers intact so Mux can show clean step status.",
+ "- `./scripts/wait_pr_ready.sh ` is the preferred tail-end helper after local validation and after you've exhausted useful local work.",
+ "- `./scripts/wait_pr_checks.sh ` is the checks watcher; `wait_pr_ready.sh` must execute `wait_pr_checks.sh --once` on each loop iteration.",
+ "- `./scripts/wait_pr_codex.sh ` is the Codex gate used by `wait_pr_ready.sh`.",
+ "",
+ "## PR Workflow (Codex)",
+ "",
+ "- If a PR has Codex review comments, address + resolve them, then re-request review by commenting `@codex review` on the PR.",
+ "- Prefer `gh` CLI for GitHub interactions over manual web/curl flows.",
+ "- In Orchestrator mode, delegate implementation/verification commands to `exec` or `explore` sub-agents and integrate their patches; do not bypass delegation with direct local edits.",
+ "- In Orchestrator mode, route higher-complexity implementation tasks to `plan` sub-agents so they can research and produce a precise plan before auto-handoff to implementation.",
+ "",
+ "- User preference: when work is already on an open PR, push branch updates at the end of each completed change set so the PR stays current.",
+ '- **PR creation gate:** Do **not** open/create a pull request unless the user explicitly asks (e.g., "open a PR", "create PR", "submit this"). By default, complete local validation, commit/push branch updates as requested, and let the user review before deciding whether to open a PR.',
+ "",
+ "> PR readiness is mandatory. You MUST keep iterating until the PR is fully ready.",
+ '> A PR is fully ready only when: (1) Codex confirms approval (thumbs-up reaction on the PR description or an approval comment like "Didn\'t find any major issues"), (2) all Codex review threads are resolved, and (3) all required CI checks pass.',
+ "> You MUST NOT report success or stop the loop before these conditions are met.",
+ "",
+ "When a PR exists, you MUST remain in this loop until the PR is fully ready:",
+ "",
+ "1. Push your latest fixes.",
+ "2. Run local validation (`make static-check` and targeted tests as needed); in Orchestrator mode, delegate command execution to sub-agents.",
+ "3. Request review with `@codex review`.",
+ "4. Run `./scripts/wait_pr_ready.sh ` (which must execute `./scripts/wait_pr_checks.sh --once` while checks are pending).",
+ "5. If Codex leaves comments, address them (delegate fixes in Orchestrator mode), resolve threads with `./scripts/resolve_pr_comment.sh `, push, and repeat.",
+ "6. If checks/mergeability fail, fix issues locally (delegate fixes in Orchestrator mode), push, and repeat.",
+ "",
+ "The only early-stop exception is when the reviewer is clearly misunderstanding the intended change and further churn would be counterproductive. In that case, leave a clarifying PR comment and pause for human direction.",
+ "",
+ "## Testing: HistoryService",
+ "",
+ "HistoryService is pure local disk I/O with a single dependency (`getSessionDir`). **Always use a real instance** via `createTestHistoryService()` (`src/node/services/testHistoryService.ts`) rather than mocking.",
+ "",
+ "- Partial message lifecycle (`readPartial` / `writePartial` / `deletePartial` / `commitPartial`) is part of HistoryService; there is no separate PartialService.",
+ "- For pre-seeded data: call `historyService.appendToHistory()` in `beforeEach`",
+ '- For error injection: use real instance + `spyOn(historyService, "method").mockRejectedValueOnce(...)`',
+ '- For call tracking: `spyOn(historyService, "method")` without `mockImplementation` — real impl runs, calls are recorded',
+ "- For assertions: read history back with `getHistoryFromLatestBoundary()` or `getLastMessages()` instead of checking mock calls",
+ "",
+ "## Mobile Testing",
+ "",
+ "Mobile app tests live in `mobile/src/**/*.test.ts` and use Bun's built-in test runner (`bun test`).",
+ "",
+ "- Run mobile tests: `make test-mobile` or `bun run test:mobile`.",
+ "- The environment currently lacks native mobile testing tools (ADB, iOS Simulator), so focus on unit and integration tests that can run in a Node/Bun environment.",
+ "- Mobile components do not yet have automated UI tests.",
+ "",
+ "## Refactoring & Runtime Etiquette",
+ "",
+ "- Use `git mv` to retain history when moving files.",
+ "",
+ "## Self-Healing & Crash Resilience",
+ "",
+ "- Prefer **self-healing** behavior: if corrupted or invalid data exists in persisted state (e.g., `chat.jsonl`), the system should sanitize or filter it at load/request time rather than failing permanently.",
+ "- Never let a single malformed line in history brick a workspace—apply defensive filtering in request-building paths so the user can continue working.",
+ "- When streaming crashes, any incomplete state committed to disk should either be repairable on next load or excluded from provider requests to avoid API validation errors.",
+ "- **Startup-time initialization must never crash the app.** Wrap in try-catch, use timeouts, fall back silently.",
+ "",
+ "## Command Palette & UI Access",
+ "",
+ "- Open palette with `Cmd+Shift+P` (mac) / `Ctrl+Shift+P` (win/linux) / `F4`; quick toggle via `Cmd+P` / `Ctrl+P`.",
+ "- Palette covers workspace mgmt, navigation, chat utils, mode/model switches, slash commands (`/` for suggestions, `>` for actions).",
+ "",
+ "## Styling",
+ "",
+ "- Never use emoji characters as UI icons or status indicators; emoji rendering varies across platforms and fonts.",
+ "- Prefer SVG icons (usually from `lucide-react`) or shared icon components under `src/browser/components/Icons/`.",
+ "- For tool call headers, use `ToolIcon` from `src/browser/components/tools/shared/ToolPrimitives.tsx`.",
+ "- If a tool/agent provides an emoji string (e.g., `status_set` or `displayStatus`), render via `EmojiIcon` (`src/browser/components/Icons/EmojiIcon.tsx`) instead of rendering the emoji.",
+ "- If a new emoji appears in tool output, extend `EmojiIcon` to map it to an SVG icon.",
+ "- Colors defined in `src/browser/styles/globals.css` (`:root @theme` block). Reference via CSS variables (e.g., `var(--color-plan-mode)`), never hardcode hex values.",
+ "",
+ "## Security: Renderer HTML & XSS",
+ "",
+ "- Treat repo-controlled strings (file paths, diff content, branch names, commit messages) as attacker-controlled input.",
+ "- Never render attacker-controlled data through `dangerouslySetInnerHTML`, `innerHTML`, `outerHTML`, or `insertAdjacentHTML`.",
+ "- Prefer React element trees for highlighting (split + `` nodes) so React escaping stays in effect.",
+ "- If raw HTML/SVG rendering is unavoidable (e.g., Shiki/Mermaid), require explicit sanitization/hardening and document the trust boundary with a `SECURITY AUDIT` comment at the sink.",
+ "",
+ "## TypeScript Discipline",
+ "",
+ "- Ban `as any`; rely on discriminated unions, type guards, or authored interfaces.",
+ "- Use `Record` for exhaustive mappings to catch missing cases.",
+ "- Apply utility types (`Omit`, `Pick`, etc.) to build UI-specific variants of backend types, preventing unnecessary re-renders and clarifying intent.",
+ "- Let types drive design: prefer discriminated unions for state, minimize runtime checks, and simplify when types feel unwieldy.",
+ "- Use `using` declarations (or equivalent disposables) for processes, file handles, etc., to ensure cleanup even on errors.",
+ "- Centralize magic constants under `src/constants/`; share them instead of duplicating values across layers.",
+ "- Never repeat constant values (like keybinds) in comments—they become stale when the constant changes.",
+ "- **Avoid `void asyncFn()`** - fire-and-forget async calls hide race conditions. When state is observable by other code (in-memory cache, event emitters), ensure visibility order matches invariants. If memory and disk must stay in sync, persist before updating memory so observers see consistent state.",
+ "- **Avoid `setTimeout` for component coordination** - racy and fragile; use callbacks or effects.",
+ "- **Keyboard event propagation** - React's `e.stopPropagation()` only stops synthetic event bubbling; native `window` listeners still fire. Escape-to-interrupt is **safe-by-default** in editable elements (``, `