package models import ( "cursor-api-proxy/internal/process" "fmt" "os" "regexp" "strings" ) type CursorCliModel struct { ID string Name string } var modelLineRe = regexp.MustCompile(`^([A-Za-z0-9][A-Za-z0-9._:/-]*)\s+-\s+(.*)$`) var trailingParenRe = regexp.MustCompile(`\s*\([^)]*\)\s*$`) func ParseCursorCliModels(output string) []CursorCliModel { lines := strings.Split(output, "\n") seen := make(map[string]CursorCliModel) var order []string for _, line := range lines { line = strings.TrimSpace(line) m := modelLineRe.FindStringSubmatch(line) if m == nil { continue } id := m[1] rawName := m[2] name := strings.TrimSpace(trailingParenRe.ReplaceAllString(rawName, "")) if name == "" { name = id } if _, exists := seen[id]; !exists { seen[id] = CursorCliModel{ID: id, Name: name} order = append(order, id) } } result := make([]CursorCliModel, 0, len(order)) for _, id := range order { result = append(result, seen[id]) } return result } func ListCursorCliModels(agentBin string, timeoutMs int) ([]CursorCliModel, error) { tmpDir := os.TempDir() result, err := process.Run(agentBin, []string{"--list-models"}, process.RunOptions{ Cwd: tmpDir, TimeoutMs: timeoutMs, }) if err != nil { return nil, err } if result.Code != 0 { return nil, fmt.Errorf("agent --list-models failed: %s", strings.TrimSpace(result.Stderr)) } return ParseCursorCliModels(result.Stdout), nil }