mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
feat: Add Cursor setup step to UI setup wizard
- Introduced a new `CursorSetupStep` component for optional Cursor CLI configuration during the setup process. - Updated `SetupView` to include the cursor step in the setup flow, allowing users to skip or proceed with Cursor CLI setup. - Enhanced state management to track Cursor CLI installation and authentication status. - Updated Electron API to support fetching Cursor CLI status. - Marked completion of the UI setup wizard phase in the integration plan.
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
|||||||
ThemeStep,
|
ThemeStep,
|
||||||
CompleteStep,
|
CompleteStep,
|
||||||
ClaudeSetupStep,
|
ClaudeSetupStep,
|
||||||
|
CursorSetupStep,
|
||||||
GitHubSetupStep,
|
GitHubSetupStep,
|
||||||
} from './setup-view/steps';
|
} from './setup-view/steps';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
@@ -14,12 +15,13 @@ export function SetupView() {
|
|||||||
const { currentStep, setCurrentStep, completeSetup, setSkipClaudeSetup } = useSetupStore();
|
const { currentStep, setCurrentStep, completeSetup, setSkipClaudeSetup } = useSetupStore();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const steps = ['welcome', 'theme', 'claude', 'github', 'complete'] as const;
|
const steps = ['welcome', 'theme', 'claude', 'cursor', 'github', 'complete'] as const;
|
||||||
type StepName = (typeof steps)[number];
|
type StepName = (typeof steps)[number];
|
||||||
const getStepName = (): StepName => {
|
const getStepName = (): StepName => {
|
||||||
if (currentStep === 'claude_detect' || currentStep === 'claude_auth') return 'claude';
|
if (currentStep === 'claude_detect' || currentStep === 'claude_auth') return 'claude';
|
||||||
if (currentStep === 'welcome') return 'welcome';
|
if (currentStep === 'welcome') return 'welcome';
|
||||||
if (currentStep === 'theme') return 'theme';
|
if (currentStep === 'theme') return 'theme';
|
||||||
|
if (currentStep === 'cursor') return 'cursor';
|
||||||
if (currentStep === 'github') return 'github';
|
if (currentStep === 'github') return 'github';
|
||||||
return 'complete';
|
return 'complete';
|
||||||
};
|
};
|
||||||
@@ -37,6 +39,10 @@ export function SetupView() {
|
|||||||
setCurrentStep('claude_detect');
|
setCurrentStep('claude_detect');
|
||||||
break;
|
break;
|
||||||
case 'claude':
|
case 'claude':
|
||||||
|
console.log('[Setup Flow] Moving to cursor step');
|
||||||
|
setCurrentStep('cursor');
|
||||||
|
break;
|
||||||
|
case 'cursor':
|
||||||
console.log('[Setup Flow] Moving to github step');
|
console.log('[Setup Flow] Moving to github step');
|
||||||
setCurrentStep('github');
|
setCurrentStep('github');
|
||||||
break;
|
break;
|
||||||
@@ -56,15 +62,23 @@ export function SetupView() {
|
|||||||
case 'claude':
|
case 'claude':
|
||||||
setCurrentStep('theme');
|
setCurrentStep('theme');
|
||||||
break;
|
break;
|
||||||
case 'github':
|
case 'cursor':
|
||||||
setCurrentStep('claude_detect');
|
setCurrentStep('claude_detect');
|
||||||
break;
|
break;
|
||||||
|
case 'github':
|
||||||
|
setCurrentStep('cursor');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipClaude = () => {
|
const handleSkipClaude = () => {
|
||||||
console.log('[Setup Flow] Skipping Claude setup');
|
console.log('[Setup Flow] Skipping Claude setup');
|
||||||
setSkipClaudeSetup(true);
|
setSkipClaudeSetup(true);
|
||||||
|
setCurrentStep('cursor');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSkipCursor = () => {
|
||||||
|
console.log('[Setup Flow] Skipping Cursor setup');
|
||||||
setCurrentStep('github');
|
setCurrentStep('github');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,6 +128,14 @@ export function SetupView() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{currentStep === 'cursor' && (
|
||||||
|
<CursorSetupStep
|
||||||
|
onNext={() => handleNext('cursor')}
|
||||||
|
onBack={() => handleBack('cursor')}
|
||||||
|
onSkip={handleSkipCursor}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{currentStep === 'github' && (
|
{currentStep === 'github' && (
|
||||||
<GitHubSetupStep
|
<GitHubSetupStep
|
||||||
onNext={() => handleNext('github')}
|
onNext={() => handleNext('github')}
|
||||||
|
|||||||
@@ -0,0 +1,368 @@
|
|||||||
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
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,
|
||||||
|
Loader2,
|
||||||
|
ArrowRight,
|
||||||
|
ArrowLeft,
|
||||||
|
ExternalLink,
|
||||||
|
Copy,
|
||||||
|
RefreshCw,
|
||||||
|
AlertTriangle,
|
||||||
|
Terminal,
|
||||||
|
XCircle,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import { StatusBadge } from '../components';
|
||||||
|
|
||||||
|
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<NodeJS.Timeout | null>(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) {
|
||||||
|
console.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) {
|
||||||
|
console.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 <StatusBadge status="checking" label="Checking..." />;
|
||||||
|
}
|
||||||
|
if (cursorCliStatus?.auth?.authenticated) {
|
||||||
|
return <StatusBadge status="authenticated" label="Ready" />;
|
||||||
|
}
|
||||||
|
if (cursorCliStatus?.installed) {
|
||||||
|
return <StatusBadge status="unverified" label="Not Logged In" />;
|
||||||
|
}
|
||||||
|
return <StatusBadge status="not_installed" label="Not Installed" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<div className="w-16 h-16 rounded-xl bg-cyan-500/10 flex items-center justify-center mx-auto mb-4">
|
||||||
|
<Terminal className="w-8 h-8 text-cyan-500" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold text-foreground mb-2">Cursor CLI Setup</h2>
|
||||||
|
<p className="text-muted-foreground">Optional - Use Cursor as an AI provider</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Info Banner */}
|
||||||
|
<Card className="bg-cyan-500/10 border-cyan-500/20">
|
||||||
|
<CardContent className="pt-4">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<AlertTriangle className="w-5 h-5 text-cyan-500 shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-foreground">This step is optional</p>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Configure Cursor CLI as an alternative AI provider. You can skip this and use Claude
|
||||||
|
instead, or configure it later in Settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Status Card */}
|
||||||
|
<Card className="bg-card border-border">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
|
<Terminal className="w-5 h-5" />
|
||||||
|
Cursor CLI Status
|
||||||
|
<Badge variant="outline" className="ml-2">
|
||||||
|
Optional
|
||||||
|
</Badge>
|
||||||
|
</CardTitle>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{getStatusBadge()}
|
||||||
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
|
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CardDescription>
|
||||||
|
{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'}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{/* Success State */}
|
||||||
|
{isReady && (
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-green-500" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-foreground">Cursor CLI is ready!</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
You can use Cursor models for AI tasks.
|
||||||
|
{cursorCliStatus?.version && (
|
||||||
|
<span className="ml-1">Version: {cursorCliStatus.version}</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Not Installed */}
|
||||||
|
{!cursorCliStatus?.installed && !isChecking && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-lg bg-muted/30 border border-border">
|
||||||
|
<XCircle className="w-5 h-5 text-muted-foreground shrink-0 mt-0.5" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-medium text-foreground">Cursor CLI not found</p>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Install the Cursor CLI to use Cursor models.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3 p-4 rounded-lg bg-muted/30 border border-border">
|
||||||
|
<p className="font-medium text-foreground text-sm">Install Cursor CLI:</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground overflow-x-auto">
|
||||||
|
{cursorCliStatus?.installCommand ||
|
||||||
|
'curl https://cursor.com/install -fsS | bash'}
|
||||||
|
</code>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() =>
|
||||||
|
copyCommand(
|
||||||
|
cursorCliStatus?.installCommand ||
|
||||||
|
'curl https://cursor.com/install -fsS | bash'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Copy className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="https://cursor.com/docs/cli"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center text-sm text-brand-500 hover:underline mt-2"
|
||||||
|
>
|
||||||
|
View installation docs
|
||||||
|
<ExternalLink className="w-3 h-3 ml-1" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Installed but not authenticated */}
|
||||||
|
{cursorCliStatus?.installed && !cursorCliStatus?.auth?.authenticated && !isChecking && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-lg bg-amber-500/10 border border-amber-500/20">
|
||||||
|
<AlertTriangle className="w-5 h-5 text-amber-500 shrink-0 mt-0.5" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-medium text-foreground">Cursor CLI not authenticated</p>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Run the login command to authenticate with Cursor.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3 p-4 rounded-lg bg-muted/30 border border-border">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Run the login command in your terminal, then complete authentication in your
|
||||||
|
browser:
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<code className="flex-1 bg-muted px-3 py-2 rounded text-sm font-mono text-foreground">
|
||||||
|
{cursorCliStatus?.loginCommand || 'cursor-agent login'}
|
||||||
|
</code>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() =>
|
||||||
|
copyCommand(cursorCliStatus?.loginCommand || 'cursor-agent login')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Copy className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={handleLogin}
|
||||||
|
disabled={isLoggingIn}
|
||||||
|
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
|
||||||
|
>
|
||||||
|
{isLoggingIn ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
Waiting for login...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Copy Command & Wait for Login'
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Loading State */}
|
||||||
|
{isChecking && (
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
|
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-foreground">Checking Cursor CLI status...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<div className="flex justify-between pt-4">
|
||||||
|
<Button variant="ghost" onClick={onBack} className="text-muted-foreground">
|
||||||
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button variant="ghost" onClick={onSkip} className="text-muted-foreground">
|
||||||
|
{isReady ? 'Skip' : 'Skip for now'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={onNext}
|
||||||
|
className="bg-brand-500 hover:bg-brand-600 text-white"
|
||||||
|
data-testid="cursor-next-button"
|
||||||
|
>
|
||||||
|
{isReady ? 'Continue' : 'Continue without Cursor'}
|
||||||
|
<ArrowRight className="w-4 h-4 ml-2" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Info note */}
|
||||||
|
<p className="text-xs text-muted-foreground text-center">
|
||||||
|
You can always configure Cursor later in Settings
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,4 +3,5 @@ export { WelcomeStep } from './welcome-step';
|
|||||||
export { ThemeStep } from './theme-step';
|
export { ThemeStep } from './theme-step';
|
||||||
export { CompleteStep } from './complete-step';
|
export { CompleteStep } from './complete-step';
|
||||||
export { ClaudeSetupStep } from './claude-setup-step';
|
export { ClaudeSetupStep } from './claude-setup-step';
|
||||||
|
export { CursorSetupStep } from './cursor-setup-step';
|
||||||
export { GitHubSetupStep } from './github-setup-step';
|
export { GitHubSetupStep } from './github-setup-step';
|
||||||
|
|||||||
@@ -551,6 +551,19 @@ export interface ElectronAPI {
|
|||||||
user: string | null;
|
user: string | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
}>;
|
}>;
|
||||||
|
getCursorStatus?: () => Promise<{
|
||||||
|
success: boolean;
|
||||||
|
installed?: boolean;
|
||||||
|
version?: string | null;
|
||||||
|
path?: string | null;
|
||||||
|
auth?: {
|
||||||
|
authenticated: boolean;
|
||||||
|
method: string;
|
||||||
|
};
|
||||||
|
installCommand?: string;
|
||||||
|
loginCommand?: string;
|
||||||
|
error?: string;
|
||||||
|
}>;
|
||||||
onInstallProgress?: (callback: (progress: any) => void) => () => void;
|
onInstallProgress?: (callback: (progress: any) => void) => () => void;
|
||||||
onAuthProgress?: (callback: (progress: any) => void) => () => void;
|
onAuthProgress?: (callback: (progress: any) => void) => () => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,20 @@ export interface GhCliStatus {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cursor CLI Status
|
||||||
|
export interface CursorCliStatus {
|
||||||
|
installed: boolean;
|
||||||
|
version?: string | null;
|
||||||
|
path?: string | null;
|
||||||
|
auth?: {
|
||||||
|
authenticated: boolean;
|
||||||
|
method: string;
|
||||||
|
};
|
||||||
|
installCommand?: string;
|
||||||
|
loginCommand?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Claude Auth Method - all possible authentication sources
|
// Claude Auth Method - all possible authentication sources
|
||||||
export type ClaudeAuthMethod =
|
export type ClaudeAuthMethod =
|
||||||
| 'oauth_token_env'
|
| 'oauth_token_env'
|
||||||
@@ -56,6 +70,7 @@ export type SetupStep =
|
|||||||
| 'theme'
|
| 'theme'
|
||||||
| 'claude_detect'
|
| 'claude_detect'
|
||||||
| 'claude_auth'
|
| 'claude_auth'
|
||||||
|
| 'cursor'
|
||||||
| 'github'
|
| 'github'
|
||||||
| 'complete';
|
| 'complete';
|
||||||
|
|
||||||
@@ -73,6 +88,9 @@ export interface SetupState {
|
|||||||
// GitHub CLI state
|
// GitHub CLI state
|
||||||
ghCliStatus: GhCliStatus | null;
|
ghCliStatus: GhCliStatus | null;
|
||||||
|
|
||||||
|
// Cursor CLI state
|
||||||
|
cursorCliStatus: CursorCliStatus | null;
|
||||||
|
|
||||||
// Setup preferences
|
// Setup preferences
|
||||||
skipClaudeSetup: boolean;
|
skipClaudeSetup: boolean;
|
||||||
}
|
}
|
||||||
@@ -94,6 +112,9 @@ export interface SetupActions {
|
|||||||
// GitHub CLI
|
// GitHub CLI
|
||||||
setGhCliStatus: (status: GhCliStatus | null) => void;
|
setGhCliStatus: (status: GhCliStatus | null) => void;
|
||||||
|
|
||||||
|
// Cursor CLI
|
||||||
|
setCursorCliStatus: (status: CursorCliStatus | null) => void;
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
setSkipClaudeSetup: (skip: boolean) => void;
|
setSkipClaudeSetup: (skip: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -118,6 +139,7 @@ const initialState: SetupState = {
|
|||||||
claudeInstallProgress: { ...initialInstallProgress },
|
claudeInstallProgress: { ...initialInstallProgress },
|
||||||
|
|
||||||
ghCliStatus: null,
|
ghCliStatus: null,
|
||||||
|
cursorCliStatus: null,
|
||||||
|
|
||||||
skipClaudeSetup: shouldSkipSetup,
|
skipClaudeSetup: shouldSkipSetup,
|
||||||
};
|
};
|
||||||
@@ -167,6 +189,9 @@ export const useSetupStore = create<SetupState & SetupActions>()(
|
|||||||
// GitHub CLI
|
// GitHub CLI
|
||||||
setGhCliStatus: (status) => set({ ghCliStatus: status }),
|
setGhCliStatus: (status) => set({ ghCliStatus: status }),
|
||||||
|
|
||||||
|
// Cursor CLI
|
||||||
|
setCursorCliStatus: (status) => set({ cursorCliStatus: status }),
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
setSkipClaudeSetup: (skip) => set({ skipClaudeSetup: skip }),
|
setSkipClaudeSetup: (skip) => set({ skipClaudeSetup: skip }),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
| 3 | [Provider Factory Integration](phases/phase-3-factory.md) | `completed` | ✅ |
|
| 3 | [Provider Factory Integration](phases/phase-3-factory.md) | `completed` | ✅ |
|
||||||
| 4 | [Setup Routes & Status Endpoints](phases/phase-4-routes.md) | `completed` | ✅ |
|
| 4 | [Setup Routes & Status Endpoints](phases/phase-4-routes.md) | `completed` | ✅ |
|
||||||
| 5 | [Log Parser Integration](phases/phase-5-log-parser.md) | `completed` | ✅ |
|
| 5 | [Log Parser Integration](phases/phase-5-log-parser.md) | `completed` | ✅ |
|
||||||
| 6 | [UI Setup Wizard](phases/phase-6-setup-wizard.md) | `pending` | - |
|
| 6 | [UI Setup Wizard](phases/phase-6-setup-wizard.md) | `completed` | ✅ |
|
||||||
| 7 | [Settings View Provider Tabs](phases/phase-7-settings.md) | `pending` | - |
|
| 7 | [Settings View Provider Tabs](phases/phase-7-settings.md) | `pending` | - |
|
||||||
| 8 | [AI Profiles Integration](phases/phase-8-profiles.md) | `pending` | - |
|
| 8 | [AI Profiles Integration](phases/phase-8-profiles.md) | `pending` | - |
|
||||||
| 9 | [Task Execution Integration](phases/phase-9-execution.md) | `pending` | - |
|
| 9 | [Task Execution Integration](phases/phase-9-execution.md) | `pending` | - |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Phase 6: UI Setup Wizard
|
# Phase 6: UI Setup Wizard
|
||||||
|
|
||||||
**Status:** `pending`
|
**Status:** `completed`
|
||||||
**Dependencies:** Phase 4 (Routes)
|
**Dependencies:** Phase 4 (Routes)
|
||||||
**Estimated Effort:** Medium (React component)
|
**Estimated Effort:** Medium (React component)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Add an optional Cursor CLI setup step to the welcome wizard, allowing users to c
|
|||||||
|
|
||||||
### Task 6.1: Create Cursor Setup Step Component
|
### Task 6.1: Create Cursor Setup Step Component
|
||||||
|
|
||||||
**Status:** `pending`
|
**Status:** `completed`
|
||||||
|
|
||||||
**File:** `apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx`
|
**File:** `apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx`
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ export default CursorSetupStep;
|
|||||||
|
|
||||||
### Task 6.2: Update Setup View Steps
|
### Task 6.2: Update Setup View Steps
|
||||||
|
|
||||||
**Status:** `pending`
|
**Status:** `completed`
|
||||||
|
|
||||||
**File:** `apps/ui/src/components/views/setup-view.tsx`
|
**File:** `apps/ui/src/components/views/setup-view.tsx`
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ function SetupView() {
|
|||||||
|
|
||||||
### Task 6.3: Add Step Indicator for Optional Steps
|
### Task 6.3: Add Step Indicator for Optional Steps
|
||||||
|
|
||||||
**Status:** `pending`
|
**Status:** `completed`
|
||||||
|
|
||||||
Add visual indicator for optional vs required steps in the progress bar.
|
Add visual indicator for optional vs required steps in the progress bar.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user