import type { ClassifiedError } from '../../shared/types' // Why: gh CLI surfaces API errors as unstructured stderr. Map known // patterns to typed errors so callers can show user-friendly messages. export function classifyGhError(stderr: string): ClassifiedError { const s = stderr.toLowerCase() // Why: primary rate-limit errors also carry "HTTP 403" — check rate limit // first so they don't misclassify as a token-scope problem. if (s.includes('rate limit')) { return { type: 'rate_limited', message: 'GitHub rate limit hit. Try again in a few minutes.' } } if (s.includes('http 403') || s.includes('resource not accessible')) { return { type: 'permission_denied', message: "You don't have permission to edit this issue. Check your GitHub token scopes." } } if (s.includes('http 404') || s.includes('could not resolve to a repository')) { return { type: 'not_found', message: 'Issue not found — it may have been deleted.' } } if (s.includes('has disabled issues')) { return { type: 'issues_disabled', message: 'Issues are disabled on this repository.' } } if (s.includes('http 422') || s.includes('validation failed')) { return { type: 'validation_error', message: `Invalid update — ${stderr.trim()}` } } if ( s.includes('timeout') || s.includes('no such host') || s.includes('network') || s.includes('could not resolve host') ) { return { type: 'network_error', message: 'Network error — check your connection.' } } return { type: 'unknown', message: `Failed to update issue: ${stderr.trim()}` } } // Why: classifyGhError's copy is phrased for edit/update operations, but // listIssues is a read op and renderer banners interpolate the message. export function classifyListIssuesError(stderr: string): ClassifiedError { const c = classifyGhError(stderr) const trimmed = stderr.trim() const readMessages: Record = { permission_denied: "You don't have permission to read issues for this repository. Check your GitHub token scopes.", not_found: 'Repository not found.', issues_disabled: 'Issues are disabled on this repository.', validation_error: `Invalid request — ${trimmed}`, rate_limited: 'GitHub rate limit hit. Try again in a few minutes.', network_error: 'Network error — check your connection.', unknown: `Failed to load issues: ${trimmed}` } return { type: c.type, message: readMessages[c.type] } } // Why: PR-side list failures need the same read-op classification — pagination // decisions key on the type, and swallowing them made failures look like // end-of-data (#11485). export function classifyListPrsError(stderr: string): ClassifiedError { const c = classifyGhError(stderr) return { type: c.type, message: `Failed to load pull requests: ${stderr.trim()}` } }