import { useState, useEffect, useCallback, useRef } from 'react'; import { createLogger } from '@automaker/utils/logger'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { useSetupStore } from '@/store/setup-store'; import { getElectronAPI } from '@/lib/electron'; import { CheckCircle2, ArrowRight, ArrowLeft, ExternalLink, Copy, RefreshCw, AlertTriangle, XCircle, } from 'lucide-react'; import { Spinner } from '@/components/ui/spinner'; import { toast } from 'sonner'; import { StatusBadge } from '../components'; import { CursorIcon } from '@/components/ui/provider-icon'; const logger = createLogger('CursorSetupStep'); interface CursorSetupStepProps { onNext: () => void; onBack: () => void; onSkip: () => void; } interface CursorCliStatus { installed: boolean; version?: string | null; path?: string | null; auth?: { authenticated: boolean; method: string; }; installCommand?: string; loginCommand?: string; } export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps) { const { cursorCliStatus, setCursorCliStatus } = useSetupStore(); const [isChecking, setIsChecking] = useState(false); const [isLoggingIn, setIsLoggingIn] = useState(false); const pollIntervalRef = useRef(null); const checkStatus = useCallback(async () => { setIsChecking(true); try { const api = getElectronAPI(); if (!api.setup?.getCursorStatus) { return; } const result = await api.setup.getCursorStatus(); if (result.success) { const status: CursorCliStatus = { installed: result.installed ?? false, version: result.version, path: result.path, auth: result.auth, installCommand: result.installCommand, loginCommand: result.loginCommand, }; setCursorCliStatus(status); if (result.auth?.authenticated) { toast.success('Cursor CLI is ready!'); } } } catch (error) { logger.error('Failed to check Cursor status:', error); } finally { setIsChecking(false); } }, [setCursorCliStatus]); useEffect(() => { checkStatus(); // Cleanup polling on unmount return () => { if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); } }; }, [checkStatus]); const copyCommand = (command: string) => { navigator.clipboard.writeText(command); toast.success('Command copied to clipboard'); }; const handleLogin = async () => { setIsLoggingIn(true); try { // Copy login command to clipboard and show instructions const loginCommand = cursorCliStatus?.loginCommand || 'cursor-agent login'; await navigator.clipboard.writeText(loginCommand); toast.info('Login command copied! Paste in terminal to authenticate.'); // Poll for auth status let attempts = 0; const maxAttempts = 60; // 2 minutes with 2s interval pollIntervalRef.current = setInterval(async () => { attempts++; try { const api = getElectronAPI(); if (!api.setup?.getCursorStatus) { return; } const result = await api.setup.getCursorStatus(); if (result.auth?.authenticated) { if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); pollIntervalRef.current = null; } setCursorCliStatus({ ...cursorCliStatus, installed: result.installed ?? true, version: result.version, path: result.path, auth: result.auth, } as CursorCliStatus); setIsLoggingIn(false); toast.success('Successfully logged in to Cursor!'); } } catch { // Ignore polling errors } if (attempts >= maxAttempts) { if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); pollIntervalRef.current = null; } setIsLoggingIn(false); toast.error('Login timed out. Please try again.'); } }, 2000); } catch (error) { logger.error('Login failed:', error); toast.error('Failed to start login process'); setIsLoggingIn(false); } }; const isReady = cursorCliStatus?.installed && cursorCliStatus?.auth?.authenticated; const getStatusBadge = () => { if (isChecking) { return ; } if (cursorCliStatus?.auth?.authenticated) { return ; } if (cursorCliStatus?.installed) { return ; } return ; }; return (

Cursor CLI Setup

Optional - Use Cursor as an AI provider

{/* Info Banner */}

This step is optional

Configure Cursor CLI as an alternative AI provider. You can skip this and use Claude instead, or configure it later in Settings.

{/* Status Card */}
Cursor CLI Status Optional
{getStatusBadge()}
{cursorCliStatus?.installed ? cursorCliStatus.auth?.authenticated ? `Authenticated via ${cursorCliStatus.auth.method === 'api_key' ? 'API Key' : 'Browser Login'}${cursorCliStatus.version ? ` (v${cursorCliStatus.version})` : ''}` : 'Installed but not authenticated' : 'Not installed on your system'}
{/* Success State */} {isReady && (

Cursor CLI is ready!

You can use Cursor models for AI tasks. {cursorCliStatus?.version && ( Version: {cursorCliStatus.version} )}

)} {/* Not Installed */} {!cursorCliStatus?.installed && !isChecking && (

Cursor CLI not found

Install the Cursor CLI to use Cursor models.

Install Cursor CLI:

{cursorCliStatus?.installCommand || 'curl https://cursor.com/install -fsS | bash'}
View installation docs
)} {/* Installed but not authenticated */} {cursorCliStatus?.installed && !cursorCliStatus?.auth?.authenticated && !isChecking && (

Cursor CLI not authenticated

Run the login command to authenticate with Cursor.

Run the login command in your terminal, then complete authentication in your browser:

{cursorCliStatus?.loginCommand || 'cursor-agent login'}
)} {/* Loading State */} {isChecking && (

Checking Cursor CLI status...

)}
{/* Navigation */}
{/* Info note */}

You can always configure Cursor later in Settings

); }