diff --git a/apps/app/src/components/onboarding/Onboarding.stories.tsx b/apps/app/src/components/onboarding/Onboarding.stories.tsx index 0430845fa78..e8c45374309 100644 --- a/apps/app/src/components/onboarding/Onboarding.stories.tsx +++ b/apps/app/src/components/onboarding/Onboarding.stories.tsx @@ -14,10 +14,6 @@ import { type OnboardingConnectState, type OnboardingRepo, } from "./OnboardingViews"; -import { - SetupChecklistBanner, - type SetupChecklistItem, -} from "./SetupChecklistHost"; import type { AgentSetupState, OnboardingStepId } from "./onboarding-model"; import claudeLogoUrl from "../../../../../plugins/provider-claude-code/icons/claude-code.svg"; import codexLogoUrl from "../../../../../plugins/provider-codex/icons/codex.svg"; @@ -490,66 +486,9 @@ function DevicesFrame({ ); } -function checklistItems(done: { - agent: boolean; - projects: boolean; - plugins: boolean; - devices: boolean; -}): SetupChecklistItem[] { - return [ - { - id: "agent", - title: "Connect a coding agent", - detail: done.agent - ? "Ready on this computer" - : "Needed before a thread can start", - done: done.agent, - actionLabel: "Set up", - }, - { - id: "projects", - title: "Add your projects", - detail: done.projects - ? "3 added" - : "Import the repos you've used recently", - done: done.projects, - actionLabel: "Review", - }, - { - id: "plugins", - title: "Pick some plugins", - detail: done.plugins - ? "2 turned on" - : "Browser automation, workflows, and more", - done: done.plugins, - actionLabel: "Browse", - }, - { - id: "devices", - title: "Use bb from anywhere", - detail: done.devices ? CONNECT_URL : "Phone, browser, other machines", - done: done.devices, - actionLabel: "Set up", - }, - ]; -} - -function HomeFrame({ - items, - agentMissing, - onOpen = noop, - onDismiss = noop, -}: { - items: SetupChecklistItem[] | null; - agentMissing: boolean; - onOpen?: (step: OnboardingStepId) => void; - onDismiss?: () => void; -}) { +function HomeFrame() { return (
-

The usual bb home composer goes here.

@@ -587,12 +526,10 @@ function InteractiveFlowRun({ scenario }: { scenario: Scenario }) { const [selectedRepoIds, setSelectedRepoIds] = useState>( () => new Set(DEFAULT_REPO_IDS), ); - const [imported, setImported] = useState(false); const [enabledPluginIds, setEnabledPluginIds] = useState>( () => new Set(), ); const [connectSetupOpen, setConnectSetupOpen] = useState(false); - const [checklistDismissed, setChecklistDismissed] = useState(false); const timers = useRef([]); const later = (delayMs: number, run: () => void) => { @@ -640,20 +577,9 @@ function InteractiveFlowRun({ scenario }: { scenario: Scenario }) { }; if (view === "home") { - const items = checklistItems({ - agent: anyReady(states), - projects: imported, - plugins: enabledPluginIds.size > 0, - devices: false, - }); return ( - setChecklistDismissed(true)} - /> + ); } @@ -709,10 +635,7 @@ function InteractiveFlowRun({ scenario }: { scenario: Scenario }) { ) } onSelectNone={() => setSelectedRepoIds(new Set())} - onPrimary={() => { - setImported(selectedRepoIds.size > 0); - setView("plugins"); - }} + onPrimary={() => setView("plugins")} onSecondary={() => setView("plugins")} onBack={() => setView("agent")} /> @@ -1037,35 +960,11 @@ export function HomeAfterSkipping() { return ( - - - - - - + diff --git a/apps/app/src/components/onboarding/OnboardingGate.test.tsx b/apps/app/src/components/onboarding/OnboardingGate.test.tsx index 3046baf5efd..d5fefc36936 100644 --- a/apps/app/src/components/onboarding/OnboardingGate.test.tsx +++ b/apps/app/src/components/onboarding/OnboardingGate.test.tsx @@ -1,10 +1,8 @@ // @vitest-environment jsdom import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import { defaultAppSettings, type AppSettings } from "@bb/domain"; -import { Provider, createStore } from "jotai"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { OnboardingGate } from "./OnboardingGate"; -import { onboardingReopenStepAtom } from "./onboarding-state"; const mocks = vi.hoisted(() => ({ mutate: vi.fn(), @@ -40,15 +38,13 @@ function configWith(settings: Partial) { }); } -function renderGate(store = createStore()) { +function renderGate() { const view = render( - - -
App shell
-
-
, + +
App shell
+
, ); - return { store, view }; + return { view }; } beforeEach(() => { @@ -79,7 +75,7 @@ describe("OnboardingGate", () => { expect(screen.queryByText(/Setup guide/u)).toBeNull(); }); - it("records completion, turns the checklist on, and reveals the app when first-run setup closes", async () => { + it("records completion and reveals the app when first-run setup closes", async () => { configWith({ onboardingCompletedAt: null, streamerMode: true }); renderGate(); @@ -90,48 +86,30 @@ describe("OnboardingGate", () => { const written = mocks.mutate.mock.calls[0]?.[0] as AppSettings; expect(written).toMatchObject({ streamerMode: true, - setupChecklistVisible: true, }); expect(Number.isNaN(Date.parse(written.onboardingCompletedAt ?? ""))).toBe( false, ); }); - it("reopens at a requested step without rewriting the completion time", async () => { - configWith({ onboardingCompletedAt: COMPLETED_AT }); - const store = createStore(); - store.set(onboardingReopenStepAtom, "plugins"); - - renderGate(store); - fireEvent.click(await screen.findByText("Setup guide at plugins")); - - expect(screen.getByText("App shell")).toBeTruthy(); - expect(store.get(onboardingReopenStepAtom)).toBeNull(); - expect(mocks.mutate).not.toHaveBeenCalled(); - }); - it("shows the guide again when the completion time is cleared after a close in the same session", async () => { configWith({ onboardingCompletedAt: null }); - const { view, store } = renderGate(); + const { view } = renderGate(); fireEvent.click(await screen.findByText("Setup guide at agent")); configWith({ onboardingCompletedAt: COMPLETED_AT }); view.rerender( - - -
App shell
-
-
, + +
App shell
+
, ); expect(screen.getByText("App shell")).toBeTruthy(); configWith({ onboardingCompletedAt: null }); view.rerender( - - -
App shell
-
-
, + +
App shell
+
, ); expect(await screen.findByText("Setup guide at agent")).toBeTruthy(); diff --git a/apps/app/src/components/onboarding/OnboardingGate.tsx b/apps/app/src/components/onboarding/OnboardingGate.tsx index 25441729a90..855c7d879e4 100644 --- a/apps/app/src/components/onboarding/OnboardingGate.tsx +++ b/apps/app/src/components/onboarding/OnboardingGate.tsx @@ -1,9 +1,7 @@ import { Suspense, lazy, useEffect, useState, type ReactNode } from "react"; -import { useAtom } from "jotai"; import { TooltipProvider } from "@bb/shared-ui/tooltip"; import { useUpdateGeneralSettings } from "@/hooks/mutations/settings-mutations"; import { useSystemConfig } from "@/hooks/queries/system-queries"; -import { onboardingReopenStepAtom } from "./onboarding-state"; const OnboardingFlow = lazy(() => import("./OnboardingFlow").then((module) => ({ @@ -32,7 +30,6 @@ function rememberOnboardingSeen(): void { export function OnboardingGate({ children }: { children: ReactNode }) { const configQuery = useSystemConfig(); const updateSettings = useUpdateGeneralSettings(); - const [reopenStep, setReopenStep] = useAtom(onboardingReopenStepAtom); const [firstRunClosed, setFirstRunClosed] = useState(false); const [seenBefore] = useState(readOnboardingSeen); @@ -53,25 +50,20 @@ export function OnboardingGate({ children }: { children: ReactNode }) { } const firstRun = settings.onboardingCompletedAt === null && !firstRunClosed; - if (!firstRun && reopenStep === null) return children; + if (!firstRun) return children; return (
{ - if (firstRun) { - setFirstRunClosed(true); - updateSettings.mutate({ - ...settings, - onboardingCompletedAt: new Date().toISOString(), - setupChecklistVisible: true, - }); - } - setReopenStep(null); + setFirstRunClosed(true); + updateSettings.mutate({ + ...settings, + onboardingCompletedAt: new Date().toISOString(), + }); }} /> diff --git a/apps/app/src/components/onboarding/SetupChecklistHost.test.tsx b/apps/app/src/components/onboarding/SetupChecklistHost.test.tsx deleted file mode 100644 index 3cf0f19bd0d..00000000000 --- a/apps/app/src/components/onboarding/SetupChecklistHost.test.tsx +++ /dev/null @@ -1,216 +0,0 @@ -// @vitest-environment jsdom -import { cleanup, fireEvent, render, screen } from "@testing-library/react"; -import { defaultAppSettings, type AppSettings } from "@bb/domain"; -import { Provider, createStore } from "jotai"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { - SetupChecklistBanner, - hasSetupChecklistBanner, - useSetupChecklist, -} from "./SetupChecklistHost"; -import { onboardingReopenStepAtom } from "./onboarding-state"; - -const mocks = vi.hoisted(() => ({ - mutate: vi.fn(), - useHosts: vi.fn(), - usePluginList: vi.fn(), - usePrimaryHost: vi.fn(), - useSidebarNavigation: vi.fn(), - useSystemConfig: vi.fn(), - useSystemProviderStates: vi.fn(), -})); - -vi.mock("@/hooks/queries/system-queries", () => ({ - useSystemConfig: mocks.useSystemConfig, - useSystemProviderStates: mocks.useSystemProviderStates, -})); -vi.mock("@/hooks/mutations/settings-mutations", () => ({ - useUpdateGeneralSettings: () => ({ mutate: mocks.mutate }), -})); -vi.mock("@/hooks/queries/host-queries", () => ({ - useHosts: mocks.useHosts, - usePrimaryHost: mocks.usePrimaryHost, -})); -vi.mock("@/hooks/queries/plugin-settings-queries", () => ({ - usePluginList: mocks.usePluginList, -})); -vi.mock("@/hooks/queries/sidebar-navigation-query", () => ({ - useSidebarNavigation: mocks.useSidebarNavigation, -})); - -const CONNECT_ON = { - providers: [ - { - id: "connect", - displayName: "bb connect", - description: "", - pluginId: "connect", - availability: { - status: "available", - serverUrl: "https://sawyer.getbb.app", - }, - }, - ], - defaultProviderId: "connect", - effectiveUrl: "https://sawyer.getbb.app", - urlSource: null, -}; -const CONNECT_OFF = { ...CONNECT_ON, providers: [] }; - -interface Scenario { - settings?: Partial; - agentStatuses?: readonly string[]; - projectCount?: number; - enabledPluginIds?: readonly string[]; - connectOn?: boolean; -} - -function arrange({ - settings = {}, - agentStatuses = ["unauthenticated"], - projectCount = 0, - enabledPluginIds = [], - connectOn = false, -}: Scenario) { - mocks.useSystemConfig.mockReturnValue({ - data: { - generalSettings: { - ...defaultAppSettings, - onboardingCompletedAt: "2026-10-01T00:00:00.000Z", - setupChecklistVisible: true, - ...settings, - }, - serverAccess: connectOn ? CONNECT_ON : CONNECT_OFF, - }, - }); - mocks.usePrimaryHost.mockReturnValue({ id: "host-1" }); - mocks.useHosts.mockReturnValue({ data: [{ id: "host-1" }] }); - mocks.useSystemProviderStates.mockReturnValue({ - data: { - providers: agentStatuses.map((status, index) => ({ - providerId: `provider-${index}`, - status, - })), - }, - }); - mocks.useSidebarNavigation.mockReturnValue({ - data: { - projects: Array.from({ length: projectCount }, (_, index) => ({ - id: `proj_${index}`, - })), - }, - }); - mocks.usePluginList.mockReturnValue({ - data: { - plugins: enabledPluginIds.map((id) => ({ id, enabled: true })), - }, - }); -} - -function Harness() { - const checklist = useSetupChecklist(); - return ( - <> - - {String(hasSetupChecklistBanner(checklist))} - - - - ); -} - -function renderHarness() { - const store = createStore(); - render( - - - , - ); - return store; -} - -beforeEach(() => { - vi.clearAllMocks(); -}); - -afterEach(() => { - cleanup(); -}); - -describe("setup checklist", () => { - it("stays out of the way for installs that never opted into the checklist", () => { - arrange({ settings: { setupChecklistVisible: false } }); - - renderHarness(); - - expect(screen.getByTestId("has-banner").textContent).toBe("false"); - expect(screen.queryByText("Finish setting up bb")).toBeNull(); - expect(screen.queryByText(/No agent is ready/u)).toBeNull(); - expect(mocks.useSystemProviderStates).toHaveBeenCalledWith( - expect.objectContaining({ enabled: false }), - ); - }); - - it("leads with the missing agent and reopens setup at the agent step", () => { - arrange({ agentStatuses: ["unauthenticated", "not_installed"] }); - - const store = renderHarness(); - - expect(screen.getByText("No agent is ready on this computer")).toBeTruthy(); - fireEvent.click(screen.getByText("Connect an agent")); - expect(store.get(onboardingReopenStepAtom)).toBe("agent"); - }); - - it("does not claim the agent is missing when a provider could not be checked", () => { - arrange({ agentStatuses: ["unauthenticated", "unknown"] }); - - renderHarness(); - - expect(screen.queryByText(/No agent is ready/u)).toBeNull(); - expect(screen.getByText("Finish setting up bb")).toBeTruthy(); - }); - - it("points the compact banner at the first step that is still open", () => { - arrange({ agentStatuses: ["ready"], projectCount: 2 }); - - const store = renderHarness(); - - expect( - screen.getByText("2 of 4 done · next: pick some plugins"), - ).toBeTruthy(); - fireEvent.click(screen.getByText("Continue")); - expect(store.get(onboardingReopenStepAtom)).toBe("plugins"); - }); - - it("turns the checklist off when dismissed, keeping other settings", () => { - arrange({ agentStatuses: ["ready"], settings: { streamerMode: true } }); - - renderHarness(); - fireEvent.click(screen.getByLabelText("Dismiss setup checklist")); - - expect(mocks.mutate).toHaveBeenCalledWith( - expect.objectContaining({ - setupChecklistVisible: false, - streamerMode: true, - onboardingCompletedAt: "2026-10-01T00:00:00.000Z", - }), - ); - }); - - it("clears itself once every step is done instead of lingering hidden", () => { - arrange({ - agentStatuses: ["ready"], - projectCount: 1, - enabledPluginIds: ["workflows"], - connectOn: true, - }); - - renderHarness(); - - expect(screen.getByTestId("has-banner").textContent).toBe("false"); - expect(mocks.mutate).toHaveBeenCalledTimes(1); - expect(mocks.mutate).toHaveBeenCalledWith( - expect.objectContaining({ setupChecklistVisible: false }), - ); - }); -}); diff --git a/apps/app/src/components/onboarding/SetupChecklistHost.tsx b/apps/app/src/components/onboarding/SetupChecklistHost.tsx deleted file mode 100644 index 9e25bd6316a..00000000000 --- a/apps/app/src/components/onboarding/SetupChecklistHost.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { useCallback, useEffect, useRef } from "react"; -import { useSetAtom } from "jotai"; -import { Button } from "@bb/shared-ui/button"; -import { Icon } from "@bb/shared-ui/icon"; -import { PromptStackCard } from "@/components/promptbox/banner/PromptStackCard"; -import { ProviderRequirementBanner } from "@/components/promptbox/banner/ProviderRequirementBanner"; -import { useUpdateGeneralSettings } from "@/hooks/mutations/settings-mutations"; -import { useHosts, usePrimaryHost } from "@/hooks/queries/host-queries"; -import { usePluginList } from "@/hooks/queries/plugin-settings-queries"; -import { useSidebarNavigation } from "@/hooks/queries/sidebar-navigation-query"; -import { - useSystemConfig, - useSystemProviderStates, -} from "@/hooks/queries/system-queries"; -import { - ONBOARDING_PLUGINS, - connectAccessUrl, - hasNoUsableAgent, - hasReadyAgent, - type OnboardingStepId, -} from "./onboarding-model"; -import { onboardingReopenStepAtom } from "./onboarding-state"; - -export interface SetupChecklistItem { - id: OnboardingStepId; - title: string; - detail: string; - done: boolean; - actionLabel: string; -} - -interface SetupChecklistState { - items: SetupChecklistItem[] | null; - agentMissing: boolean; - open: (step: OnboardingStepId) => void; - dismiss: () => void; -} - -export function useSetupChecklist(): SetupChecklistState { - const configQuery = useSystemConfig(); - const updateSettings = useUpdateGeneralSettings(); - const setReopenStep = useSetAtom(onboardingReopenStepAtom); - const primaryHost = usePrimaryHost(); - const hostId = primaryHost?.id ?? null; - const settings = configQuery.data?.generalSettings; - const onboarded = - settings !== undefined && settings.onboardingCompletedAt !== null; - const visible = onboarded && settings.setupChecklistVisible; - - const statesQuery = useSystemProviderStates({ - enabled: visible && hostId !== null, - poll: false, - ...(hostId === null ? {} : { hostId }), - }); - const navigationQuery = useSidebarNavigation({ enabled: visible }); - const pluginsQuery = usePluginList({ enabled: visible }); - const hostsQuery = useHosts({ enabled: visible }); - - const states = statesQuery.data?.providers; - const agentReady = hasReadyAgent(states); - const agentMissing = visible && hasNoUsableAgent(states); - const open = (step: OnboardingStepId) => setReopenStep(step); - const { mutate: updateSettingsMutate } = updateSettings; - const dismiss = useCallback(() => { - if (settings === undefined) return; - updateSettingsMutate({ ...settings, setupChecklistVisible: false }); - }, [settings, updateSettingsMutate]); - - const projectCount = navigationQuery.data?.projects.length; - const installedPlugins = pluginsQuery.data?.plugins; - const enabledPluginCount = - installedPlugins === undefined - ? undefined - : ONBOARDING_PLUGINS.filter(({ pluginId }) => - installedPlugins.some( - (plugin) => plugin.id === pluginId && plugin.enabled, - ), - ).length; - const connect = connectAccessUrl(configQuery.data?.serverAccess); - const machineCount = hostsQuery.data?.length ?? 0; - const devicesDone = connect.status === "on" || machineCount > 1; - const everythingDone = - visible && - agentReady && - projectCount !== undefined && - projectCount > 0 && - enabledPluginCount !== undefined && - enabledPluginCount > 0 && - devicesDone; - const clearedRef = useRef(false); - useEffect(() => { - if (!everythingDone || clearedRef.current) return; - clearedRef.current = true; - dismiss(); - }, [dismiss, everythingDone]); - - if ( - !visible || - states === undefined || - projectCount === undefined || - enabledPluginCount === undefined - ) { - return { items: null, agentMissing, open, dismiss }; - } - - const items: SetupChecklistItem[] = [ - { - id: "agent", - title: "Connect a coding agent", - detail: agentReady - ? "Ready on this computer" - : "Needed before a thread can start", - done: agentReady, - actionLabel: "Set up", - }, - { - id: "projects", - title: "Add your projects", - detail: - projectCount > 0 - ? `${projectCount} added` - : "Import the repos you've used recently", - done: projectCount > 0, - actionLabel: "Review", - }, - { - id: "plugins", - title: "Pick some plugins", - detail: - enabledPluginCount > 0 - ? `${enabledPluginCount} turned on` - : "Browser automation, workflows, and more", - done: enabledPluginCount > 0, - actionLabel: "Browse", - }, - { - id: "devices", - title: "Use bb from anywhere", - detail: - connect.status === "on" - ? connect.url - : "Phone, browser, other machines", - done: devicesDone, - actionLabel: "Set up", - }, - ]; - return { - items: items.every((item) => item.done) ? null : items, - agentMissing, - open, - dismiss, - }; -} - -export function hasSetupChecklistBanner( - checklist: SetupChecklistState, -): boolean { - return checklist.agentMissing || checklist.items !== null; -} - -export function SetupChecklistBanner({ - checklist, -}: { - checklist: SetupChecklistState; -}) { - if (checklist.agentMissing) { - return ( - checklist.open("agent")} - > - Connect an agent - - } - /> - ); - } - if (checklist.items === null) return null; - const remaining = checklist.items.filter((item) => !item.done); - const next = remaining[0]; - if (next === undefined) return null; - return ( - -
-
-

- Finish setting up bb -

-

- {checklist.items.length - remaining.length} of{" "} - {checklist.items.length} done · next: {next.title.toLowerCase()} -

-
- - -
-
- ); -} diff --git a/apps/app/src/components/onboarding/onboarding-model.test.ts b/apps/app/src/components/onboarding/onboarding-model.test.ts index 48b7a29f0ed..a41604a22d0 100644 --- a/apps/app/src/components/onboarding/onboarding-model.test.ts +++ b/apps/app/src/components/onboarding/onboarding-model.test.ts @@ -8,7 +8,6 @@ import { describe, expect, it } from "vitest"; import { connectAccessUrl, defaultSelectedRepoPaths, - hasNoUsableAgent, hasReadyAgent, parseSignInOutput, resolveAgentSetupState, @@ -261,16 +260,10 @@ describe("resolveSignInCommand", () => { }); describe("agent readiness summaries", () => { - it("only reports no usable agent when every agent is definitely unusable", () => { + it("reports readiness only when an agent is ready", () => { const signedOut = health({ status: "unauthenticated" }); - const missing = health({ providerId: "codex", status: "not_installed" }); const unknown = health({ providerId: "pi", status: "unknown" }); - expect(hasNoUsableAgent([signedOut, missing])).toBe(true); - expect(hasNoUsableAgent([signedOut, unknown])).toBe(false); - expect(hasNoUsableAgent([signedOut, health()])).toBe(false); - expect(hasNoUsableAgent([])).toBe(false); - expect(hasNoUsableAgent(undefined)).toBe(false); expect(hasReadyAgent([signedOut, health()])).toBe(true); expect(hasReadyAgent([signedOut, unknown])).toBe(false); expect(hasReadyAgent(undefined)).toBe(false); diff --git a/apps/app/src/components/onboarding/onboarding-model.ts b/apps/app/src/components/onboarding/onboarding-model.ts index 4afe28785ce..ccd1f00de47 100644 --- a/apps/app/src/components/onboarding/onboarding-model.ts +++ b/apps/app/src/components/onboarding/onboarding-model.ts @@ -127,15 +127,6 @@ export function hasReadyAgent( return providers?.some((provider) => provider.status === "ready") ?? false; } -export function hasNoUsableAgent( - providers: readonly SystemProviderState[] | undefined, -): boolean { - if (providers === undefined || providers.length === 0) return false; - return providers.every( - (provider) => provider.status !== "ready" && provider.status !== "unknown", - ); -} - const RECENT_REPO_WINDOW_MS = 7 * 86_400_000; const DEFAULT_SELECTED_REPO_LIMIT = 5; diff --git a/apps/app/src/components/onboarding/onboarding-state.ts b/apps/app/src/components/onboarding/onboarding-state.ts deleted file mode 100644 index 95da5862d02..00000000000 --- a/apps/app/src/components/onboarding/onboarding-state.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { atom } from "jotai"; -import type { OnboardingStepId } from "./onboarding-model"; - -export const onboardingReopenStepAtom = atom(null); diff --git a/apps/app/src/components/plugin/PluginNewThreadComposer.test.tsx b/apps/app/src/components/plugin/PluginNewThreadComposer.test.tsx index 4ce07a8ca0d..8ff70d9b3a0 100644 --- a/apps/app/src/components/plugin/PluginNewThreadComposer.test.tsx +++ b/apps/app/src/components/plugin/PluginNewThreadComposer.test.tsx @@ -244,16 +244,6 @@ vi.mock("@/hooks/queries/sidebar-navigation-query", () => ({ }, })); -vi.mock("@/components/onboarding/SetupChecklistHost", () => ({ - SetupChecklistBanner: () => null, - hasSetupChecklistBanner: () => false, - useSetupChecklist: () => ({ - items: null, - agentMissing: false, - open: () => {}, - dismiss: () => {}, - }), -})); vi.mock("@/hooks/queries/host-queries", () => ({ useHosts: () => ({ data: [ diff --git a/apps/app/src/views/RootComposeView.tsx b/apps/app/src/views/RootComposeView.tsx index aabaf9ef002..8875f93d611 100644 --- a/apps/app/src/views/RootComposeView.tsx +++ b/apps/app/src/views/RootComposeView.tsx @@ -28,11 +28,6 @@ import { type NewThreadComposerState, type NewThreadComposerSubmission, } from "@/components/promptbox/NewThreadComposer"; -import { - SetupChecklistBanner, - hasSetupChecklistBanner, - useSetupChecklist, -} from "@/components/onboarding/SetupChecklistHost"; import { ProviderCliBanner, providerCliBlockedReason, @@ -1427,12 +1422,9 @@ function RootComposeSurface({ }, [parsedEnvironment, setEnvironmentSelectionValue], ); - const setupChecklist = useSetupChecklist(); const promptBanner = useMemo(() => { if (blockingProviderCliStatus === null) { - return hasSetupChecklistBanner(setupChecklist) ? ( - - ) : null; + return null; } return ( { generalSettings: { ...defaultAppSettings, onboardingCompletedAt: "2026-10-01T00:00:00.000Z", - setupChecklistVisible: true, streamerMode: true, }, })), @@ -36,7 +35,6 @@ describe("bb settings commands", () => { json: { ...defaultAppSettings, onboardingCompletedAt: null, - setupChecklistVisible: true, streamerMode: true, }, }); diff --git a/apps/server/src/routes/system.ts b/apps/server/src/routes/system.ts index 60dbbdc9115..515aad3b6eb 100644 --- a/apps/server/src/routes/system.ts +++ b/apps/server/src/routes/system.ts @@ -320,8 +320,6 @@ export function registerSystemRoutes( settings.onboardingCompletedAt === undefined ? current.onboardingCompletedAt : settings.onboardingCompletedAt, - setupChecklistVisible: - settings.setupChecklistVisible ?? current.setupChecklistVisible, showDiagnosticEvents: diagnosticValue === undefined || (showUnhandledProviderEvents !== undefined && diff --git a/docs/configuration.md b/docs/configuration.md index 8adb86690d7..587a1baebaf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -414,8 +414,7 @@ A new install opens a first-run setup guide (connect an agent, add projects, pick plugins, set up devices). `onboardingCompletedAt` in general settings records when it was finished or skipped; `bb settings replay-onboarding`, or Settings → General → Setup guide, clears it so the guide shows again. -`setupChecklistVisible` controls the "Finish setting up bb" home-screen -checklist. The projects step lists what `bb project discover` and +The projects step lists what `bb project discover` and `bb.sdk.hosts.experimental_discoverRepos({ hostId })` return. The "Streamer mode" toggle in Settings → General hides every `customModels` diff --git a/packages/db/test/migrate.test.ts b/packages/db/test/migrate.test.ts index 51a451a9aad..ac349cbf7da 100644 --- a/packages/db/test/migrate.test.ts +++ b/packages/db/test/migrate.test.ts @@ -1792,7 +1792,6 @@ describe("migrate", () => { telemetryEnabled: true, managedBranchPrefix: "bb/", onboardingCompletedAt: "2026-08-01T00:00:00.000Z", - setupChecklistVisible: false, }); expect( db.$client diff --git a/packages/domain/src/app-settings.ts b/packages/domain/src/app-settings.ts index bb9a9b0eb2c..294e49a8642 100644 --- a/packages/domain/src/app-settings.ts +++ b/packages/domain/src/app-settings.ts @@ -46,7 +46,6 @@ export const appSettingsSchema = z machineGitCredentialsEnabled: z.boolean(), defaultMachineAccess: z.string().min(1).nullable(), onboardingCompletedAt: z.string().min(1).nullable(), - setupChecklistVisible: z.boolean(), }) .strict(); export type AppSettings = z.infer; @@ -69,7 +68,6 @@ export const defaultAppSettings: AppSettings = { defaultMachineAccess: null, machineGitCredentialsEnabled: true, onboardingCompletedAt: null, - setupChecklistVisible: false, }; export const disabledProviderIdsSchema = z.array(z.string().min(1)); @@ -83,7 +81,6 @@ export const appSettingsUpdateSchema = z.union([ keepHistoryAfterContextClear: z.boolean().optional(), showUnhandledProviderEvents: z.boolean().optional(), onboardingCompletedAt: z.string().min(1).nullable().optional(), - setupChecklistVisible: z.boolean().optional(), }), appSettingsSchema.omit({ showDiagnosticEvents: true }).extend({ allowFastServiceTier: z.boolean().optional(), @@ -93,7 +90,6 @@ export const appSettingsUpdateSchema = z.union([ keepHistoryAfterContextClear: z.boolean().optional(), showUnhandledProviderEvents: z.boolean(), onboardingCompletedAt: z.string().min(1).nullable().optional(), - setupChecklistVisible: z.boolean().optional(), }), ]); export type AppSettingsUpdate = z.infer; diff --git a/packages/templates/src/templates/bb-guide-customization.md b/packages/templates/src/templates/bb-guide-customization.md index 1825dde63e6..b0a93ef3a5e 100644 --- a/packages/templates/src/templates/bb-guide-customization.md +++ b/packages/templates/src/templates/bb-guide-customization.md @@ -217,9 +217,7 @@ and `null` clears a preference that can be unset. `bb settings replay-onboarding` clears `onboardingCompletedAt`, so the first-run setup guide (connect an agent, add projects, pick plugins, set up devices) shows again in every open client. Settings → General → Setup guide has the same -button. `setupChecklistVisible` controls the "Finish setting up bb" checklist -on the home screen; the guide turns it on when steps are left undone, and -dismissing the checklist turns it off. +button. `bb settings completed-turns` lists how each provider shows a finished turn: `collapse` folds the turn's work into one "Worked for" row and keeps the final diff --git a/plugins/bb-guide/skills/bb-cli/references/app-settings.md b/plugins/bb-guide/skills/bb-cli/references/app-settings.md index 15bd996415b..b3746f31ab7 100644 --- a/plugins/bb-guide/skills/bb-cli/references/app-settings.md +++ b/plugins/bb-guide/skills/bb-cli/references/app-settings.md @@ -19,9 +19,6 @@ every window and client sees the same value. or skipped; `null` means the guide is showing. - `bb settings replay-onboarding` clears it. Settings → General → Setup guide has the same button. -- `setupChecklistVisible` shows the "Finish setting up bb" checklist on the - home screen. Turn it off with - `bb settings general setupChecklistVisible false`. - `bb project discover [--machine ]` lists the git repositories the guide offers to import; add one with `bb project create --name --root `.