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)} -