feat(ui): Enhance AI model handling with Cursor support

- Refactor model handling to support both Claude and Cursor models across various components.
- Introduce `stripProviderPrefix` utility for consistent model ID processing.
- Update `CursorProvider` to utilize `isCursorModel` for model validation.
- Implement model override functionality in GitHub issue validation and enhancement routes.
- Add `useCursorStatusInit` hook to initialize Cursor CLI status on app startup.
- Update UI components to reflect changes in model selection and validation processes.

This update improves the flexibility of AI model usage and enhances user experience by allowing quick model overrides.
This commit is contained in:
Kacper
2025-12-30 04:01:56 +01:00
parent 3d655c3298
commit 39f2c8c9ff
38 changed files with 713 additions and 258 deletions

View File

@@ -1,10 +1,10 @@
import type { AgentModel, ThinkingLevel } from '@/store/app-store';
import type { ModelAlias, ThinkingLevel } from '@/store/app-store';
import type { ModelProvider } from '@automaker/types';
import { CURSOR_MODEL_MAP } from '@automaker/types';
import { Brain, Zap, Scale, Cpu, Rocket, Sparkles } from 'lucide-react';
export type ModelOption = {
id: string; // Claude models use AgentModel, Cursor models use "cursor-{id}"
id: string; // Claude models use ModelAlias, Cursor models use "cursor-{id}"
label: string;
description: string;
badge?: string;

View File

@@ -2,28 +2,19 @@ import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Brain, Bot, Terminal, AlertTriangle } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { AgentModel } from '@/store/app-store';
import type { ModelAlias } from '@/store/app-store';
import { useAppStore } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types';
import type { ModelProvider } from '@automaker/types';
import { CLAUDE_MODELS, CURSOR_MODELS, ModelOption } from './model-constants';
interface ModelSelectorProps {
selectedModel: string; // Can be AgentModel or "cursor-{id}"
selectedModel: string; // Can be ModelAlias or "cursor-{id}"
onModelSelect: (model: string) => void;
testIdPrefix?: string;
}
/**
* Get the provider from a model string
*/
function getProviderFromModelString(model: string): ModelProvider {
if (model.startsWith('cursor-')) {
return 'cursor';
}
return 'claude';
}
export function ModelSelector({
selectedModel,
onModelSelect,
@@ -32,7 +23,7 @@ export function ModelSelector({
const { enabledCursorModels, cursorDefaultModel } = useAppStore();
const { cursorCliStatus } = useSetupStore();
const selectedProvider = getProviderFromModelString(selectedModel);
const selectedProvider = getModelProvider(selectedModel);
// Check if Cursor CLI is available
const isCursorAvailable = cursorCliStatus?.installed && cursorCliStatus?.auth?.authenticated;
@@ -40,14 +31,14 @@ export function ModelSelector({
// Filter Cursor models based on enabled models from global settings
const filteredCursorModels = CURSOR_MODELS.filter((model) => {
// Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto")
const cursorModelId = model.id.replace('cursor-', '');
const cursorModelId = stripProviderPrefix(model.id);
return enabledCursorModels.includes(cursorModelId as any);
});
const handleProviderChange = (provider: ModelProvider) => {
if (provider === 'cursor' && selectedProvider !== 'cursor') {
// Switch to Cursor's default model (from global settings)
onModelSelect(`cursor-${cursorDefaultModel}`);
onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`);
} else if (provider === 'claude' && selectedProvider !== 'claude') {
// Switch to Claude's default model
onModelSelect('sonnet');

View File

@@ -1,8 +1,8 @@
import { Label } from '@/components/ui/label';
import { Brain, UserCircle, Terminal } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { AgentModel, ThinkingLevel, AIProfile } from '@automaker/types';
import { CURSOR_MODEL_MAP, profileHasThinking } from '@automaker/types';
import type { ModelAlias, ThinkingLevel, AIProfile, CursorModelId } from '@automaker/types';
import { CURSOR_MODEL_MAP, profileHasThinking, PROVIDER_PREFIXES } from '@automaker/types';
import { PROFILE_ICONS } from './model-constants';
/**
@@ -32,7 +32,7 @@ function getProfileThinkingDisplay(profile: AIProfile): string | null {
interface ProfileQuickSelectProps {
profiles: AIProfile[];
selectedModel: AgentModel;
selectedModel: ModelAlias | CursorModelId;
selectedThinkingLevel: ThinkingLevel;
selectedCursorModel?: string; // For detecting cursor profile selection
onSelect: (profile: AIProfile) => void; // Changed to pass full profile
@@ -62,7 +62,7 @@ export function ProfileQuickSelect({
const isProfileSelected = (profile: AIProfile): boolean => {
if (profile.provider === 'cursor') {
// For cursor profiles, check if cursor model matches
const profileCursorModel = `cursor-${profile.cursorModel || 'auto'}`;
const profileCursorModel = `${PROVIDER_PREFIXES.cursor}${profile.cursorModel || 'auto'}`;
return selectedCursorModel === profileCursorModel;
}
// For Claude profiles