From 80786ddccbe8caadea342e73b7b603518cc7d8eb Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:55:17 -0400 Subject: [PATCH] fix(onboarding): build the agent step around skills, not CLI registration (#22720) * fix(onboarding): build the agent step around skills, not CLI registration The checklist step "Enable Orca CLI" was marked done once the agent skills were installed, while Settings -> Browser still showed CLI registration as a pending step. Orca terminals already put the bundled CLI on PATH, so registration only matters for shells Orca did not launch, plus WSL, where `orca-ide` exists only once registered. - Rename the step to "Give agents Orca skills"; setup registers the CLI only for WSL (isOrcaCliRegistrationRequired), via onboarding-cli-registration.ts. - Settings -> Browser drops the CLI step outside WSL (2 steps instead of 3). - Skills panel: "All skills installed" + "Update skills" replaces the disabled button; status pills sit top-right; no "Installed" beside "Unavailable". - Full Disk Access moves to the "Start work in multiple repos" step. Fixes STA-8306 / #22524. * refactor(onboarding): simplify agent-skill step state after review - One done rule: isAgentCapabilitiesDone in feature-wall-setup-progress.ts, reused by the skills panel instead of a mirrored copy. - 'unavailable' is an install-status tone instead of a second boolean; pill/note rendering moves to AgentCapabilityStatusBadges.tsx. - "Update skills" skips Computer Use when it can't run (no warning toast); setup takes an explicit selection. - The WSL gate lives in registerOnboardingCliIfRequired; onboarding deps drop the now-unreachable host CLI branches. - BrowserUsePane: one cliRequired/cliReady pair, no host CLI status fetch, WSL-only enable path, single "Finish the steps below." string. - Full Disk Access placement goes through a SelectedStepFooter switch. - Prune orphaned locale keys (and their boot-bundle entries); fix a stale comment. * fix(skills): require CLI registration only for WSL setup * fix(skills): retain CLI install labels for WSL * docs(skills): clarify remaining WSL registration fallback * fix(skills): skip WSL registration when the host confirms managed CLI access * fix(wsl): prepare managed shell wrappers before onboarding probes * test: update daemon capability and terminal hook expectations * refactor(onboarding): remove CLI registration checks from skill setup * refactor(setup): remove redundant state and obsolete registration scaffolding * fix(settings): stop registering the CLI before installing the CLI skill The General > Orca CLI skill panel still registered `orca` on PATH before opening the install terminal, contradicting the rest of skill setup. Orca terminals already provide the CLI, so the shell command toggle now says it is only for terminals outside Orca. * style(onboarding): polish the setup checklist and first-run steps Make onboarding monochrome: completion is a neutral check, selection a neutral outline, and color only flags real problems. Tighten the checklist rail and header, single-line agent cards with a grid that scrolls only when it runs out of room, a labeled permission switch under the grid, calmer notification and skill cards, sentence-case copy, and a labeled "Hide checklist from sidebar" action. Workspace setup leads with "Add project" when no git project exists. * fix(emulator): drop the Enable Orca CLI step from agent control setup Agents that drive the emulator run in Orca terminals, which already provide the `orca` command. Agent control setup in the emulator card and Settings is now a single step: install the Orca CLI skill. * fix(onboarding): hide the Full Disk Access card once access is granted A granted card has no remaining action and only takes space on the add projects step. It also no longer flashes a "Checking" state before the first status arrives. * fix(onboarding): address review on permission warning, hide button, and translations - Name the permission switch "Yolo mode" (matching Settings > Agents) and state the risk: agents act without asking and some bypass their sandbox. - Hide the modal's "Hide checklist from sidebar" button below sm, where the header centers its title under it; the sidebar entry keeps its own control. - Translate every string this PR adds into es, fr, ja, ko, and zh. --- ...r-cookie-import-google-disclosure.test.tsx | 2 +- .../MobileEmulatorAgentSetupGuide.tsx | 17 +- .../MobileEmulatorAgentSetupGuideSteps.tsx | 210 ++-------- ...ile-emulator-agent-setup-cli-state.test.ts | 87 ---- .../mobile-emulator-agent-setup-cli-state.ts | 29 -- .../use-mobile-emulator-agent-setup-state.ts | 160 +------ .../AgentCapabilitiesSetupAction.test.ts | 33 +- .../AgentCapabilitiesSetupAction.tsx | 104 ++--- .../AgentCapabilityStatusBadges.tsx | 49 +++ .../feature-wall/BrowserUseSkillSetupCard.tsx | 23 +- .../feature-wall/FeatureWallBrowserAction.tsx | 9 +- .../feature-wall/FeatureWallRail.tsx | 8 +- .../FeatureWallSetupChecklist.tsx | 111 +++-- .../FeatureWallSetupWorkflowActions.tsx | 27 +- .../FullDiskAccessSetupPrompt.test.ts | 11 +- .../FullDiskAccessSetupPrompt.tsx | 30 +- .../agent-capability-setup-status.ts | 30 +- .../feature-wall/connect-integration-step.tsx | 4 +- ...ure-wall-setup-checklist-localized-copy.ts | 12 +- .../feature-wall-setup-progress.ts | 27 +- .../FloatingTerminalOrchestrationDialog.tsx | 28 +- .../mac-notification-permission-card.tsx | 77 ++-- .../components/onboarding/AgentStep.test.tsx | 8 +- .../src/components/onboarding/AgentStep.tsx | 185 +++----- .../onboarding/GhosttyDiscoveryRow.tsx | 6 +- .../onboarding/NotificationStep.test.tsx | 4 +- .../onboarding/NotificationStep.tsx | 151 +++---- .../src/components/onboarding/ThemeStep.tsx | 8 +- .../onboarding-feature-setup.test.ts | 119 +----- .../onboarding/onboarding-feature-setup.ts | 65 +-- .../components/settings/BrowserUseCliStep.tsx | 96 ----- .../settings/BrowserUseCookieImportStep.tsx | 13 +- .../components/settings/BrowserUsePane.tsx | 162 +------ .../settings/BrowserUseSkillStep.tsx | 9 +- .../components/settings/CliSection.test.tsx | 50 +-- .../src/components/settings/CliSection.tsx | 31 +- .../settings/ComputerUseSkillSetupPanel.tsx | 23 +- .../components/settings/EphemeralVmsPane.tsx | 23 +- .../settings/LinearAgentSkillPane.tsx | 3 - .../MobileEmulatorAgentControlRow.test.tsx | 41 +- .../MobileEmulatorAgentControlRow.tsx | 114 +---- .../components/settings/OrchestrationPane.tsx | 23 +- .../settings/OrchestrationSetupCard.tsx | 23 +- .../settings/TaskSourceLinearSetup.tsx | 3 - .../components/settings/browser-use-search.ts | 23 - .../settings/use-linear-agent-skill-setup.ts | 37 +- .../setup-guide/SetupGuideModal.tsx | 57 +-- .../sidebar/LinearAgentSkillSetupDialog.tsx | 16 +- ...ntSkillSetupPrompt.reminder-toast.test.tsx | 110 ++--- .../LinearAgentSkillSetupPrompt.test.tsx | 395 +++++------------- .../sidebar/LinearAgentSkillSetupPrompt.tsx | 113 +---- ...ntSkillSetupPrompt.update-command.test.tsx | 81 ++-- .../sidebar/linear-agent-skill-setup-copy.ts | 55 +-- .../src/i18n/en-runtime-required.json | 1 + src/renderer/src/i18n/locales/en.json | 100 ++--- src/renderer/src/i18n/locales/es.json | 97 ++--- src/renderer/src/i18n/locales/fr.json | 94 +---- src/renderer/src/i18n/locales/ja.json | 94 +---- src/renderer/src/i18n/locales/ko.json | 94 +---- src/renderer/src/i18n/locales/zh.json | 94 +---- src/shared/feature-wall-setup-steps.ts | 10 +- 61 files changed, 976 insertions(+), 2743 deletions(-) delete mode 100644 src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.test.ts delete mode 100644 src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.ts create mode 100644 src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx delete mode 100644 src/renderer/src/components/settings/BrowserUseCliStep.tsx diff --git a/src/renderer/src/components/browser-cookie-import-google-disclosure.test.tsx b/src/renderer/src/components/browser-cookie-import-google-disclosure.test.tsx index ed8cc04c31f..f7bba3bfe9a 100644 --- a/src/renderer/src/components/browser-cookie-import-google-disclosure.test.tsx +++ b/src/renderer/src/components/browser-cookie-import-google-disclosure.test.tsx @@ -83,7 +83,7 @@ describe('cookie-import Google disclosure footer', () => { ) diff --git a/src/renderer/src/components/emulator-pane/MobileEmulatorAgentSetupGuide.tsx b/src/renderer/src/components/emulator-pane/MobileEmulatorAgentSetupGuide.tsx index df764f8f69d..d99f5713e3d 100644 --- a/src/renderer/src/components/emulator-pane/MobileEmulatorAgentSetupGuide.tsx +++ b/src/renderer/src/components/emulator-pane/MobileEmulatorAgentSetupGuide.tsx @@ -1,6 +1,5 @@ import { useState } from 'react' import { ChevronDown, ChevronUp } from 'lucide-react' -import { cn } from '@/lib/utils' import { useAppStore } from '@/store' import { Button } from '../ui/button' import { MobileEmulatorAgentSetupGuideSteps } from './MobileEmulatorAgentSetupGuideSteps' @@ -59,8 +58,8 @@ export function MobileEmulatorAgentSetupGuide({ )}{' '} {translate( - 'auto.components.emulator.pane.MobileEmulatorAgentSetupGuide.72736b051f', - 'Set up Orca CLI + skill when you want agents to drive this simulator.' + 'auto.components.emulator.pane.MobileEmulatorAgentSetupGuide.installSkillPrompt', + 'Install the Orca CLI skill when you want agents to drive this simulator.' )} )} @@ -120,18 +119,6 @@ export function MobileEmulatorAgentSetupGuide({ {expanded && !setup.setupComplete ? (
-
- - {setup.completedCount}/2 - -
- - - {!setup.cliSupported && !setup.cliLoading && setup.cliInstallStatus?.detail ? ( - - {setup.cliInstallStatus.detail} - - ) : null} - - -
- -
-
- -
-
-

- {translate( - 'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07', - 'Orca CLI skill' - )} -

- { - recordFeatureInteraction('mobile-emulator-agent-setup') - await ensureOrcaCliAvailableForAgentSkillTerminal() - }} - onRecheck={() => { - recordFeatureInteraction('mobile-emulator-agent-setup') - void setup.recheckSetup() - }} - /> -
-
+
+

+ {translate( + 'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07', + 'Orca CLI skill' + )} +

+ { + recordFeatureInteraction('mobile-emulator-agent-setup') + }} + onRecheck={() => { + recordFeatureInteraction('mobile-emulator-agent-setup') + void setup.recheckSetup() + }} + />
) } diff --git a/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.test.ts b/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.test.ts deleted file mode 100644 index 2bb986b6291..00000000000 --- a/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { CliInstallStatus } from '../../../../shared/cli-install-types' -import { - getMobileEmulatorCliPathNeedsAttention, - getMobileEmulatorCliStepBadgeState, - shouldShowMobileEmulatorSkillPreInstallNotice -} from './mobile-emulator-agent-setup-cli-state' - -function cliStatus(overrides: Partial = {}): CliInstallStatus { - return { - platform: 'darwin', - commandName: 'orca', - commandPath: '/usr/local/bin/orca', - pathDirectory: '/usr/local/bin', - pathConfigured: true, - launcherPath: '/Applications/Orca.app/Contents/MacOS/orca', - installMethod: 'symlink', - supported: true, - state: 'installed', - currentTarget: null, - unsupportedReason: null, - detail: null, - ...overrides - } -} - -describe('getMobileEmulatorCliPathNeedsAttention', () => { - it('flags installed CLIs that are not visible on PATH yet', () => { - expect(getMobileEmulatorCliPathNeedsAttention(cliStatus({ pathConfigured: false }))).toBe(true) - expect(getMobileEmulatorCliPathNeedsAttention(cliStatus())).toBe(false) - expect(getMobileEmulatorCliPathNeedsAttention(cliStatus({ state: 'not_installed' }))).toBe( - false - ) - }) -}) - -describe('getMobileEmulatorCliStepBadgeState', () => { - it('marks enabled CLIs as done', () => { - expect( - getMobileEmulatorCliStepBadgeState({ - cliBusy: false, - cliEnabled: true, - cliPathNeedsAttention: false - }) - ).toBe('done') - }) - - it('marks PATH-fix and registration flows as in progress', () => { - expect( - getMobileEmulatorCliStepBadgeState({ - cliBusy: true, - cliEnabled: false, - cliPathNeedsAttention: false - }) - ).toBe('in-progress') - expect( - getMobileEmulatorCliStepBadgeState({ - cliBusy: false, - cliEnabled: false, - cliPathNeedsAttention: true - }) - ).toBe('in-progress') - }) -}) - -describe('shouldShowMobileEmulatorSkillPreInstallNotice', () => { - it('hides the prereq notice once either step is already complete', () => { - expect( - shouldShowMobileEmulatorSkillPreInstallNotice({ - cliEnabled: true, - cliSkillInstalled: false - }) - ).toBe(false) - expect( - shouldShowMobileEmulatorSkillPreInstallNotice({ - cliEnabled: false, - cliSkillInstalled: true - }) - ).toBe(false) - expect( - shouldShowMobileEmulatorSkillPreInstallNotice({ - cliEnabled: false, - cliSkillInstalled: false - }) - ).toBe(true) - }) -}) diff --git a/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.ts b/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.ts deleted file mode 100644 index 4e350eaf0a4..00000000000 --- a/src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { CliInstallStatus } from '../../../../shared/cli-install-types' -import type { StepState } from '../settings/SetupStepBadge' - -export function getMobileEmulatorCliPathNeedsAttention(status: CliInstallStatus | null): boolean { - return status?.state === 'installed' && status.pathConfigured === false -} - -export function getMobileEmulatorCliStepBadgeState(input: { - cliBusy: boolean - cliEnabled: boolean - cliPathNeedsAttention: boolean -}): StepState { - if (input.cliEnabled) { - return 'done' - } - if (input.cliBusy || input.cliPathNeedsAttention) { - return 'in-progress' - } - return 'pending' -} - -export function shouldShowMobileEmulatorSkillPreInstallNotice(input: { - cliEnabled: boolean - cliSkillInstalled: boolean -}): boolean { - // Why: an installed skill should not reopen with "Install" just because CLI - // probes are stale; only gate first-time setup on CLI availability. - return !input.cliSkillInstalled && !input.cliEnabled -} diff --git a/src/renderer/src/components/emulator-pane/use-mobile-emulator-agent-setup-state.ts b/src/renderer/src/components/emulator-pane/use-mobile-emulator-agent-setup-state.ts index 2970bdad007..dae1b70f0e4 100644 --- a/src/renderer/src/components/emulator-pane/use-mobile-emulator-agent-setup-state.ts +++ b/src/renderer/src/components/emulator-pane/use-mobile-emulator-agent-setup-state.ts @@ -1,67 +1,24 @@ import { useCallback, useEffect, useState } from 'react' import { toast } from 'sonner' -import type { CliInstallStatus } from '../../../../shared/cli-install-types' import { ORCA_CLI_SKILL_NAME } from '@/lib/agent-feature-install-commands' -import { - ensureOrcaCliAvailableForAgentSkillTerminal, - isOrcaCliAvailableOnPath -} from '@/lib/agent-skill-cli-prerequisite' import { GLOBAL_AGENT_SKILL_SOURCE_KINDS, useInstalledAgentSkill } from '@/hooks/useInstalledAgentSkills' import { useMountedRef } from '@/hooks/useMountedRef' -import { getMobileEmulatorCliPathNeedsAttention } from './mobile-emulator-agent-setup-cli-state' import { translate } from '@/i18n/i18n' -function getCliActionLabel(status: CliInstallStatus | null, busy: boolean): string { - if (busy) { - return translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.fdcca1ec75', - 'Registering...' - ) - } - if (isOrcaCliAvailableOnPath(status)) { - return translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.69fb2c2289', - 'Enabled' - ) - } - if (status?.state === 'installed') { - return translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.c6705092ba', - 'Fix PATH' - ) - } - return translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.7c1b6bdb1e', - 'Enable' - ) -} - +// Why: Orca terminals already provide the `orca` command, so agent control only needs the skill. export function useMobileEmulatorAgentSetupState(enabled = true): { - cliActionLabel: string - cliBusy: boolean - cliEnabled: boolean - cliInstallStatus: CliInstallStatus | null - cliPathNeedsAttention: boolean - cliLoading: boolean cliSkillError: string | null cliSkillInstalled: boolean cliSkillLoading: boolean - cliSupported: boolean - completedCount: number - handleEnableCli: () => Promise recheckSetup: () => Promise refreshCliSkill: () => Promise setupComplete: boolean setupRechecking: boolean statusReady: boolean - step2Blocked: boolean } { - const [cliInstallStatus, setCliInstallStatus] = useState(null) - const [cliLoading, setCliLoading] = useState(true) - const [cliBusy, setCliBusy] = useState(false) const [setupRechecking, setSetupRechecking] = useState(false) const mountedRef = useMountedRef() const { @@ -74,60 +31,17 @@ export function useMobileEmulatorAgentSetupState(enabled = true): { sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS }) - const refreshCliStatus = useCallback(async (): Promise => { - setCliLoading(true) - try { - const status = await window.api.cli.getInstallStatus() - if (mountedRef.current) { - setCliInstallStatus(status) - } - } catch (error) { - if (mountedRef.current) { - toast.error( - error instanceof Error - ? error.message - : translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.51074ccb05', - 'Failed to load CLI status.' - ) - ) - setCliInstallStatus(null) - } - } finally { - if (mountedRef.current) { - setCliLoading(false) - } - } - }, [mountedRef]) - useEffect(() => { if (!enabled) { return } - void refreshCliStatus() - }, [enabled, refreshCliStatus]) - - useEffect(() => { - if (!enabled) { - return - } - // Why: users often register the CLI from Settings first; refresh on focus so - // the emulator guide reflects the latest install/PATH state. + // Why: users often install the skill from Settings or a terminal; refresh on focus. const handleFocus = (): void => { - void refreshCliStatus() void refreshCliSkill() } window.addEventListener('focus', handleFocus) return () => window.removeEventListener('focus', handleFocus) - }, [enabled, refreshCliSkill, refreshCliStatus]) - - const cliEnabled = isOrcaCliAvailableOnPath(cliInstallStatus) - const cliPathNeedsAttention = getMobileEmulatorCliPathNeedsAttention(cliInstallStatus) - const cliSupported = cliInstallStatus?.supported ?? false - const completedCount = [cliEnabled, cliSkillInstalled].filter(Boolean).length - const step2Blocked = !cliEnabled && !cliSkillInstalled - const setupComplete = cliEnabled && cliSkillInstalled - const statusReady = !cliLoading && !cliSkillLoading + }, [enabled, refreshCliSkill]) const recheckSetup = useCallback(async (): Promise => { if (setupRechecking) { @@ -135,18 +49,11 @@ export function useMobileEmulatorAgentSetupState(enabled = true): { } setSetupRechecking(true) try { - const [cliStatus, skillInstalled] = await Promise.all([ - window.api.cli.getInstallStatus(), - refreshCliSkill() - ]) - if (mountedRef.current) { - setCliInstallStatus(cliStatus) - } - const cliReady = isOrcaCliAvailableOnPath(cliStatus) + const skillInstalled = await refreshCliSkill() if (!mountedRef.current) { return } - if (cliReady && skillInstalled) { + if (skillInstalled) { toast.success( translate( 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.35dea1ae12', @@ -155,28 +62,10 @@ export function useMobileEmulatorAgentSetupState(enabled = true): { ) return } - if (skillInstalled) { - toast.message( - translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.9dff3a6338', - 'Skill is installed. Enable the Orca CLI to finish setup.' - ) - ) - return - } - if (cliReady) { - toast.message( - translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.15986a1080', - 'Orca CLI is ready. Install the skill to finish setup.' - ) - ) - return - } toast.message( translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.4c26913def', - 'Still not set up. Complete both steps to enable agent control.' + 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.skillNotInstalled', + 'The Orca CLI skill is not installed yet.' ) ) } catch (error) { @@ -197,45 +86,14 @@ export function useMobileEmulatorAgentSetupState(enabled = true): { } }, [mountedRef, refreshCliSkill, setupRechecking]) - const handleEnableCli = useCallback(async (): Promise => { - setCliBusy(true) - try { - const next = await ensureOrcaCliAvailableForAgentSkillTerminal({ - onStatusChange: setCliInstallStatus - }) - if (mountedRef.current && isOrcaCliAvailableOnPath(next)) { - toast.success( - translate( - 'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.2b519eed94', - 'Registered the Orca CLI in PATH.' - ) - ) - } - } finally { - if (mountedRef.current) { - setCliBusy(false) - } - } - }, [mountedRef]) - return { - cliActionLabel: getCliActionLabel(cliInstallStatus, cliBusy), - cliBusy, - cliEnabled, - cliInstallStatus, - cliPathNeedsAttention, - cliLoading, cliSkillError, cliSkillInstalled, cliSkillLoading, - cliSupported, - completedCount, - handleEnableCli, recheckSetup, refreshCliSkill, - setupComplete, + setupComplete: cliSkillInstalled, setupRechecking, - statusReady, - step2Blocked + statusReady: !cliSkillLoading } } diff --git a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.test.ts b/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.test.ts index a321f57a2ae..4f5aa79a15c 100644 --- a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.test.ts +++ b/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from 'vitest' -import { getDefaultAgentCapabilitySetupSelection } from './agent-capability-setup-status' +import { + getDefaultAgentCapabilitySetupSelection, + isAgentCapabilityReadinessComplete +} from './agent-capability-setup-status' const READY_INPUT = { browserUseSkillInstalled: true, @@ -67,3 +70,31 @@ describe('getDefaultAgentCapabilitySetupSelection', () => { }) }) }) + +describe('isAgentCapabilityReadinessComplete', () => { + it('is complete when every skill is ready, or Computer Use cannot run here', () => { + expect(isAgentCapabilityReadinessComplete(READY_INPUT)).toBe(true) + expect( + isAgentCapabilityReadinessComplete({ + ...READY_INPUT, + computerUseReady: false, + computerUseUnavailable: true + }) + ).toBe(true) + }) + + it('is incomplete while Computer Use still needs macOS access or a skill is missing', () => { + expect(isAgentCapabilityReadinessComplete({ ...READY_INPUT, computerUseReady: false })).toBe( + false + ) + expect( + isAgentCapabilityReadinessComplete({ ...READY_INPUT, orchestrationSkillInstalled: false }) + ).toBe(false) + }) + + it('does not claim completion while install probes are still running', () => { + expect( + isAgentCapabilityReadinessComplete({ ...READY_INPUT, browserUseSkillLoading: true }) + ).toBe(false) + }) +}) diff --git a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx b/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx index 7ba461093a8..52a54a208f4 100644 --- a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx +++ b/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx @@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { useAppStore } from '@/store' +import { AgentCapabilityStatusNote, AgentCapabilityStatusPill } from './AgentCapabilityStatusBadges' import { FeatureSetupInlineTerminal } from '../onboarding/FeatureSetupInlineTerminal' import type { OnboardingFeatureSetupRuntimeContext } from '../onboarding/onboarding-feature-setup-runtime' import { @@ -15,13 +16,12 @@ import { type OnboardingFeatureSetupSelection } from '../onboarding/onboarding-feature-setup' import { - getAgentCapabilityStatusClassName, getDefaultAgentCapabilitySetupSelection, isAgentCapabilityReadinessChecking, + isAgentCapabilityReadinessComplete, useAgentCapabilitySetupStatus, type AgentCapabilityInstallStatus } from './agent-capability-setup-status' -import { FullDiskAccessSetupPrompt } from './FullDiskAccessSetupPrompt' import { translate } from '@/i18n/i18n' export function AgentCapabilitiesSetupAction(props: { @@ -140,6 +140,7 @@ export function AgentCapabilitiesSetupAction(props: { featureSetupRuntime={featureSetupRuntime} setupBusyLabel={setupBusyLabel} onStartFeatureSetup={() => void handleStartFeatureSetup()} + allReady={isAgentCapabilityReadinessComplete(readiness)} installStatus={capabilitySetupStatus.installStatus} />
@@ -212,10 +213,13 @@ function AgentCapabilitySetupControls(props: { featureSetupRuntime: OnboardingFeatureSetupRuntimeContext | null setupBusyLabel: string | null onStartFeatureSetup: () => void + allReady: boolean installStatus: Record }): React.JSX.Element { const hasSelectedFeatures = hasSelectedOnboardingFeatureSetup(props.featureSetup) const showSetupAction = !props.featureSetupCommand + // Why: a disabled install button is noise once everything is set up. + const showAllReady = props.allReady && !hasSelectedFeatures && !props.setupBusyLabel return ( <> @@ -224,8 +228,15 @@ function AgentCapabilitySetupControls(props: { onChange={props.onFeatureSetupChange} installStatus={props.installStatus} /> - - {showSetupAction ? ( + {showSetupAction && showAllReady ? ( +

+ + {translate( + 'auto.components.feature.wall.AgentCapabilitiesSetupAction.allInstalled', + 'All skills installed' + )} +

+ ) : showSetupAction ? (
@@ -277,35 +288,32 @@ function AgentCapabilitySetupChecklist(props: { aria-checked={selected} aria-label={`${selected ? 'Disable' : 'Enable'} ${row.title}`} className={cn( - 'flex min-h-24 flex-col rounded-lg border px-4 py-3 text-left transition-colors', - 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', - selected - ? 'border-ring bg-accent text-foreground ring-2 ring-ring/25' - : 'border-border bg-muted/20 text-muted-foreground hover:bg-muted/40' + 'flex min-h-24 flex-col rounded-lg border px-4 py-3.5 text-left transition-colors', + 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', + selected ? 'border-foreground/60 bg-accent' : 'border-border hover:bg-accent/60' )} onClick={() => props.onChange({ ...props.value, [row.id]: !selected })} > - + {row.icon} - - {selected ? : null} + + + {/* Why: an empty circle on an installed card reads as "not done"; show it only when it means something. */} + {selected || !installStatus.installed ? ( + + {selected ? : null} + + ) : null} {row.title} @@ -320,41 +328,3 @@ function AgentCapabilitySetupChecklist(props: { ) } - -function AgentCapabilityStatusNote(props: { - status: AgentCapabilityInstallStatus -}): React.JSX.Element { - if (props.status.installed) { - return ( - - - {translate( - 'auto.components.feature.wall.AgentCapabilitiesSetupAction.b8dc9dd8a2', - 'Installed' - )} - - {props.status.tone !== 'ready' ? ( - - {props.status.label} - - ) : null} - - ) - } - - return ( - - {props.status.label} - - ) -} diff --git a/src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx b/src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx new file mode 100644 index 00000000000..f07c666c69d --- /dev/null +++ b/src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx @@ -0,0 +1,49 @@ +import { Check } from 'lucide-react' +import { cn } from '@/lib/utils' +import { translate } from '@/i18n/i18n' +import { + getAgentCapabilityStatusClassName, + type AgentCapabilityInstallStatus +} from './agent-capability-setup-status' + +// Why: pills sit top-right so they line up across cards regardless of description length. +export function AgentCapabilityStatusPill(props: { + status: AgentCapabilityInstallStatus +}): React.JSX.Element | null { + if (props.status.tone === 'unavailable') { + return ( + {props.status.label} + ) + } + if (!props.status.installed) { + return null + } + return ( + + + {translate( + 'auto.components.feature.wall.AgentCapabilitiesSetupAction.b8dc9dd8a2', + 'Installed' + )} + + ) +} + +/** Secondary status text (checking, errors, pending actions); installed/unavailable live in the pill. */ +export function AgentCapabilityStatusNote(props: { + status: AgentCapabilityInstallStatus +}): React.JSX.Element | null { + if (props.status.tone === 'ready' || props.status.tone === 'unavailable') { + return null + } + return ( + + {props.status.label} + + ) +} diff --git a/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx b/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx index 8edf52ec9a8..b31e007e1fb 100644 --- a/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx +++ b/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx @@ -4,19 +4,11 @@ import { ORCA_CLI_SKILL_NAME, ORCA_CLI_SKILL_UPDATE_COMMAND } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state' import type { InstalledAgentSkillState } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { AgentSkillSetupPanel } from '@/components/settings/AgentSkillSetupPanel' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from '@/components/settings/CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from '@/components/settings/CliSkillRuntimeSetup' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' @@ -34,11 +26,8 @@ export function BrowserUseSkillSetupCard(props: { ? buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND, activeSkillRuntime.agentRuntime) : ORCA_CLI_SKILL_UPDATE_COMMAND - const handleBeforeOpenTerminal = async (): Promise => { + const handleBeforeOpenTerminal = (): void => { useAppStore.getState().recordFeatureInteraction('agent-browser-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) localStorage.setItem(BROWSER_USE_ENABLED_STORAGE_KEY, '1') } @@ -65,14 +54,6 @@ export function BrowserUseSkillSetupCard(props: { error={activeSkillRuntime.installDisabledReason ?? skill.error} installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} terminalHeightPx={terminalHeightPx} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } onBeforeOpenTerminal={handleBeforeOpenTerminal} showRecheckWhenInstalled={false} onRecheck={skill.refresh} diff --git a/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx b/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx index 4567f75932e..c84dcc60a52 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx +++ b/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx @@ -89,8 +89,8 @@ const BROWSER_ONLY_FEATURE_SETUP: OnboardingFeatureSetupSelection = { linearTickets: false } -// The grab→agent flow relies on the Orca CLI and browser skill, so offer the same -// install action the Enable Orca CLI step uses, scoped to just browser use. +// The grab→agent flow relies on the browser skill, so offer the same install action +// the agent skills step uses, scoped to just browser use. function BrowserSkillInstallButton(): React.JSX.Element { const recordFeatureInteraction = useAppStore((s) => s.recordFeatureInteraction) const [command, setCommand] = useState(null) @@ -186,10 +186,7 @@ function BrowserSkillInstallButton(): React.JSX.Element { 'auto.components.feature.wall.FeatureWallBrowserAction.5f97caf76b', 'Installing…' ) - : translate( - 'auto.components.feature.wall.FeatureWallBrowserAction.c2df599513', - 'Install CLI & Skill' - )} + : translate('auto.components.skills.SkillInstallDialog.39acb9e8f4', 'Install skill')} ) } diff --git a/src/renderer/src/components/feature-wall/FeatureWallRail.tsx b/src/renderer/src/components/feature-wall/FeatureWallRail.tsx index e3224d8ed5d..8c19083acc5 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallRail.tsx +++ b/src/renderer/src/components/feature-wall/FeatureWallRail.tsx @@ -109,9 +109,9 @@ export function FeatureWallRail(props: { > void - layout: FeatureWallSetupChecklistLayout }): React.JSX.Element { - const { step, done, active, ordinal, onSelect, layout } = props - const isEmbedded = layout === 'embedded' + const { step, done, active, ordinal, onSelect } = props const localizedStepCopy = getLocalizedFeatureWallSetupChecklistCopy(step) return ( ) @@ -102,20 +99,19 @@ function SetupSection(props: { activeStepId: FeatureWallSetupStepId | null progress: FeatureWallSetupProgress onSelectStep: (id: FeatureWallSetupStepId) => void - layout: FeatureWallSetupChecklistLayout }): React.JSX.Element { const doneCount = props.steps.filter((step) => props.progress.stepDone[step.id]).length return (
-

+

{props.title}

- + {doneCount}/{props.steps.length}
-
+
{props.steps.map((step, index) => ( props.onSelectStep(step.id)} /> ))} @@ -170,6 +165,15 @@ function SelectedStepAction(props: FeatureWallSetupChecklistProps): React.JSX.El return null } +// Full-width content below the caption/visual grid. +function SelectedStepFooter(props: { stepId: FeatureWallSetupStepId }): React.JSX.Element | null { + // Why: Full Disk Access matters for projects in protected folders, so it sits with adding projects. + if (props.stepId === 'add-two-repos') { + return + } + return null +} + function SelectedStepVisual(props: { stepId: FeatureWallSetupStepId }): React.JSX.Element | null { if (props.stepId === 'two-worktrees') { return @@ -299,7 +303,6 @@ export function FeatureWallSetupChecklist( activeStepId={activeStep?.id ?? null} progress={progress} onSelectStep={onSelectStep} - layout={layout} />
@@ -324,31 +326,21 @@ export function FeatureWallSetupChecklist( )} > {activeStep ? ( -
-
-
-
- {getLocalizedFeatureWallSetupChecklistCopy(activeStep).name} -
-
- - {activeDone - ? translate( - 'auto.components.feature.wall.FeatureWallSetupChecklist.13294d3405', - 'Done' - ) - : translate( - 'auto.components.feature.wall.FeatureWallSetupChecklist.0235b268b2', - 'Not done yet' - )} - +
+
+

+ {getLocalizedFeatureWallSetupChecklistCopy(activeStep).name} +

+ {/* Why: a "not done" pill on every open step is noise; only completion is news. */} + {activeDone ? ( + + + {translate( + 'auto.components.feature.wall.FeatureWallSetupChecklist.13294d3405', + 'Done' + )} + + ) : null}

@@ -368,12 +360,13 @@ export function FeatureWallSetupChecklist(

{/* Action lives under the caption, not after the grid, so it sits just below the copy instead of being pushed down by the taller visual. */} -
+
+
) : null}
diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx b/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx index 2649029c9b5..268b7dfd567 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx +++ b/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx @@ -161,12 +161,26 @@ export function SetupScriptAction(): React.JSX.Element { } }, [repo, setupScript, updateRepo]) + // Why: disabled inputs with the prerequisite in fine print read as broken; lead with the fix. + if (!canConfigure) { + return ( +
+

+ {translate( + 'auto.components.feature.wall.FeatureWallSetupWorkflowActions.486c2f4d8d', + 'Add a git project first, then configure the setup script for that repository.' + )} +

+ +
+ ) + } + return (
setSetupScript(event.target.value)} placeholder={translate( 'auto.components.feature.wall.FeatureWallSetupWorkflowActions.5c5b65044e', @@ -182,7 +196,7 @@ export function SetupScriptAction(): React.JSX.Element { type="button" size="sm" className="gap-2" - disabled={!canConfigure || setupScript.trim().length === 0} + disabled={setupScript.trim().length === 0} onClick={() => void handleSaveSetupScript()} > @@ -197,7 +211,6 @@ export function SetupScriptAction(): React.JSX.Element { variant="ghost" size="sm" className="w-fit gap-2 px-0 text-muted-foreground hover:bg-transparent hover:text-foreground" - disabled={!canConfigure} onClick={openLocalCommandSettings} > @@ -206,14 +219,6 @@ export function SetupScriptAction(): React.JSX.Element { 'View in settings' )} - {!canConfigure ? ( -

- {translate( - 'auto.components.feature.wall.FeatureWallSetupWorkflowActions.486c2f4d8d', - 'Add a git project first, then configure the setup script for that repository.' - )} -

- ) : null}
) } diff --git a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts b/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts index 6e22269f19c..ff4bf3b75b6 100644 --- a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts +++ b/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts @@ -80,10 +80,11 @@ describe('FullDiskAccessSetupPrompt state helpers', () => { expect(isFullDiskAccessSetupVisible('unsupported')).toBe(false) }) - it('shows the setup prompt for macOS statuses users can act on', () => { + it('shows the setup prompt only for macOS statuses users can act on', () => { expect(isFullDiskAccessSetupVisible('unknown')).toBe(true) expect(isFullDiskAccessSetupVisible('denied')).toBe(true) - expect(isFullDiskAccessSetupVisible('granted')).toBe(true) + expect(isFullDiskAccessSetupVisible('granted')).toBe(false) + expect(isFullDiskAccessSetupVisible('ready')).toBe(false) }) it('treats granted and entitled statuses as ready', () => { @@ -109,7 +110,7 @@ describe('FullDiskAccessSetupPrompt state helpers', () => { }) expect(getStatus).toHaveBeenCalledTimes(2) - expect(container.textContent).toContain('Granted') + expect(container.textContent).not.toContain('Full Disk Access') root.unmount() }) @@ -130,14 +131,14 @@ describe('FullDiskAccessSetupPrompt state helpers', () => { window.dispatchEvent(new Event('focus')) await Promise.resolve() }) - expect(container.textContent).toContain('Granted') + expect(container.textContent).not.toContain('Full Disk Access') await act(async () => { resolveFirst([{ id: 'full-disk-access', status: 'unknown' }]) await Promise.resolve() }) - expect(container.textContent).toContain('Granted') + expect(container.textContent).not.toContain('Full Disk Access') root.unmount() }) diff --git a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx b/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx index 8cecfba9082..f11f8f0d063 100644 --- a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx +++ b/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react' -import { Check, ExternalLink, HardDrive, Loader2 } from 'lucide-react' +import { ExternalLink, HardDrive, Loader2 } from 'lucide-react' import { toast } from 'sonner' import type { DeveloperPermissionId, @@ -18,7 +18,6 @@ type FullDiskAccessStatusState = { } type FullDiskAccessButtonState = { - ready: boolean requesting: boolean } @@ -27,7 +26,8 @@ const FULL_DISK_ACCESS_PERMISSION_ID: DeveloperPermissionId = 'full-disk-access' export function isFullDiskAccessSetupVisible( status: DeveloperPermissionStatus | undefined ): boolean { - return status !== undefined && status !== 'unsupported' + // Why: once granted there is nothing left to do, so the card would only take space. + return status !== undefined && status !== 'unsupported' && !isFullDiskAccessReady(status) } export function isFullDiskAccessReady(status: DeveloperPermissionStatus | undefined): boolean { @@ -47,9 +47,6 @@ function getFullDiskAccessStatusLabel(args: FullDiskAccessStatusState): string { 'Checking' ) } - if (isFullDiskAccessReady(args.status)) { - return translate('auto.components.feature.wall.FullDiskAccessSetupPrompt.48d87edcd2', 'Granted') - } return translate( 'auto.components.feature.wall.FullDiskAccessSetupPrompt.6db9a69f4e', 'Recommended' @@ -63,9 +60,6 @@ function getFullDiskAccessButtonLabel(args: FullDiskAccessButtonState): string { 'Opening...' ) } - if (args.ready) { - return translate('auto.components.feature.wall.FullDiskAccessSetupPrompt.48d87edcd2', 'Granted') - } return translate( 'auto.components.feature.wall.FullDiskAccessSetupPrompt.6e3d62b816', 'Open Full Disk Access' @@ -76,9 +70,6 @@ function FullDiskAccessButtonIcon(props: FullDiskAccessButtonState): React.JSX.E if (props.requesting) { return } - if (props.ready) { - return - } return } @@ -151,8 +142,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null { const { checking, refresh, status } = useFullDiskAccessStatus() const mountedRef = useMountedRef() const [requesting, setRequesting] = useState(false) - const ready = isFullDiskAccessReady(status) - const visible = checking || isFullDiskAccessSetupVisible(status) + const visible = isFullDiskAccessSetupVisible(status) const handleOpenFullDiskAccess = useCallback(async (): Promise => { setRequesting(true) @@ -195,7 +185,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null { } return ( -
+
@@ -208,9 +198,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null { 'Full Disk Access' )} - - {getFullDiskAccessStatusLabel({ checking, status })} - + {getFullDiskAccessStatusLabel({ checking, status })}

{translate( @@ -225,11 +213,11 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null { variant="outline" size="sm" className="shrink-0 gap-1.5" - disabled={ready || requesting || checking} + disabled={requesting || checking} onClick={() => void handleOpenFullDiskAccess()} > - - {getFullDiskAccessButtonLabel({ ready, requesting })} + + {getFullDiskAccessButtonLabel({ requesting })}

) diff --git a/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts b/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts index 9f4fcb31da7..6fac8066a74 100644 --- a/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts +++ b/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts @@ -14,8 +14,14 @@ import { } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { translate } from '@/i18n/i18n' +import { isAgentCapabilitiesDone } from './feature-wall-setup-progress' -export type AgentCapabilityInstallStatusTone = 'ready' | 'pending' | 'checking' | 'error' +export type AgentCapabilityInstallStatusTone = + | 'ready' + | 'pending' + | 'checking' + | 'error' + | 'unavailable' export type AgentCapabilityInstallStatus = { label: string @@ -119,14 +125,18 @@ export function isAgentCapabilityReadinessChecking(readiness: AgentCapabilityRea ) } +export function isAgentCapabilityReadinessComplete(readiness: AgentCapabilityReadiness): boolean { + return !isAgentCapabilityReadinessChecking(readiness) && isAgentCapabilitiesDone(readiness) +} + export function getAgentCapabilityStatusClassName(tone: AgentCapabilityInstallStatusTone): string { switch (tone) { - case 'ready': - return 'text-green-600 dark:text-green-300' case 'error': return 'text-destructive' + case 'ready': case 'checking': case 'pending': + case 'unavailable': return 'text-muted-foreground' } } @@ -166,8 +176,8 @@ function getSkillInstallStatus(skill: { } return { label: translate( - 'auto.components.feature.wall.agent.capability.setup.status.aae94eeb52', - 'Click Install CLI & Skills' + 'auto.components.feature.wall.agent.capability.setup.status.notInstalled', + 'Not installed' ), tone: 'pending' } @@ -211,22 +221,22 @@ function getComputerUseInstallStatus( label: permissions.unavailableReason === 'web_client' ? translate( - 'auto.components.feature.wall.agent.capability.setup.status.4c8e1f92a7', - 'open Orca Desktop on this Mac' + 'auto.components.feature.wall.agent.capability.setup.status.requiresOrcaDesktop', + 'Requires Orca Desktop' ) : translate( 'auto.components.feature.wall.agent.capability.setup.status.6d2b0a84e1', 'Unavailable in this build' ), - tone: 'pending', + tone: 'unavailable', installed: true } } if (!permissions.ready) { return { label: translate( - 'auto.components.feature.wall.agent.capability.setup.status.21d4f79c93', - 'click Install CLI & Skills to open macOS access settings' + 'auto.components.feature.wall.agent.capability.setup.status.needsMacAccess', + 'Needs macOS access' ), tone: 'pending', installed: true diff --git a/src/renderer/src/components/feature-wall/connect-integration-step.tsx b/src/renderer/src/components/feature-wall/connect-integration-step.tsx index fa2c3e71b2e..1ef2b6700e1 100644 --- a/src/renderer/src/components/feature-wall/connect-integration-step.tsx +++ b/src/renderer/src/components/feature-wall/connect-integration-step.tsx @@ -51,9 +51,9 @@ export function IntegrationStep(props: { className={cn( 'flex size-7 shrink-0 items-center justify-center rounded-full border text-[13px] font-semibold leading-none', done - ? 'border-status-success-border bg-status-success-background text-status-success' + ? 'border-foreground bg-foreground text-background' : active - ? 'border-foreground bg-foreground text-background' + ? 'border-foreground text-foreground' : 'border-border text-muted-foreground' )} > diff --git a/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts b/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts index 316011b4a0f..a6d6ed6d880 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts +++ b/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts @@ -11,8 +11,8 @@ const getLocalizedFeatureWallSetupChecklistCopyById = createLocalizedCatalog( (): Record => ({ 'two-worktrees': { name: translate( - 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.ec0a363633', - 'Multi-task' + 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.workOnTwoTasks', + 'Work on two tasks at once' ), description: translate( 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.62bac8f43c', @@ -51,12 +51,12 @@ const getLocalizedFeatureWallSetupChecklistCopyById = createLocalizedCatalog( }, 'agent-capabilities': { name: translate( - 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.fee5557b02', - 'Enable Orca CLI' + 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.agentSkillsName', + 'Give agents Orca skills' ), description: translate( - 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.7bcb4097fa', - 'Register the Orca shell command and install agent skills for browser, computer, and orchestration workflows.' + 'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.agentSkillsDescription', + "Install the skills agents use to drive Orca's browser, control your computer, and coordinate multi-step work." ) }, 'task-sources': { diff --git a/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts b/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts index 585df18a9ac..e600ddbdaec 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts +++ b/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts @@ -42,14 +42,31 @@ function countAvailableNonMainWorktrees(worktreesByRepo: Record = { 'default-agent': Boolean(input.settings?.defaultTuiAgent) && input.settings?.defaultTuiAgent !== 'blank', diff --git a/src/renderer/src/components/floating-terminal/FloatingTerminalOrchestrationDialog.tsx b/src/renderer/src/components/floating-terminal/FloatingTerminalOrchestrationDialog.tsx index bc0540cda14..bcd43b3fbb0 100644 --- a/src/renderer/src/components/floating-terminal/FloatingTerminalOrchestrationDialog.tsx +++ b/src/renderer/src/components/floating-terminal/FloatingTerminalOrchestrationDialog.tsx @@ -10,10 +10,6 @@ import { AgentSkillSetupPanel } from '@/components/settings/AgentSkillSetupPanel import { IntegrationStatusPill } from '@/components/integration-status-pill' import { SkillFreshnessStatusPill } from '@/components/skills/SkillFreshnessStatusPill' import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { ORCHESTRATION_SKILL_INSTALL_COMMAND, ORCHESTRATION_SKILL_UPDATE_COMMAND @@ -25,11 +21,7 @@ import { import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { refreshSkillFreshness } from '@/hooks/useSkillFreshness' import { useAppStore } from '@/store' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from '@/components/settings/CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from '@/components/settings/CliSkillRuntimeSetup' import { translate } from '@/i18n/i18n' type FloatingTerminalOrchestrationDialogProps = { @@ -153,20 +145,12 @@ export function FloatingTerminalOrchestrationDialog({ installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} variant="inline" hideHeader - installLabel="Install CLI & skill" - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { + installLabel={translate( + 'auto.components.skills.SkillInstallDialog.39acb9e8f4', + 'Install skill' + )} + onBeforeOpenTerminal={() => { useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) }} onRecheck={recheckOrchestrationSkill} /> diff --git a/src/renderer/src/components/notifications/mac-notification-permission-card.tsx b/src/renderer/src/components/notifications/mac-notification-permission-card.tsx index fca7aab97d5..3e097848e9c 100644 --- a/src/renderer/src/components/notifications/mac-notification-permission-card.tsx +++ b/src/renderer/src/components/notifications/mac-notification-permission-card.tsx @@ -117,7 +117,7 @@ export function MacNotificationPermissionCard({ }): React.JSX.Element | null { if (state === 'checking') { return ( -
+
{translate( 'auto.components.onboarding.NotificationStep.56b836215c', 'Checking notification permission…' @@ -128,10 +128,10 @@ export function MacNotificationPermissionCard({ if (state === 'enabled') { return ( -
- -
-
+
+ +
+
{translate( 'auto.components.onboarding.NotificationStep.fd84d3e9b8', 'Notifications are enabled' @@ -150,22 +150,24 @@ export function MacNotificationPermissionCard({ if (state === 'awaiting-permission') { return ( -
-
-
-
- - {translate( - 'auto.components.onboarding.NotificationStep.95d99b52fa', - 'Allow notifications for Orca' - )} +
+
+
+ +
+
+ {translate( + 'auto.components.onboarding.NotificationStep.95d99b52fa', + 'Allow notifications for Orca' + )} +
+

+ {translate( + 'auto.components.onboarding.mac.notification.permission.card.f696515944', + 'Click Allow in the macOS dialog.' + )} +

-

- {translate( - 'auto.components.onboarding.mac.notification.permission.card.f696515944', - 'Click Allow in the macOS dialog.' - )} -

+
)} -
+
-
+
-
+
{primary.map((agent) => ( -
+
{fallbackRest.map((agent) => ( void }): React.JSX.Element { + const switchId = useId() return ( -
diff --git a/src/renderer/src/components/onboarding/ThemeStep.tsx b/src/renderer/src/components/onboarding/ThemeStep.tsx index 0929aba0d59..8e9bafeb7df 100644 --- a/src/renderer/src/components/onboarding/ThemeStep.tsx +++ b/src/renderer/src/components/onboarding/ThemeStep.tsx @@ -221,17 +221,15 @@ export function ThemeStep({ theme, onThemeChange, settings, updateSettings }: Th - - - {!cliSupported && !cliLoading && cliStatus?.detail ? ( - - {cliStatus.detail} - - ) : null} - - -
- - ) -} diff --git a/src/renderer/src/components/settings/BrowserUseCookieImportStep.tsx b/src/renderer/src/components/settings/BrowserUseCookieImportStep.tsx index d7525d2b0dc..50e40c0d658 100644 --- a/src/renderer/src/components/settings/BrowserUseCookieImportStep.tsx +++ b/src/renderer/src/components/settings/BrowserUseCookieImportStep.tsx @@ -25,7 +25,7 @@ import { translate } from '@/i18n/i18n' type BrowserUseCookieImportStepProps = { cookiesImported: boolean isImportingDefault: boolean - step3Blocked: boolean + disabled: boolean sourceLabel: string | null onConfigureMoreBrowsers?: () => void } @@ -33,7 +33,7 @@ type BrowserUseCookieImportStepProps = { export function BrowserUseCookieImportStep({ cookiesImported, isImportingDefault, - step3Blocked, + disabled, sourceLabel, onConfigureMoreBrowsers }: BrowserUseCookieImportStepProps): React.JSX.Element { @@ -95,15 +95,12 @@ export function BrowserUseCookieImportStep({ 'auto.components.settings.BrowserUsePane.af8c83ed61', 'Import cookies from Chrome, Edge, or other browsers so agents can reuse your logins.' )} - keywords={getBrowserUsePaneSearchEntries()[2].keywords} - className={cn( - 'rounded-xl border border-border/60 bg-card/50 p-4', - step3Blocked && 'opacity-60' - )} + keywords={getBrowserUsePaneSearchEntries()[1].keywords} + className={cn('rounded-xl border border-border/60 bg-card/50 p-4', disabled && 'opacity-60')} >
diff --git a/src/renderer/src/components/settings/BrowserUsePane.tsx b/src/renderer/src/components/settings/BrowserUsePane.tsx index 73564a22d4e..517a79afbb2 100644 --- a/src/renderer/src/components/settings/BrowserUsePane.tsx +++ b/src/renderer/src/components/settings/BrowserUsePane.tsx @@ -1,23 +1,15 @@ -import { useCallback, useEffect, useState } from 'react' -import { toast } from 'sonner' -import type { CliInstallStatus } from '../../../../shared/cli-install-types' +import { useEffect, useState } from 'react' import { ORCA_CLI_SKILL_INSTALL_COMMAND, ORCA_CLI_SKILL_NAME, ORCA_CLI_SKILL_UPDATE_COMMAND } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal, - isOrcaCliAvailableOnPath -} from '@/lib/agent-skill-cli-prerequisite' import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state' import { GLOBAL_AGENT_SKILL_SOURCE_KINDS, useInstalledAgentSkill } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' -import { useMountedRef } from '@/hooks/useMountedRef' import { cn } from '@/lib/utils' import { useAppStore } from '../../store' import { BROWSER_FAMILY_LABELS } from '../../../../shared/constants' @@ -28,13 +20,8 @@ import { BrowserUseExamples } from './BrowserUseExamples' import { BrowserUseComputerUseNotice } from './BrowserUseComputerUseNotice' import { BrowserUseEnableSwitch } from './BrowserUseEnableSwitch' import { BrowserUseSkillStep } from './BrowserUseSkillStep' -import { BrowserUseCliStep } from './BrowserUseCliStep' import { BrowserUseCookieImportStep } from './BrowserUseCookieImportStep' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' import { translate } from '@/i18n/i18n' type BrowserUseSetupProps = { @@ -51,10 +38,6 @@ export function BrowserUseSetup({ const fetchBrowserSessionProfiles = useAppStore((s) => s.fetchBrowserSessionProfiles) const browserSessionImportState = useAppStore((s) => s.browserSessionImportState) - const [cliStatus, setCliStatus] = useState(null) - const [cliLoading, setCliLoading] = useState(true) - const [cliBusy, setCliBusy] = useState(false) - const mountedRef = useMountedRef() const activeSkillRuntime = useActiveProjectSkillRuntime() const browserUseInstallCommand = !activeSkillRuntime.installDisabledReason ? buildSkillCommandForRuntime(ORCA_CLI_SKILL_INSTALL_COMMAND, activeSkillRuntime.agentRuntime) @@ -63,15 +46,6 @@ export function BrowserUseSetup({ ? buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND, activeSkillRuntime.agentRuntime) : ORCA_CLI_SKILL_UPDATE_COMMAND - const handleCliStatusChange = useCallback( - (nextStatus: CliInstallStatus | null): void => { - if (mountedRef.current) { - setCliStatus(nextStatus) - } - }, - [mountedRef] - ) - const [browserUseEnabled, setBrowserUseEnabled] = useState(() => { return localStorage.getItem(BROWSER_USE_ENABLED_STORAGE_KEY) === '1' }) @@ -84,54 +58,16 @@ export function BrowserUseSetup({ } } - const refreshCli = useCallback(async (): Promise => { - setCliLoading(true) - try { - if (activeSkillRuntime.installDisabledReason) { - handleCliStatusChange(null) - return - } - const nextStatus = - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? await window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : await window.api.cli.getInstallStatus() - handleCliStatusChange(nextStatus) - } catch (error) { - if (mountedRef.current) { - toast.error( - error instanceof Error - ? error.message - : translate( - 'auto.components.settings.BrowserUsePane.180a9abf3a', - 'Failed to load CLI status.' - ) - ) - } - } finally { - if (mountedRef.current) { - setCliLoading(false) - } - } - }, [activeSkillRuntime, handleCliStatusChange, mountedRef]) - useEffect(() => { if (!browserUseEnabled) { return } - void refreshCli() void fetchBrowserSessionProfiles() - }, [browserUseEnabled, fetchBrowserSessionProfiles, refreshCli]) + }, [browserUseEnabled, fetchBrowserSessionProfiles]) const defaultProfile = browserSessionProfiles.find((p) => p.id === 'default') const cookiesImported = !!defaultProfile?.source - const cliEnabled = isOrcaCliAvailableOnPath(cliStatus) - const cliPathNeedsAttention = - cliStatus?.state === 'installed' && cliStatus.pathConfigured === false - const cliSupported = cliStatus?.supported ?? false - const { installed: skillDetected, loading: skillLoading, @@ -143,47 +79,18 @@ export function BrowserUseSetup({ sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS }) - const handleEnableCli = async (): Promise => { - if (activeSkillRuntime.installDisabledReason) { - return - } - setCliBusy(true) - try { - const next = - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? await ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : await ensureOrcaCliAvailableForAgentSkillTerminal({ - onStatusChange: handleCliStatusChange - }) - if (activeSkillRuntime.agentRuntime?.runtime === 'wsl') { - handleCliStatusChange(next) - } - if (mountedRef.current && isOrcaCliAvailableOnPath(next)) { - toast.success( - translate( - 'auto.components.settings.BrowserUsePane.721aee31b4', - 'Registered the Orca CLI in PATH.' - ) - ) - } - } finally { - if (mountedRef.current) { - setCliBusy(false) - } - } - } - const isImportingDefault = browserSessionImportState?.profileId === 'default' && browserSessionImportState.status === 'importing' - const showStep1 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[0]]) - const showStep2 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[1]]) - const showStep3 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[2]]) - const completedCount = [cliEnabled, skillDetected, cookiesImported].filter(Boolean).length - const step2Blocked = - Boolean(activeSkillRuntime.installDisabledReason) || (!cliEnabled && !skillDetected) - const step3Blocked = !cookiesImported && (!cliEnabled || !skillDetected) + const showSkillStep = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[0]]) + const showCookieImportStep = matchesSettingsSearch(searchQuery, [ + getBrowserUsePaneSearchEntries()[1] + ]) + const steps = [skillDetected, cookiesImported] + const completedCount = steps.filter(Boolean).length + const skillDisabled = Boolean(activeSkillRuntime.installDisabledReason) + const cookieImportDisabled = !cookiesImported && !skillDetected const sourceLabel = defaultProfile?.source ? `${BROWSER_FAMILY_LABELS[defaultProfile.source.browserFamily] ?? defaultProfile.source.browserFamily}${defaultProfile.source.profileName ? ` (${defaultProfile.source.profileName})` : ''}` @@ -220,20 +127,20 @@ export function BrowserUseSetup({

{translate( - 'auto.components.settings.BrowserUsePane.702488a5f7', - 'Let coding agents drive this browser with your logins. Finish the three steps below.' + 'auto.components.settings.BrowserUsePane.finishSteps', + 'Let coding agents drive this browser with your logins. Finish the steps below.' )}

- {completedCount}/3 + {completedCount}/{steps.length} ) : null} - {showStep1 ? ( - void handleEnableCli()} - /> - ) : null} - - {showStep2 ? ( + {showSkillStep ? ( - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { + onBeforeOpenTerminal={() => { useAppStore.getState().recordFeatureInteraction('agent-browser-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal({ - onStatusChange: handleCliStatusChange - })) }} onRecheck={refreshSkill} /> ) : null} - {showStep3 ? ( + {showCookieImportStep ? ( diff --git a/src/renderer/src/components/settings/BrowserUseSkillStep.tsx b/src/renderer/src/components/settings/BrowserUseSkillStep.tsx index 399293d2756..5c4e3b365e0 100644 --- a/src/renderer/src/components/settings/BrowserUseSkillStep.tsx +++ b/src/renderer/src/components/settings/BrowserUseSkillStep.tsx @@ -1,4 +1,3 @@ -import type { ReactNode } from 'react' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' import type { LocalAgentRuntime } from './CliSkillRuntimeSetup' import { StepBadge } from './SetupStepBadge' @@ -13,8 +12,6 @@ type Props = { disabled?: boolean terminalShellOverride?: string terminalRuntime?: LocalAgentRuntime - preInstallNotice?: ReactNode - getPrerequisiteStatus?: () => Promise>> onBeforeOpenTerminal?: () => void | Promise onRecheck: () => void | Promise } @@ -28,8 +25,6 @@ export function BrowserUseSkillStep({ disabled = false, terminalShellOverride, terminalRuntime, - preInstallNotice, - getPrerequisiteStatus, onBeforeOpenTerminal, onRecheck }: Props): React.JSX.Element { @@ -55,9 +50,7 @@ export function BrowserUseSkillStep({ loading={skillLoading} error={skillError} installDisabled={disabled} - leading={} - preInstallNotice={preInstallNotice} - getPrerequisiteStatus={getPrerequisiteStatus} + leading={} onBeforeOpenTerminal={onBeforeOpenTerminal} onRecheck={onRecheck} /> diff --git a/src/renderer/src/components/settings/CliSection.test.tsx b/src/renderer/src/components/settings/CliSection.test.tsx index e07bbd77f28..c9fcc87d889 100644 --- a/src/renderer/src/components/settings/CliSection.test.tsx +++ b/src/renderer/src/components/settings/CliSection.test.tsx @@ -10,18 +10,26 @@ import { } from '@/lib/agent-feature-install-commands' import { CliSection } from './CliSection' -const capturedPanel = vi.hoisted(() => ({ - canUseLocalSkillFreshness: true, - props: null as null | { - command: string - installedCommand: string - terminalRuntime?: { runtime: 'host' | 'wsl'; wslDistro?: string | null; label: string } - freshnessSkillName?: string - getPrerequisiteStatus: () => Promise - onBeforeOpenTerminal: () => Promise - }, - useInstalledAgentSkill: vi.fn() -})) +type CapturedPanelProps = { + command: string + installedCommand: string + terminalRuntime?: { runtime: 'host' | 'wsl'; wslDistro?: string | null; label: string } + freshnessSkillName?: string + getPrerequisiteStatus?: () => Promise + onBeforeOpenTerminal?: () => Promise +} + +const capturedPanel = vi.hoisted( + (): { + canUseLocalSkillFreshness: boolean + props: CapturedPanelProps | null + useInstalledAgentSkill: ReturnType + } => ({ + canUseLocalSkillFreshness: true, + props: null, + useInstalledAgentSkill: vi.fn() + }) +) const toastError = vi.hoisted(() => vi.fn()) vi.mock('sonner', () => ({ toast: { error: toastError, success: vi.fn() } })) @@ -52,13 +60,7 @@ afterEach(() => { }) vi.mock('./AgentSkillSetupPanel', () => ({ - AgentSkillSetupPanel: function AgentSkillSetupPanel(props: { - command: string - installedCommand: string - freshnessSkillName?: string - getPrerequisiteStatus: () => Promise - onBeforeOpenTerminal: () => Promise - }) { + AgentSkillSetupPanel: function AgentSkillSetupPanel(props: CapturedPanelProps) { capturedPanel.props = props return
} @@ -101,7 +103,7 @@ describe('CliSection project runtime defaults', () => { expect(capturedPanel.props?.freshnessSkillName).toBeUndefined() }) - it('passes the default project WSL distro to CLI skill prerequisite checks', async () => { + it('targets the default project WSL distro without registering the CLI', async () => { const getWslInstallStatus = vi .fn() .mockResolvedValue({ supported: true, state: 'installed', pathConfigured: true }) @@ -130,9 +132,6 @@ describe('CliSection project runtime defaults', () => { /> ) - await capturedPanel.props?.getPrerequisiteStatus() - await capturedPanel.props?.onBeforeOpenTerminal() - expect(capturedPanel.useInstalledAgentSkill).toHaveBeenCalledWith( 'orca-cli', expect.objectContaining({ @@ -147,8 +146,9 @@ describe('CliSection project runtime defaults', () => { wslDistro: 'Ubuntu', label: 'WSL Ubuntu' }) - expect(getWslInstallStatus).toHaveBeenCalledWith({ distro: 'Ubuntu' }) - expect(getWslInstallStatus).toHaveBeenCalledTimes(2) + expect(capturedPanel.props?.getPrerequisiteStatus).toBeUndefined() + expect(capturedPanel.props?.onBeforeOpenTerminal).toBeUndefined() + expect(getWslInstallStatus).not.toHaveBeenCalled() }) it('renders an inline unknown PATH state without offering a mutation', async () => { diff --git a/src/renderer/src/components/settings/CliSection.tsx b/src/renderer/src/components/settings/CliSection.tsx index 85a738860a4..8f6b5c85152 100644 --- a/src/renderer/src/components/settings/CliSection.tsx +++ b/src/renderer/src/components/settings/CliSection.tsx @@ -8,11 +8,6 @@ import { ORCA_CLI_SKILL_NAME, ORCA_CLI_SKILL_UPDATE_COMMAND } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal, - isOrcaCliAvailableOnPath -} from '@/lib/agent-skill-cli-prerequisite' import { GLOBAL_AGENT_SKILL_SOURCE_KINDS, useInstalledAgentSkill @@ -26,11 +21,9 @@ import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' import { CliRegistrationDialog } from './CliRegistrationDialog' import { buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, getAgentSkillTerminalShellOverride, getSelectedAgentRuntime, - getSkillDiscoveryTargetForRuntime, - getWslCliDistroRequest + getSkillDiscoveryTargetForRuntime } from './CliSkillRuntimeSetup' import { WslCliRegistration } from './WslCliRegistration' import { useCliRegistrationActions } from './use-cli-registration-actions' @@ -115,14 +108,6 @@ export function CliSection({ settings, agentRuntime ) - const getCliSkillPrerequisiteStatus = useCallback( - () => - agentRuntime.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus(getWslCliDistroRequest(agentRuntime)) - : window.api.cli.getInstallStatus(), - [agentRuntime] - ) - const handleStatusChange = useCallback( (nextStatus: CliInstallStatus): void => { if (mountedRef.current) { @@ -185,8 +170,8 @@ export function CliSection({

{translate( - 'auto.components.settings.CliSection.6930feda9e', - 'Use Orca from your terminal to open the app, manage worktrees, and interact with Orca terminals.' + 'auto.components.settings.CliSection.outsideOrcaDescription', + 'Orca terminals already have `orca`. Turn this on to use `orca` from other terminals outside Orca.' )}

@@ -350,16 +335,6 @@ export function CliSection({ installed={cliSkillDetected} loading={cliSkillLoading} error={cliSkillError} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={getCliSkillPrerequisiteStatus} - isPrerequisiteAvailable={isOrcaCliAvailableOnPath} - onBeforeOpenTerminal={async () => { - await (agentRuntime.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal({ - onStatusChange: handleStatusChange - })) - }} onRecheck={refreshCliSkill} freshnessSkillName={cliSkillFreshnessName} /> diff --git a/src/renderer/src/components/settings/ComputerUseSkillSetupPanel.tsx b/src/renderer/src/components/settings/ComputerUseSkillSetupPanel.tsx index 9acfddd23bc..ba2f4458842 100644 --- a/src/renderer/src/components/settings/ComputerUseSkillSetupPanel.tsx +++ b/src/renderer/src/components/settings/ComputerUseSkillSetupPanel.tsx @@ -4,10 +4,6 @@ import { COMPUTER_USE_SKILL_NAME, COMPUTER_USE_SKILL_UPDATE_COMMAND } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { GLOBAL_AGENT_SKILL_SOURCE_KINDS, useInstalledAgentSkill @@ -15,11 +11,7 @@ import { import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { useAppStore } from '@/store' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' import { translate } from '@/i18n/i18n' export function ComputerUseSkillSetupPanel(): React.JSX.Element { @@ -65,19 +57,8 @@ export function ComputerUseSkillSetupPanel(): React.JSX.Element { error={activeSkillRuntime.installDisabledReason ?? computerUseSkillError} installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} icon={} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { + onBeforeOpenTerminal={() => { useAppStore.getState().recordFeatureInteraction('computer-use-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) }} onRecheck={refreshComputerUseSkill} freshnessSkillName={ diff --git a/src/renderer/src/components/settings/EphemeralVmsPane.tsx b/src/renderer/src/components/settings/EphemeralVmsPane.tsx index 7b0086ae78b..5814478eeb5 100644 --- a/src/renderer/src/components/settings/EphemeralVmsPane.tsx +++ b/src/renderer/src/components/settings/EphemeralVmsPane.tsx @@ -8,10 +8,6 @@ import { Button } from '../ui/button' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' import { EphemeralVmRecipeRow } from './EphemeralVmRecipeRow' import { translate } from '@/i18n/i18n' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { EPHEMERAL_VMS_SKILL_INSTALL_COMMAND, EPHEMERAL_VMS_SKILL_NAME, @@ -22,11 +18,7 @@ import { useInstalledAgentSkill } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' type RecipeCatalogEntry = Awaited< ReturnType @@ -187,19 +179,6 @@ export function EphemeralVmsPane(): React.JSX.Element { error={activeSkillRuntime.installDisabledReason ?? skillError} installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} icon={} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) - }} onRecheck={refreshSkill} freshnessSkillName={ activeSkillRuntime.canUseLocalSkillFreshness ? EPHEMERAL_VMS_SKILL_NAME : undefined diff --git a/src/renderer/src/components/settings/LinearAgentSkillPane.tsx b/src/renderer/src/components/settings/LinearAgentSkillPane.tsx index 0e2bbaff108..19ce6e975c6 100644 --- a/src/renderer/src/components/settings/LinearAgentSkillPane.tsx +++ b/src/renderer/src/components/settings/LinearAgentSkillPane.tsx @@ -88,9 +88,6 @@ export function LinearAgentSkillPane(): React.JSX.Element { loading={skillSetup.skillLoading} error={skillSetup.error} installDisabled={skillSetup.installDisabled} - preInstallNotice={skillSetup.preInstallNotice} - getPrerequisiteStatus={skillSetup.getPrerequisiteStatus} - onBeforeOpenTerminal={skillSetup.onBeforeOpenTerminal} onRecheck={skillSetup.refreshSkill} freshnessSkillName={skillSetup.freshnessSkillName} /> diff --git a/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.test.tsx b/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.test.tsx index 668d0d1fdf7..13473c00dbd 100644 --- a/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.test.tsx +++ b/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.test.tsx @@ -2,10 +2,17 @@ import { renderToStaticMarkup } from 'react-dom/server' import { beforeEach, describe, expect, it, vi } from 'vitest' import { MobileEmulatorAgentControlRow } from './MobileEmulatorAgentControlRow' -const mocks = vi.hoisted(() => ({ - canUseLocalSkillFreshness: true, - freshnessSkillName: undefined as string | undefined -})) +const mocks = vi.hoisted( + (): { + canUseLocalSkillFreshness: boolean + freshnessSkillName: string | undefined + panelProps: Record + } => ({ + canUseLocalSkillFreshness: true, + freshnessSkillName: undefined, + panelProps: {} + }) +) vi.mock('@/hooks/useActiveProjectSkillRuntime', () => ({ useActiveProjectSkillRuntime: () => ({ @@ -16,30 +23,25 @@ vi.mock('@/hooks/useActiveProjectSkillRuntime', () => ({ vi.mock('../emulator-pane/use-mobile-emulator-agent-setup-state', () => ({ useMobileEmulatorAgentSetupState: () => ({ - cliActionLabel: 'Enable', - cliBusy: false, - cliEnabled: true, - cliInstallStatus: null, - cliLoading: false, cliSkillError: null, cliSkillInstalled: true, cliSkillLoading: false, - cliSupported: true, - completedCount: 2, - handleEnableCli: vi.fn(), + recheckSetup: vi.fn(), refreshCliSkill: vi.fn(), - step2Blocked: false + setupComplete: true, + setupRechecking: false, + statusReady: true }) })) vi.mock('./AgentSkillSetupPanel', () => ({ - AgentSkillSetupPanel: ({ freshnessSkillName }: { freshnessSkillName?: string }) => { - mocks.freshnessSkillName = freshnessSkillName + AgentSkillSetupPanel: (props: Record & { freshnessSkillName?: string }) => { + mocks.freshnessSkillName = props.freshnessSkillName + mocks.panelProps = props return null } })) -vi.mock('./SetupStepBadge', () => ({ StepBadge: () => null })) vi.mock('./MobileEmulatorExamples', () => ({ MobileEmulatorExamples: () => null })) describe('MobileEmulatorAgentControlRow freshness authority', () => { @@ -56,4 +58,11 @@ describe('MobileEmulatorAgentControlRow freshness authority', () => { renderToStaticMarkup() expect(mocks.freshnessSkillName).toBeUndefined() }) + + it('installs the skill without registering the CLI first', () => { + renderToStaticMarkup() + expect(mocks.panelProps.onBeforeOpenTerminal).toBeUndefined() + expect(mocks.panelProps.preInstallNotice).toBeUndefined() + expect(mocks.panelProps.installDisabled).toBeUndefined() + }) }) diff --git a/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.tsx b/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.tsx index 872f530fedb..17ae635cdb5 100644 --- a/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.tsx +++ b/src/renderer/src/components/settings/MobileEmulatorAgentControlRow.tsx @@ -1,22 +1,14 @@ -import { Import, Loader2 } from 'lucide-react' +import { Import } from 'lucide-react' import { ORCA_CLI_SKILL_INSTALL_COMMAND, ORCA_CLI_SKILL_NAME, ORCA_CLI_SKILL_UPDATE_COMMAND } from '@/lib/agent-feature-install-commands' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' -import { cn } from '@/lib/utils' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { useMobileEmulatorAgentSetupState } from '../emulator-pane/use-mobile-emulator-agent-setup-state' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' -import { StepBadge } from './SetupStepBadge' import { MobileEmulatorExamples } from './MobileEmulatorExamples' -import { Button } from '../ui/button' -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip' import { translate } from '@/i18n/i18n' const EMULATOR_CLI_COMMANDS = [ @@ -34,10 +26,6 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element { const cliSkillInstallCommand = buildSkillCommandForRuntime(ORCA_CLI_SKILL_INSTALL_COMMAND) const cliSkillUpdateCommand = buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND) - const handleEnableCli = async (): Promise => { - await setup.handleEnableCli() - } - return (
@@ -55,98 +43,10 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element { )}

- - {setup.completedCount}/2 -
-
- -
-

- {translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.4f2205f3b6', - 'Enable Orca CLI' - )} -

-

- {translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.2fef055608', - 'Registers the Orca CLI command so agents can control the active emulator from their shell.' - )} -

- {setup.cliInstallStatus?.commandPath && setup.cliEnabled ? ( -

- {translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.aaf62a3dd2', - 'Installed at' - )}{' '} - - {setup.cliInstallStatus.commandPath} - -

- ) : null} - {!setup.cliEnabled && setup.cliInstallStatus?.detail ? ( -

{setup.cliInstallStatus.detail}

- ) : null} - {setup.cliBusy ? ( -

- {translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.3d34423e88', - 'Registering the Orca CLI' - )}{' '} - {setup.cliInstallStatus?.commandPath ? ( - - {setup.cliInstallStatus.commandPath} - - ) : null}{' '} - {translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.3be27641c9', - 'so emulator commands can run from agent shells.' - )} -

- ) : null} -
- - - - - - - - {!setup.cliSupported && !setup.cliLoading && setup.cliInstallStatus?.detail ? ( - - {setup.cliInstallStatus.detail} - - ) : null} - - -
- -
+
} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - openingHint={translate( - 'auto.components.settings.MobileEmulatorAgentControlRow.3941719a56', - 'Checking Orca CLI before opening skill setup.' - )} - onBeforeOpenTerminal={async () => { - await ensureOrcaCliAvailableForAgentSkillTerminal() - }} onRecheck={setup.refreshCliSkill} freshnessSkillName={ activeSkillRuntime.canUseLocalSkillFreshness ? ORCA_CLI_SKILL_NAME : undefined diff --git a/src/renderer/src/components/settings/OrchestrationPane.tsx b/src/renderer/src/components/settings/OrchestrationPane.tsx index e3230f4cf86..ef3df303166 100644 --- a/src/renderer/src/components/settings/OrchestrationPane.tsx +++ b/src/renderer/src/components/settings/OrchestrationPane.tsx @@ -2,10 +2,6 @@ import { useState } from 'react' import { ArrowRightLeft, GitBranch, ListChecks, Workflow, type LucideIcon } from 'lucide-react' import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands' import type { SkillUsageExample } from '@/lib/skill-usage-example' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { ORCHESTRATION_SKILL_INSTALL_COMMAND, ORCHESTRATION_SKILL_UPDATE_COMMAND @@ -21,11 +17,7 @@ import { matchesSettingsSearch } from './settings-search' import { useAppStore } from '../../store' import { getOrchestrationPaneSearchEntries } from './orchestration-search' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' import { OrchestrationSkillAgentCoverage } from './OrchestrationSkillAgentCoverage' import { SkillUsageExamplesSection } from './SkillUsageExamplesSection' import { OrchestrationSkillPromptDialog } from './OrchestrationSkillPromptDialog' @@ -132,19 +124,8 @@ export function OrchestrationPane({ error={activeSkillRuntime.installDisabledReason ?? orchestrationSkillError} installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} icon={} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { + onBeforeOpenTerminal={() => { useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) }} actionHint={ // Installed updates stay on the primary panel so there is only one update path. diff --git a/src/renderer/src/components/settings/OrchestrationSetupCard.tsx b/src/renderer/src/components/settings/OrchestrationSetupCard.tsx index 39e01a59683..9ca5be26fdb 100644 --- a/src/renderer/src/components/settings/OrchestrationSetupCard.tsx +++ b/src/renderer/src/components/settings/OrchestrationSetupCard.tsx @@ -1,8 +1,4 @@ import type { JSX } from 'react' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' import { ORCHESTRATION_SKILL_INSTALL_COMMAND, ORCHESTRATION_SKILL_UPDATE_COMMAND @@ -11,11 +7,7 @@ import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands' import type { InstalledAgentSkillState } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' @@ -62,19 +54,8 @@ export function OrchestrationSetupCard(props: { error={activeSkillRuntime.installDisabledReason ?? skill.error} installDisabled={Boolean(activeSkillRuntime.installDisabledReason)} terminalHeightPx={terminalHeightPx} - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={() => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus() - } - onBeforeOpenTerminal={async () => { + onBeforeOpenTerminal={() => { useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup') - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) }} onRecheck={skill.refresh} freshnessSkillName={ diff --git a/src/renderer/src/components/settings/TaskSourceLinearSetup.tsx b/src/renderer/src/components/settings/TaskSourceLinearSetup.tsx index 8b349db4959..9d439086555 100644 --- a/src/renderer/src/components/settings/TaskSourceLinearSetup.tsx +++ b/src/renderer/src/components/settings/TaskSourceLinearSetup.tsx @@ -142,9 +142,6 @@ export function TaskSourceLinearSetup({ loading={skillSetup.skillLoading} error={skillSetup.error} installDisabled={skillSetup.installDisabled} - preInstallNotice={skillSetup.preInstallNotice} - getPrerequisiteStatus={skillSetup.getPrerequisiteStatus} - onBeforeOpenTerminal={skillSetup.onBeforeOpenTerminal} onRecheck={skillSetup.refreshSkill} freshnessSkillName={skillSetup.freshnessSkillName} /> diff --git a/src/renderer/src/components/settings/browser-use-search.ts b/src/renderer/src/components/settings/browser-use-search.ts index ee48211ac0c..c065960b736 100644 --- a/src/renderer/src/components/settings/browser-use-search.ts +++ b/src/renderer/src/components/settings/browser-use-search.ts @@ -3,29 +3,6 @@ import { translateSearchKeyword } from './settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' export const getBrowserUsePaneSearchEntries = createLocalizedCatalog(() => [ - { - title: translate('auto.components.settings.browser.use.search.50f0860e18', 'Enable Orca CLI'), - description: translate( - 'auto.components.settings.browser.use.search.890ddf943d', - 'Register the Orca CLI so agents can drive the browser.' - ), - keywords: [ - ...translateSearchKeyword( - 'auto.components.settings.browser.use.search.ba4eb53b72', - 'browser use' - ), - ...translateSearchKeyword('auto.components.settings.browser.use.search.85fab5e12c', 'cli'), - ...translateSearchKeyword('auto.components.settings.browser.use.search.ff05cbc344', 'orca'), - ...translateSearchKeyword('auto.components.settings.browser.use.search.30c74aaa1f', 'path'), - ...translateSearchKeyword( - 'auto.components.settings.browser.use.search.3ffafc9b95', - 'command' - ), - ...translateSearchKeyword('auto.components.settings.browser.use.search.7e0dcb257a', 'shell'), - ...translateSearchKeyword('auto.components.settings.browser.use.search.034c5e8d7f', 'enable'), - ...translateSearchKeyword('auto.components.settings.browser.use.search.e56c7b55c9', 'setup') - ] - }, { title: translate( 'auto.components.settings.browser.use.search.a1414dcefb', diff --git a/src/renderer/src/components/settings/use-linear-agent-skill-setup.ts b/src/renderer/src/components/settings/use-linear-agent-skill-setup.ts index f3a32534fe4..59f18dfe4cd 100644 --- a/src/renderer/src/components/settings/use-linear-agent-skill-setup.ts +++ b/src/renderer/src/components/settings/use-linear-agent-skill-setup.ts @@ -1,8 +1,4 @@ -import { useCallback, useMemo } from 'react' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - ensureOrcaCliAvailableForAgentSkillTerminal -} from '@/lib/agent-skill-cli-prerequisite' +import { useMemo } from 'react' import { LINEAR_AGENT_SKILL_NAMES, ORCA_LINEAR_SKILL_INSTALL_COMMAND @@ -13,12 +9,7 @@ import { useInstalledAgentSkillNames } from '@/hooks/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime' -import { - buildSkillCommandForRuntime, - ensureWslCliAvailableForAgentSkillTerminal, - getWslCliDistroRequest, - type LocalAgentRuntime -} from './CliSkillRuntimeSetup' +import { buildSkillCommandForRuntime, type LocalAgentRuntime } from './CliSkillRuntimeSetup' // Shared install/update wiring for Task Sources + Linear settings. export function useLinearAgentSkillSetup(): { @@ -36,10 +27,7 @@ export function useLinearAgentSkillSetup(): { error: string | null terminalShellOverride: string | undefined terminalRuntime: LocalAgentRuntime | undefined - preInstallNotice: string refreshSkill: () => Promise - getPrerequisiteStatus: () => Promise>> - onBeforeOpenTerminal: () => Promise } { const activeSkillRuntime = useActiveProjectSkillRuntime() const { @@ -76,22 +64,6 @@ export function useLinearAgentSkillSetup(): { ? updateTarget.skillName : undefined - const getPrerequisiteStatus = useCallback( - () => - activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? window.api.cli.getWslInstallStatus( - getWslCliDistroRequest(activeSkillRuntime.agentRuntime) - ) - : window.api.cli.getInstallStatus(), - [activeSkillRuntime.agentRuntime] - ) - - const onBeforeOpenTerminal = useCallback(async () => { - await (activeSkillRuntime.agentRuntime?.runtime === 'wsl' - ? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime) - : ensureOrcaCliAvailableForAgentSkillTerminal()) - }, [activeSkillRuntime.agentRuntime]) - const installDisabled = Boolean(activeSkillRuntime.installDisabledReason) return { @@ -106,9 +78,6 @@ export function useLinearAgentSkillSetup(): { error: activeSkillRuntime.installDisabledReason ?? skillError, terminalShellOverride: activeSkillRuntime.terminalShellOverride, terminalRuntime: activeSkillRuntime.agentRuntime, - preInstallNotice: AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - refreshSkill, - getPrerequisiteStatus, - onBeforeOpenTerminal + refreshSkill } } diff --git a/src/renderer/src/components/setup-guide/SetupGuideModal.tsx b/src/renderer/src/components/setup-guide/SetupGuideModal.tsx index 79d11b7b23c..b8f4f5b044f 100644 --- a/src/renderer/src/components/setup-guide/SetupGuideModal.tsx +++ b/src/renderer/src/components/setup-guide/SetupGuideModal.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useMemo, useState, type JSX } from 'react' -import { EyeOff } from 'lucide-react' import { FEATURE_WALL_SETUP_STEP_IDS, getFirstIncompleteFeatureWallSetupStepId, @@ -14,10 +13,8 @@ import { DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useAppStore } from '@/store' import { FeatureWallSetupChecklist } from '../feature-wall/FeatureWallSetupChecklist' -import { SetupGuideProgressRing } from './SetupGuideProgressRing' import { useSetupGuideProgress } from './use-setup-guide-progress' import { useSetupGuideOpenCloseTelemetry } from './use-setup-guide-telemetry' import { translate } from '@/i18n/i18n' @@ -141,44 +138,24 @@ function SetupGuideModalContent({ className="grid h-[min(780px,calc(100vh-2rem))] w-[min(1080px,calc(100vw-2rem))] max-w-none grid-rows-[auto_minmax(0,1fr)] gap-0 p-0 sm:max-w-none" tabIndex={-1} > - - - - - - {translate( - 'auto.components.setup.guide.SetupGuideModal.28cf59fcb4', - 'This will hide the checklist from the sidebar' - )} - - + {/* Why: labeled so it can't be mistaken for close; hidden below sm, where the header + centers the title under it (the sidebar entry keeps its own hide control). */} + -
- - {translate( - 'auto.components.setup.guide.SetupGuideModal.48a9e5ef2d', - 'Getting started' - )} - - -
+ + {translate('auto.components.setup.guide.SetupGuideModal.48a9e5ef2d', 'Getting started')} + {translate( 'auto.components.setup.guide.SetupGuideModal.3598a3ca0c', diff --git a/src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx b/src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx index 3e4749f0f3b..a7b79c9b29f 100644 --- a/src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx +++ b/src/renderer/src/components/sidebar/LinearAgentSkillSetupDialog.tsx @@ -12,10 +12,6 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { - AGENT_SKILL_CLI_PREREQUISITE_NOTICE, - isOrcaCliAvailableOnPath -} from '@/lib/agent-skill-cli-prerequisite' import { translate } from '@/i18n/i18n' type AgentSkillSetupPanelProps = ComponentProps @@ -32,8 +28,6 @@ type LinearAgentSkillSetupDialogProps = { installed: boolean loading: boolean error: string | null - getPrerequisiteStatus?: AgentSkillSetupPanelProps['getPrerequisiteStatus'] - onBeforeOpenTerminal: AgentSkillSetupPanelProps['onBeforeOpenTerminal'] onRecheck: AgentSkillSetupPanelProps['onRecheck'] onOpenChange: (open: boolean) => void onDismissPermanently: () => void @@ -52,8 +46,6 @@ export function LinearAgentSkillSetupDialog({ installed, loading, error, - getPrerequisiteStatus, - onBeforeOpenTerminal, onRecheck, onOpenChange, onDismissPermanently, @@ -144,16 +136,12 @@ export function LinearAgentSkillSetupDialog({ loading={loading} error={error} installLabel={translate( - 'auto.components.sidebar.LinearAgentSkillSetupPrompt.install', - 'Install CLI & Skill' + 'auto.components.skills.SkillInstallDialog.39acb9e8f4', + 'Install skill' )} // Why: Install is this modal's sole CTA, so make it the filled primary — // matching the other setup surfaces (filled primary + muted dismiss). installVariant="default" - preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE} - getPrerequisiteStatus={getPrerequisiteStatus} - isPrerequisiteAvailable={isOrcaCliAvailableOnPath} - onBeforeOpenTerminal={onBeforeOpenTerminal} onRecheck={onRecheck} /> {/* Why: permanent opt-out as a quiet EyeOff icon next to the × — matching diff --git a/src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.reminder-toast.test.tsx b/src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.reminder-toast.test.tsx index 40d6e6259ac..603493b80c6 100644 --- a/src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.reminder-toast.test.tsx +++ b/src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.reminder-toast.test.tsx @@ -3,7 +3,6 @@ import { act, type ComponentProps, type ReactNode } from 'react' import { createRoot, type Root } from 'react-dom/client' import { toast } from 'sonner' -import type { CliInstallStatus } from '../../../../shared/cli-install-types' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { LINEAR_AGENT_SKILL_NAMES } from '@/lib/agent-feature-install-commands' import { @@ -18,6 +17,19 @@ import { getExistingLinearAgentSkillSetupReminderState } from './linear-agent-sk const HOST_DISMISS_STORAGE_KEY = 'orca.linearTicketsSkill.setupDismissed.host' +const wslFedoraProps = { + linked: true, + remote: false, + surface: 'modal', + currentPlatform: 'win32', + settings: { + localAgentRuntime: 'wsl', + localAgentWslDistro: 'Fedora', + terminalWindowsShell: 'wsl.exe', + activeRuntimeEnvironmentId: null + } +} satisfies ComponentProps + const mocks = vi.hoisted(() => ({ skillState: { installed: false, @@ -29,8 +41,6 @@ const mocks = vi.hoisted(() => ({ useInstalledAgentSkillNames: vi.fn(), getCliStatus: vi.fn(), getWslCliStatus: vi.fn(), - ensureCli: vi.fn(async () => null as CliInstallStatus | null), - ensureWslCli: vi.fn(async () => null as CliInstallStatus | null), toastDismiss: vi.fn(), toastWarning: vi.fn(() => 'linear-setup-toast-id'), panelProps: [] as Record[] @@ -48,13 +58,6 @@ vi.mock('@/hooks/useInstalledAgentSkills', async (importOriginal) => ({ useInstalledAgentSkillNames: mocks.useInstalledAgentSkillNames })) -vi.mock('@/lib/agent-skill-cli-prerequisite', () => ({ - AGENT_SKILL_CLI_PREREQUISITE_NOTICE: 'CLI registration notice', - ensureOrcaCliAvailableForAgentSkillTerminal: mocks.ensureCli, - isOrcaCliAvailableOnPath: (status: CliInstallStatus | null | undefined) => - status?.state === 'installed' && status.pathConfigured -})) - vi.mock('../settings/CliSkillRuntimeSetup', () => ({ buildSkillCommandForRuntime: ( command: string, @@ -62,12 +65,7 @@ vi.mock('../settings/CliSkillRuntimeSetup', () => ({ ) => runtime.runtime === 'wsl' ? `wsl.exe${runtime.wslDistro ? ` -d '${runtime.wslDistro}'` : ''} --exec bash -lc '${command}'` - : command, - ensureWslCliAvailableForAgentSkillTerminal: mocks.ensureWslCli, - getWslCliDistroRequest: (runtime?: { runtime: string; wslDistro?: string | null }) => - runtime?.runtime === 'wsl' && runtime.wslDistro?.trim() - ? { distro: runtime.wslDistro.trim() } - : undefined + : command })) vi.mock('../settings/AgentSkillSetupPanel', () => ({ @@ -78,7 +76,14 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({

{String(props.title)}

{String(props.description)}

{String(props.command)} -