/ lib / types.ts
types.ts
  1  export type SlideType = 'foundation' | 'feature' | 'refactor' | 'bugfix' | 'test' | 'config' | 'docs';
  2  
  3  export interface CiCheck {
  4    name: string;
  5    status: 'queued' | 'in_progress' | 'completed';
  6    conclusion: string | null;
  7  }
  8  
  9  export interface ReviewSummary {
 10    approved: number;
 11    changesRequested: number;
 12    commented: number;
 13  }
 14  
 15  export interface DiffHunk {
 16    filePath: string;
 17    hunkHeader: string;
 18    content: string;
 19    language: string;
 20    renderedHtml: string;
 21  }
 22  
 23  export interface ReviewCheck {
 24    text: string;
 25    filePath?: string;
 26    startLine?: number;
 27  }
 28  
 29  export interface Slide {
 30    id: string;
 31    slideNumber: number;
 32    title: string;
 33    slideType: SlideType;
 34    narrative: string;
 35    reviewFocus: string | null;
 36    diffHunks: DiffHunk[];
 37    contextSnippets: string[];
 38    affectedFiles: string[];
 39    dependsOn: string[];
 40    mermaidDiagram?: string | null;
 41    reviewChecks?: ReviewCheck[];
 42  }
 43  
 44  // Intermediate types for raw AI response (before hunk resolution)
 45  export interface AISlide {
 46    id: string;
 47    slideNumber: number;
 48    title: string;
 49    slideType: SlideType;
 50    narrative: string;
 51    reviewFocus: string | null;
 52    diffHunkIds: string[];
 53    contextSnippets: string[];
 54    affectedFiles: string[];
 55    dependsOn: string[];
 56    mermaidDiagram?: string | null;
 57    reviewChecks?: ReviewCheck[];
 58  }
 59  
 60  export interface WebSource {
 61    url: string;
 62    title: string;
 63  }
 64  
 65  export interface AIReviewGuide {
 66    prTitle: string;
 67    prDescription: string;
 68    prUrl: string;
 69    author: string;
 70    summary: string;
 71    riskLevel: 'low' | 'medium' | 'high';
 72    riskRationale: string;
 73    totalFilesChanged: number;
 74    totalLinesChanged: number;
 75    slides: AISlide[];
 76    webSources?: WebSource[];
 77  }
 78  
 79  export interface ReviewGuide {
 80    prTitle: string;
 81    prDescription: string;
 82    prUrl: string;
 83    author: string;
 84    summary: string;
 85    riskLevel: 'low' | 'medium' | 'high';
 86    riskRationale: string;
 87    totalFilesChanged: number;
 88    totalLinesChanged: number;
 89    neighborFileCount?: number;
 90    excludedFiles?: string[];
 91    generationDurationMs?: number;
 92    slides: Slide[];
 93    headSha?: string;
 94    webSources?: WebSource[];
 95  }
 96  
 97  export interface PrStatus {
 98    labels: string[];
 99    mergeable: boolean | null;
100    isDraft: boolean;
101    ciChecks: CiCheck[];
102    ciConclusion: 'success' | 'failure' | 'pending' | 'neutral';
103    reviewSummary: ReviewSummary;
104    baseBranch: string;
105    commitCount: number;
106    requestedReviewers: string[];
107    requestedTeams: string[];
108    mergeableState: string | null;
109    autoMerge: { method: string } | null;
110    milestone: { title: string; dueOn: string | null } | null;
111  }
112  
113  export type DiffSide = 'LEFT' | 'RIGHT';
114  export type ReviewEvent = 'APPROVE' | 'REQUEST_CHANGES' | 'COMMENT';
115  
116  export interface PendingReviewComment {
117    id: string;
118    filePath: string;
119    line: number;
120    side: DiffSide;
121    body: string;
122    hunkHeader: string;
123    codeSnippet: string;
124    slideIndex: number;
125  }
126  
127  export interface SubmitReviewRequest {
128    prUrl: string;
129    headSha: string;
130    event: ReviewEvent;
131    body: string;
132    comments: { path: string; line: number; side: DiffSide; body: string }[];
133  }
134  
135  export interface ReviewHistoryEntry {
136    id: string;
137    prTitle: string;
138    prUrl: string;
139    author: string;
140    riskLevel: 'low' | 'medium' | 'high';
141    status?: 'generating' | 'completed' | 'failed'; // undefined defaults to 'completed' (backward compat)
142    error?: string;
143    model?: ModelId;
144    generationDurationMs?: number;
145    savedAt: string; // ISO date string
146    unread?: boolean;
147    prState?: 'open' | 'merged' | 'closed';
148    prHeadSha?: string;
149  }
150  
151  export interface StartReviewResult {
152    reviewId: string;
153    prTitle: string;
154    prUrl: string;
155    author: string;
156  }
157  
158  export type Provider = 'claude' | 'gemini';
159  
160  export type ClaudeModel = 'claude-opus-4-6' | 'claude-sonnet-4-6' | 'claude-haiku-4-5-20251001';
161  
162  export type GeminiModel =
163    | 'gemini-3.1-pro-preview'
164    | 'gemini-3-pro-preview'
165    | 'gemini-3-flash-preview'
166    | 'gemini-2.5-pro'
167    | 'gemini-2.5-flash';
168  
169  export type ModelId = ClaudeModel | GeminiModel;
170  
171  export interface Preferences {
172    instructions: string;
173    provider: Provider;
174    model: ModelId;
175    thinking: boolean;
176    signalBoost: boolean;
177    smartImports: boolean;
178    reviewSuggestions: boolean;
179    enableTools: boolean;
180    enableWebResearch: boolean;
181    autoReviewOnRequest: boolean;
182    codeTheme: string;
183    codeFont: string;
184    claudePath: string;
185    geminiPath: string;
186    notifications: boolean;
187    diffLayout: 'unified' | 'split';
188    includeAllFiles: boolean;
189    reviewSignature: boolean;
190  }
191  
192  export interface SendSlideChatRequest {
193    prTitle: string;
194    prDescription: string;
195    summary: string;
196    slideTitle: string;
197    slideNarrative: string;
198    slideReviewFocus: string | null;
199    affectedFiles: string[];
200    diffContent: string;
201    history: Array<{ role: 'user' | 'assistant'; content: string }>;
202    question: string;
203    provider: Provider;
204    model: ModelId;
205  }
206  
207  export interface GenerateReviewRequest {
208    prUrl: string;
209    provider: Provider;
210    model: ModelId;
211    instructions?: string;
212    thinking?: boolean;
213    signalBoost?: boolean;
214    smartImports?: boolean;
215    reviewSuggestions?: boolean;
216    webResearch?: boolean;
217    excludedFiles?: string[];
218  }
219  
220  export interface GenerateReviewResponse {
221    review: ReviewGuide;
222  }
223  
224  export interface ChangedFile {
225    filename: string;
226    status: 'added' | 'modified' | 'deleted' | 'renamed';
227    additions: number;
228    deletions: number;
229    previous_filename?: string;
230  }
231  
232  export interface FreshnessCommit {
233    sha: string;
234    message: string;
235    authorLogin: string;
236    authorDate: string;
237  }
238  
239  export type FreshnessResult =
240    | { status: 'current' }
241    | { status: 'stale'; aheadBy: number; commits: FreshnessCommit[] }
242    | { status: 'force-pushed' }
243    | { status: 'unknown'; reason: string };
244  
245  export interface PrSearchResult {
246    number: number;
247    title: string;
248    url: string;
249    repoOwner: string;
250    repoName: string;
251    author: string;
252    updatedAt: string;
253    isDraft: boolean;
254    role: 'author' | 'review-requested';
255  }
256  
257  export interface UpdateInfo {
258    version: string;
259    releaseUrl: string;
260  }
261  
262  export interface PrMetadata {
263    title: string;
264    description: string;
265    author: string;
266    baseBranch: string;
267    headBranch: string;
268    headSha: string;
269    merged: boolean;
270    state: 'open' | 'closed';
271    createdAt: string;
272    updatedAt: string;
273    url: string;
274    labels: string[];
275    mergeable: boolean | null;
276    isDraft: boolean;
277    commitCount: number;
278    requestedReviewers: string[];
279    requestedTeams: string[];
280    mergeableState: string | null;
281    autoMerge: { method: string } | null;
282    milestone: { title: string; dueOn: string | null } | null;
283  }