source dump of claude code
at main 106 lines 2.8 kB view raw
1import { execFileNoThrow } from './execFileNoThrow.js' 2import { getBranch, getDefaultBranch, getIsGit } from './git.js' 3import { jsonParse } from './slowOperations.js' 4 5export type PrReviewState = 6 | 'approved' 7 | 'pending' 8 | 'changes_requested' 9 | 'draft' 10 | 'merged' 11 | 'closed' 12 13export type PrStatus = { 14 number: number 15 url: string 16 reviewState: PrReviewState 17} 18 19const GH_TIMEOUT_MS = 5000 20 21/** 22 * Derive review state from GitHub API values. 23 * Draft PRs always show as 'draft' regardless of reviewDecision. 24 * reviewDecision can be: APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED, or empty string. 25 */ 26export function deriveReviewState( 27 isDraft: boolean, 28 reviewDecision: string, 29): PrReviewState { 30 if (isDraft) return 'draft' 31 switch (reviewDecision) { 32 case 'APPROVED': 33 return 'approved' 34 case 'CHANGES_REQUESTED': 35 return 'changes_requested' 36 default: 37 return 'pending' 38 } 39} 40 41/** 42 * Fetch PR status for the current branch using `gh pr view`. 43 * Returns null on any failure (gh not installed, no PR, not in git repo, etc). 44 * Also returns null if the PR's head branch is the default branch (e.g., main/master). 45 */ 46export async function fetchPrStatus(): Promise<PrStatus | null> { 47 const isGit = await getIsGit() 48 if (!isGit) return null 49 50 // Skip on the default branch — `gh pr view` returns the most recently 51 // merged PR there, which is misleading. 52 const [branch, defaultBranch] = await Promise.all([ 53 getBranch(), 54 getDefaultBranch(), 55 ]) 56 if (branch === defaultBranch) return null 57 58 const { stdout, code } = await execFileNoThrow( 59 'gh', 60 [ 61 'pr', 62 'view', 63 '--json', 64 'number,url,reviewDecision,isDraft,headRefName,state', 65 ], 66 { timeout: GH_TIMEOUT_MS, preserveOutputOnError: false }, 67 ) 68 69 if (code !== 0 || !stdout.trim()) return null 70 71 try { 72 const data = jsonParse(stdout) as { 73 number: number 74 url: string 75 reviewDecision: string 76 isDraft: boolean 77 headRefName: string 78 state: string 79 } 80 81 // Don't show PR status for PRs from the default branch (e.g., main, master) 82 // This can happen when someone opens a PR from main to another branch 83 if ( 84 data.headRefName === defaultBranch || 85 data.headRefName === 'main' || 86 data.headRefName === 'master' 87 ) { 88 return null 89 } 90 91 // Don't show PR status for merged or closed PRs — `gh pr view` returns 92 // the most recently associated PR for a branch, which may be merged/closed. 93 // The status line should only display open PRs. 94 if (data.state === 'MERGED' || data.state === 'CLOSED') { 95 return null 96 } 97 98 return { 99 number: data.number, 100 url: data.url, 101 reviewState: deriveReviewState(data.isDraft, data.reviewDecision), 102 } 103 } catch { 104 return null 105 } 106}