From 14e8d7be9b9ed2205cd051f14500f620caa8eda2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:24:44 +0000 Subject: [PATCH 1/6] Initial plan From a222edad4ddab516ba62b264cbf818b0e7e408d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:40:55 +0000 Subject: [PATCH 2/6] Refactor: Extract helper functions to reduce code duplication - Add parseHeaderInt helper to simplify header parsing - Add updateProgressStatus helper to consolidate rate limit/status updates - Add error handling helpers (logErrorAndReturn, handleFatalError, etc.) - Refactor PopulateSearchTable to use indexTable helper - Simplify updateRateLimitInfo using parseHeaderInt - Replace 60+ lines of duplicated error handling with helpers - Reduced code from 5782 to 5713 lines (69 line reduction) Co-authored-by: wham <448809+wham@users.noreply.github.com> --- main.go | 343 ++++++++++++++++++++++---------------------------------- 1 file changed, 137 insertions(+), 206 deletions(-) diff --git a/main.go b/main.go index 61e6f97..87fed4e 100644 --- a/main.go +++ b/main.go @@ -182,24 +182,15 @@ func updateRateLimitInfo(headers http.Header) { rateLimitInfoMutex.Lock() defer rateLimitInfoMutex.Unlock() - if limit := headers.Get("x-ratelimit-limit"); limit != "" { - if val, err := strconv.Atoi(limit); err == nil { - currentRateLimit.Limit = val - } + if val, ok := parseHeaderInt(headers, "x-ratelimit-limit"); ok { + currentRateLimit.Limit = val } - - if remaining := headers.Get("x-ratelimit-remaining"); remaining != "" { - if val, err := strconv.Atoi(remaining); err == nil { - currentRateLimit.Remaining = val - } + if val, ok := parseHeaderInt(headers, "x-ratelimit-remaining"); ok { + currentRateLimit.Remaining = val } - - if used := headers.Get("x-ratelimit-used"); used != "" { - if val, err := strconv.Atoi(used); err == nil { - currentRateLimit.Used = val - } + if val, ok := parseHeaderInt(headers, "x-ratelimit-used"); ok { + currentRateLimit.Used = val } - if reset := headers.Get("x-ratelimit-reset"); reset != "" { if val, err := strconv.ParseInt(reset, 10, 64); err == nil { currentRateLimit.Reset = time.Unix(val, 0) @@ -207,7 +198,6 @@ func updateRateLimitInfo(headers http.Header) { } } -// getRateLimitInfo returns a copy of the current rate limit information // updateStatusCounter increments the appropriate status code counter func updateStatusCounter(statusCode int) { statusMutex.Lock() @@ -1073,60 +1063,51 @@ func (db *DB) PopulateSearchTable(currentUsername string, progress ProgressInter progress.Log("Found %d repositories with your contributions (will receive 2x boost)", len(userReposMap)) slog.Info("User contribution repositories identified", "count", len(userReposMap), "username", currentUsername) - // Insert discussions with boost calculation - if discussionCount > 0 { - progress.Log("Indexing %d discussions into search table...", discussionCount) - slog.Info("Indexing discussions...") - _, err := db.Exec(` - INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) - SELECT 'discussion', title, body, url, repository, author, created_at, 'open', - CASE WHEN author = ? THEN 2.0 ELSE 1.0 END - FROM discussions - `, currentUsername) - if err != nil { - return fmt.Errorf("failed to populate discussions in search table: %w", err) - } - progress.Log("✅ Completed indexing %d discussions", discussionCount) - } else { - progress.Log("No discussions to index") - } - - // Insert issues with boost calculation - if issueCount > 0 { - progress.Log("Indexing %d issues into search table...", issueCount) - slog.Info("Indexing issues...") - _, err := db.Exec(` - INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) - SELECT 'issue', title, body, url, repository, author, created_at, - CASE WHEN closed_at IS NULL THEN 'open' ELSE 'closed' END, - CASE WHEN author = ? THEN 2.0 ELSE 1.0 END - FROM issues - `, currentUsername) - if err != nil { - return fmt.Errorf("failed to populate issues in search table: %w", err) - } - progress.Log("✅ Completed indexing %d issues", issueCount) - } else { - progress.Log("No issues to index") - } - - // Insert pull requests with boost calculation - if prCount > 0 { - progress.Log("Indexing %d pull requests into search table...", prCount) - slog.Info("Indexing pull requests...") - _, err := db.Exec(` - INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) - SELECT 'pull_request', title, body, url, repository, author, created_at, - CASE WHEN closed_at IS NULL THEN 'open' ELSE 'closed' END, - CASE WHEN author = ? THEN 2.0 ELSE 1.0 END - FROM pull_requests - `, currentUsername) - if err != nil { - return fmt.Errorf("failed to populate pull_requests in search table: %w", err) - } - progress.Log("✅ Completed indexing %d pull requests", prCount) - } else { - progress.Log("No pull requests to index") + // Helper to index a table type into search + indexTable := func(tableName, typeName string, count int, query string) error { + if count == 0 { + progress.Log("No %s to index", tableName) + return nil + } + progress.Log("Indexing %d %s into search table...", count, tableName) + slog.Info("Indexing " + tableName + "...") + if _, err := db.Exec(query, currentUsername); err != nil { + return fmt.Errorf("failed to populate %s in search table: %w", tableName, err) + } + progress.Log("✅ Completed indexing %d %s", count, tableName) + return nil + } + + // Insert discussions + if err := indexTable("discussions", "discussion", discussionCount, ` + INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) + SELECT 'discussion', title, body, url, repository, author, created_at, 'open', + CASE WHEN author = ? THEN 2.0 ELSE 1.0 END + FROM discussions + `); err != nil { + return err + } + + // Insert issues + if err := indexTable("issues", "issue", issueCount, ` + INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) + SELECT 'issue', title, body, url, repository, author, created_at, + CASE WHEN closed_at IS NULL THEN 'open' ELSE 'closed' END, + CASE WHEN author = ? THEN 2.0 ELSE 1.0 END + FROM issues + `); err != nil { + return err + } + + // Insert pull requests + if err := indexTable("pull requests", "pull_request", prCount, ` + INSERT INTO search(type, title, body, url, repository, author, created_at, state, boost) + SELECT 'pull_request', title, body, url, repository, author, created_at, + CASE WHEN closed_at IS NULL THEN 'open' ELSE 'closed' END, + CASE WHEN author = ? THEN 2.0 ELSE 1.0 END + FROM pull_requests + `); err != nil { + return err } progress.Log("🎉 Search index rebuild completed successfully with %d total items", totalItems) @@ -2261,15 +2242,9 @@ func PullRepositories(ctx context.Context, client *githubv4.Client, db *DB, conf // Update spinner speed based on request rate progress.UpdateRequestRate(int(requestsInLastSecond)) - // Update rate limit display from global state - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() +// Update rate limit and API status display from global state +updateProgressStatus(progress) - // Update API status display from global counters - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() } } }() @@ -2646,15 +2621,9 @@ func PullDiscussions(ctx context.Context, client *githubv4.Client, db *DB, confi // Update spinner speed based on request rate progress.UpdateRequestRate(int(requestsInLastSecond)) - // Update rate limit display from global state - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() +// Update rate limit and API status display from global state +updateProgressStatus(progress) - // Update API status display from global counters - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() } } }() @@ -2894,15 +2863,9 @@ func PullIssues(ctx context.Context, client *githubv4.Client, db *DB, config *Co progress.UpdateRequestRate(int(requestsInLastSecond)) - // Update rate limit display from global state - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() +// Update rate limit and API status display from global state +updateProgressStatus(progress) - // Update API status display from global counters - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() } } }() @@ -3146,15 +3109,9 @@ func PullPullRequests(ctx context.Context, client *githubv4.Client, db *DB, conf progress.UpdateRequestRate(int(requestsInLastSecond)) - // Update rate limit display from global state - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() +// Update rate limit and API status display from global state +updateProgressStatus(progress) - // Update API status display from global counters - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() } } }() @@ -4196,6 +4153,53 @@ func RunMCPServer(db *DB) error { // UI Server Implementation // ============================================================================ +// logErrorAndReturn logs an error message, waits for display, and returns (for use in main goroutine with defer) +func logErrorAndReturn(progress ProgressInterface, format string, args ...interface{}) { + progress.Log(format, args...) + time.Sleep(3 * time.Second) +} + +// handleFatalError logs an error and exits gracefully after a delay +func handleFatalError(progress ProgressInterface, format string, args ...interface{}) { + logErrorAndReturn(progress, format, args...) + progress.Stop() + os.Exit(1) +} + +// handlePullItemError marks an item as failed and exits +func handlePullItemError(progress ProgressInterface, item string, err error) { + progress.MarkItemFailed(item, err.Error()) + handleFatalError(progress, "Error: %v", err) +} + +// checkPreviousFailures checks if any previous item failed and exits if so +func checkPreviousFailures(progress ProgressInterface, currentItem string) { + if progress.HasAnyFailed() { + handleFatalError(progress, "Skipping %s due to previous failures", currentItem) + } +} + +// updateProgressStatus updates the progress UI with current rate limit and API status +func updateProgressStatus(progress ProgressInterface) { + rateLimitInfoMutex.RLock() + progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) + rateLimitInfoMutex.RUnlock() + + statusMutex.Lock() + progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) + statusMutex.Unlock() +} + +// parseHeaderInt safely parses an integer from an HTTP header +func parseHeaderInt(headers http.Header, key string) (int, bool) { + if value := headers.Get(key); value != "" { + if val, err := strconv.Atoi(value); err == nil { + return val, true + } + } + return 0, false +} + func main() { // Handle --version flag before any other processing if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-v") { @@ -4293,15 +4297,11 @@ func main() { // Continue with the original logic if config.GithubToken == "" { - progress.Log("Error: GitHub token is required. Run 'github-brain login' or set GITHUB_TOKEN environment variable.") - // Give console time to display the error before exiting - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: GitHub token is required. Run 'github-brain login' or set GITHUB_TOKEN environment variable.") return } if config.Organization == "" { - progress.Log("Error: Organization is required. Use -o or set ORGANIZATION environment variable.") - // Give console time to display the error before exiting - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: Organization is required. Use -o or set ORGANIZATION environment variable.") return } @@ -4322,9 +4322,7 @@ func main() { } for _, item := range config.Items { if !validItems[item] { - progress.Log("Error: Invalid item: %s. Valid items are: repositories, discussions, issues, pull-requests", item) - // Give console time to display the error before exiting - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: Invalid item: %s. Valid items are: repositories, discussions, issues, pull-requests", item) return } } @@ -4351,8 +4349,7 @@ func main() { if _, err := os.Stat(config.HomeDir); os.IsNotExist(err) { progress.Log("Creating GitHub Brain home directory: %s", config.HomeDir) if err := os.MkdirAll(config.HomeDir, 0755); err != nil { - progress.Log("Error: Failed to create home directory: %v", err) - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: Failed to create home directory: %v", err) return } } @@ -4361,17 +4358,14 @@ func main() { progress.Log("Initializing database at path: %s", getDBPath(config.DBDir, config.Organization)) db, err := InitDB(config.DBDir, config.Organization, progress) if err != nil { - progress.Log("Error: Failed to initialize database: %v", err) - // Give console time to display the error before exiting - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: Failed to initialize database: %v", err) return } defer db.Close() // Acquire lock to prevent concurrent pull operations if err := db.LockPull(); err != nil { - progress.Log("Error: Failed to acquire lock: %v", err) - time.Sleep(3 * time.Second) + logErrorAndReturn(progress, "Error: Failed to acquire lock: %v", err) return } @@ -4438,38 +4432,19 @@ func main() { statusMutex.Unlock() // Even on error, update UI with any rate limit info we captured - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() - - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() + updateProgressStatus(progress) progress.Log("Error: Failed to fetch current user: %v", err) progress.Log("Please run 'login' again to re-authenticate") - // Give user time to see the error before stopping - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) + handleFatalError(progress, "") } currentUsername := currentUser.Viewer.Login progress.Log("Authenticated as user: %s", currentUsername) // Update UI with rate limit info from the user query response - rateLimitInfoMutex.RLock() - progress.UpdateRateLimit(currentRateLimit.Used, currentRateLimit.Limit, currentRateLimit.Reset) - rateLimitInfoMutex.RUnlock() - - // Update API status from the user query - statusMutex.Lock() - progress.UpdateAPIStatus(statusCounters.Success2XX, statusCounters.Error4XX, statusCounters.Error5XX) - statusMutex.Unlock() // Clear data if Force flag is set + updateProgressStatus(progress) // Clear data if Force flag is set if err := ClearData(db, config, progress); err != nil { - progress.Log("Error: Failed to clear data: %v", err) - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) + handleFatalError(progress, "Error: Failed to clear data: %v", err) } // No longer deleting data from other organizations - keeping all data @@ -4479,79 +4454,35 @@ func main() { if pullRepositories { if err := PullRepositories(ctx, graphqlClient, db, config, progress); err != nil { progress.MarkItemFailed("repositories", err.Error()) - progress.Log("Failed to pull repositories: %v", err) - // Stop processing subsequent items if repositories failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) + handleFatalError(progress, "Failed to pull repositories: %v", err) } } - // Pull discussions if requested - if pullDiscussions { - // Check if any previous item failed - if progress.HasAnyFailed() { - progress.Log("Skipping discussions due to previous failures") - // Exit early if any previous item failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } - - if err := PullDiscussions(ctx, graphqlClient, db, config, progress); err != nil { - progress.MarkItemFailed("discussions", err.Error()) - progress.Log("Error: %v", err) - // Stop processing subsequent items if discussions failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } + // Pull discussions if requested + if pullDiscussions { + checkPreviousFailures(progress, "discussions") + if err := PullDiscussions(ctx, graphqlClient, db, config, progress); err != nil { + handlePullItemError(progress, "discussions", err) } + } - // Pull issues if requested - if pullIssues { - // Check if any previous item failed - if progress.HasAnyFailed() { - progress.Log("Skipping issues due to previous failures") - // Exit early if any previous item failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } - - if err := PullIssues(ctx, graphqlClient, db, config, progress); err != nil { - progress.MarkItemFailed("issues", err.Error()) - progress.Log("Error: %v", err) - // Stop processing subsequent items if issues failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } + // Pull issues if requested + if pullIssues { + checkPreviousFailures(progress, "issues") + if err := PullIssues(ctx, graphqlClient, db, config, progress); err != nil { + handlePullItemError(progress, "issues", err) } + } - // Pull pull requests if requested - if pullPullRequests { - // Check if any previous item failed - if progress.HasAnyFailed() { - progress.Log("Skipping pull requests due to previous failures") - // Exit early if any previous item failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } - - progress.Log("Starting pull requests operation") - - progress.Log("About to call PullPullRequests") - if err := PullPullRequests(ctx, graphqlClient, db, config, progress); err != nil { - progress.MarkItemFailed("pull-requests", err.Error()) - progress.Log("Error: %v", err) - // Stop processing subsequent items if pull requests failed - time.Sleep(3 * time.Second) - progress.Stop() - os.Exit(1) - } + // Pull pull requests if requested + if pullPullRequests { + checkPreviousFailures(progress, "pull requests") + progress.Log("Starting pull requests operation") + progress.Log("About to call PullPullRequests") + if err := PullPullRequests(ctx, graphqlClient, db, config, progress); err != nil { + handlePullItemError(progress, "pull-requests", err) } + } // Truncate search FTS5 table and repopulate it from discussions, issues, and pull_requests tables progress.UpdateMessage("Updating search index...") From 27e0a73e477548af9a49b8a202924094ebe897e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:45:01 +0000 Subject: [PATCH 3/6] Refactor: Extract gradient colors to shared variable - Move duplicate gradient color definitions to global variable - Reduced duplication in newModel and newLoginModel - Removed 16 lines of duplicate color definitions - Further reduced from 5713 to 5704 lines Co-authored-by: wham <448809+wham@users.noreply.github.com> --- main.go | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/main.go b/main.go index 87fed4e..0eb0c8d 100644 --- a/main.go +++ b/main.go @@ -62,6 +62,16 @@ var ( statusMutex sync.Mutex ) +// gradientColors defines the color gradient for UI borders (purple → blue → cyan) +var gradientColors = []lipgloss.AdaptiveColor{ + {Light: "#874BFD", Dark: "#7D56F4"}, // Purple + {Light: "#7D56F4", Dark: "#6B4FD8"}, // Purple-blue + {Light: "#5B4FE0", Dark: "#5948C8"}, // Blue-purple + {Light: "#4F7BD8", Dark: "#4B6FD0"}, // Blue + {Light: "#48A8D8", Dark: "#45A0D0"}, // Cyan-blue + {Light: "#48D8D0", Dark: "#45D0C8"}, // Cyan +} + // Removed ConsoleHandler - not needed with Bubble Tea // BubbleTeaHandler is a custom slog handler that routes logs to Bubble Tea UI @@ -4763,16 +4773,6 @@ func newModel(enabledItems map[string]bool) model { s.Spinner = spinner.Dot s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("12")) // Bright blue - // Define gradient colors for border animation (purple → blue → cyan) - gradientColors := []lipgloss.AdaptiveColor{ - {Light: "#874BFD", Dark: "#7D56F4"}, // Purple - {Light: "#7D56F4", Dark: "#6B4FD8"}, // Purple-blue - {Light: "#5B4FE0", Dark: "#5948C8"}, // Blue-purple - {Light: "#4F7BD8", Dark: "#4B6FD0"}, // Blue - {Light: "#48A8D8", Dark: "#45A0D0"}, // Cyan-blue - {Light: "#48D8D0", Dark: "#45D0C8"}, // Cyan - } - itemOrder := []string{"repositories", "discussions", "issues", "pull-requests"} items := make(map[string]itemState) for _, name := range itemOrder { @@ -5202,15 +5202,6 @@ func newLoginModel(homeDir string) loginModel { ti.Prompt = "> " ti.PromptStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("12")) - gradientColors := []lipgloss.AdaptiveColor{ - {Light: "#874BFD", Dark: "#7D56F4"}, - {Light: "#7D56F4", Dark: "#6B4FD8"}, - {Light: "#5B4FE0", Dark: "#5948C8"}, - {Light: "#4F7BD8", Dark: "#4B6FD0"}, - {Light: "#48A8D8", Dark: "#45A0D0"}, - {Light: "#48D8D0", Dark: "#45D0C8"}, - } - return loginModel{ spinner: s, textInput: ti, From 0a90997a66f33907698d703db0a42e7c89191c84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:46:59 +0000 Subject: [PATCH 4/6] Refactor: Simplify item processing logic - Replace switch statements with map lookups for item flags - Consolidate ClearData function logic using map and shared loop - More concise and maintainable item processing - Reduced from 5704 to 5684 lines (20 line reduction) - Total reduction: 98 lines from original 5782 Co-authored-by: wham <448809+wham@users.noreply.github.com> --- main.go | 78 +++++++++++++++++++++------------------------------------ 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/main.go b/main.go index 0eb0c8d..a2ffc30 100644 --- a/main.go +++ b/main.go @@ -2157,45 +2157,33 @@ func ClearData(db *DB, config *Config, progress ProgressInterface) error { return nil } - // If specific items are provided, clear only those + // Map item names to table names + itemToTable := map[string]string{ + "repositories": "repositories", + "discussions": "discussions", + "issues": "issues", + "pull-requests": "pull_requests", + } + + // Determine which tables to clear + tablesToClear := []string{} if len(config.Items) > 0 { + // Clear only specified items for _, item := range config.Items { - switch item { - case "repositories": - progress.Log("Deleting repositories table") - _, err := db.Exec("DELETE FROM repositories") - if err != nil { - return fmt.Errorf("failed to clear repositories: %w", err) - } - case "discussions": - progress.Log("Deleting discussions table") - _, err := db.Exec("DELETE FROM discussions") - if err != nil { - return fmt.Errorf("failed to clear discussions: %w", err) - } - case "issues": - progress.Log("Deleting issues table") - _, err := db.Exec("DELETE FROM issues") - if err != nil { - return fmt.Errorf("failed to clear issues: %w", err) - } - case "pull-requests": - progress.Log("Deleting pull_requests table") - _, err := db.Exec("DELETE FROM pull_requests") - if err != nil { - return fmt.Errorf("failed to clear pull requests: %w", err) - } - + if table, ok := itemToTable[item]; ok { + tablesToClear = append(tablesToClear, table) } } } else { - // Clear all data - tables := []string{"pull_requests", "issues", "discussions", "repositories"} - for _, table := range tables { - _, err := db.Exec(fmt.Sprintf("DELETE FROM %s", table)) - if err != nil { - return fmt.Errorf("failed to clear %s: %w", table, err) - } + // Clear all tables + tablesToClear = []string{"pull_requests", "issues", "discussions", "repositories"} + } + + // Clear the tables + for _, table := range tablesToClear { + progress.Log("Deleting %s table", table) + if _, err := db.Exec(fmt.Sprintf("DELETE FROM %s", table)); err != nil { + return fmt.Errorf("failed to clear %s: %w", table, err) } } @@ -4337,23 +4325,15 @@ func main() { } } - // Check if we should pull each item type - pullRepositories := false - pullDiscussions := false - pullIssues := false - pullPullRequests := false + // Check if we should pull each item type (convert to map for efficient lookup) + itemsMap := make(map[string]bool) for _, item := range config.Items { - switch item { - case "repositories": - pullRepositories = true - case "discussions": - pullDiscussions = true - case "issues": - pullIssues = true - case "pull-requests": - pullPullRequests = true - } + itemsMap[item] = true } + pullRepositories := itemsMap["repositories"] + pullDiscussions := itemsMap["discussions"] + pullIssues := itemsMap["issues"] + pullPullRequests := itemsMap["pull-requests"] // Create GitHub Brain home directory if it doesn't exist if _, err := os.Stat(config.HomeDir); os.IsNotExist(err) { From 6f013d3b8b05ab81c8ed67fd41430711e19df917 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:50:11 +0000 Subject: [PATCH 5/6] Fix code formatting issues from refactoring - Fix indentation of updateProgressStatus calls and comments - Separate misplaced comment onto correct line - Address all code review feedback - Code still builds successfully Co-authored-by: wham <448809+wham@users.noreply.github.com> --- main.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index a2ffc30..2163194 100644 --- a/main.go +++ b/main.go @@ -2240,8 +2240,8 @@ func PullRepositories(ctx context.Context, client *githubv4.Client, db *DB, conf // Update spinner speed based on request rate progress.UpdateRequestRate(int(requestsInLastSecond)) -// Update rate limit and API status display from global state -updateProgressStatus(progress) + // Update rate limit and API status display from global state + updateProgressStatus(progress) } } @@ -2619,8 +2619,8 @@ func PullDiscussions(ctx context.Context, client *githubv4.Client, db *DB, confi // Update spinner speed based on request rate progress.UpdateRequestRate(int(requestsInLastSecond)) -// Update rate limit and API status display from global state -updateProgressStatus(progress) + // Update rate limit and API status display from global state + updateProgressStatus(progress) } } @@ -2861,8 +2861,8 @@ func PullIssues(ctx context.Context, client *githubv4.Client, db *DB, config *Co progress.UpdateRequestRate(int(requestsInLastSecond)) -// Update rate limit and API status display from global state -updateProgressStatus(progress) + // Update rate limit and API status display from global state + updateProgressStatus(progress) } } @@ -3107,8 +3107,8 @@ func PullPullRequests(ctx context.Context, client *githubv4.Client, db *DB, conf progress.UpdateRequestRate(int(requestsInLastSecond)) -// Update rate limit and API status display from global state -updateProgressStatus(progress) + // Update rate limit and API status display from global state + updateProgressStatus(progress) } } @@ -4432,7 +4432,9 @@ func main() { progress.Log("Authenticated as user: %s", currentUsername) // Update UI with rate limit info from the user query response - updateProgressStatus(progress) // Clear data if Force flag is set + updateProgressStatus(progress) + + // Clear data if Force flag is set if err := ClearData(db, config, progress); err != nil { handleFatalError(progress, "Error: Failed to clear data: %v", err) } From 0928c288503fcdd881d51d257bd08155db57bee4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:53:43 +0000 Subject: [PATCH 6/6] Add exitAfterDelay helper to improve error handling clarity - Add new helper function for cases where errors are already logged - Replace handleFatalError("") with exitAfterDelay for clearer intent - Improves code readability and addresses code review feedback - Line count: 5693 (still 89 lines reduction from original 5782) Co-authored-by: wham <448809+wham@users.noreply.github.com> --- main.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 2163194..6df4ac3 100644 --- a/main.go +++ b/main.go @@ -4157,6 +4157,13 @@ func logErrorAndReturn(progress ProgressInterface, format string, args ...interf time.Sleep(3 * time.Second) } +// exitAfterDelay waits for display and exits (for use when errors are already logged) +func exitAfterDelay(progress ProgressInterface) { + time.Sleep(3 * time.Second) + progress.Stop() + os.Exit(1) +} + // handleFatalError logs an error and exits gracefully after a delay func handleFatalError(progress ProgressInterface, format string, args ...interface{}) { logErrorAndReturn(progress, format, args...) @@ -4426,7 +4433,7 @@ func main() { progress.Log("Error: Failed to fetch current user: %v", err) progress.Log("Please run 'login' again to re-authenticate") - handleFatalError(progress, "") + exitAfterDelay(progress) } currentUsername := currentUser.Viewer.Login progress.Log("Authenticated as user: %s", currentUsername)