import * as vscode from 'vscode'; import fetch from 'node-fetch'; export interface SearchOptions { query: string; state?: 'open' | 'closed'; labels?: string[]; assignee?: string; sort?: 'created' | 'updated' | 'popularity'; order?: 'asc' | 'desc'; } export interface FilterOptions { state?: 'open' | 'closed'; labels?: string[]; assignee?: string; } /** * Search repositories across WizGIT */ export async function searchRepositories( apiEndpoint: string, token: string, query: string, page: number = 1, limit: number = 30 ): Promise { try { const url = new URL(`${apiEndpoint}/repos/search`); url.searchParams.append('q', query); url.searchParams.append('page', page.toString()); url.searchParams.append('limit', limit.toString()); const response = await fetch(url.toString(), { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Search failed: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('Repository search error:', error); throw error; } } /** * Search issues in a repository with filters */ export async function searchIssues( apiEndpoint: string, token: string, owner: string, repo: string, options: SearchOptions & { page?: number; limit?: number } ): Promise { try { const url = new URL(`${apiEndpoint}/repos/${owner}/${repo}/issues`); url.searchParams.append('page', (options.page || 1).toString()); url.searchParams.append('limit', (options.limit || 30).toString()); if (options.query) { url.searchParams.append('q', options.query); } if (options.state) { url.searchParams.append('state', options.state); } if (options.sort) { url.searchParams.append('sort', options.sort); } if (options.order) { url.searchParams.append('order', options.order); } if (options.labels && options.labels.length > 0) { url.searchParams.append('labels', options.labels.join(',')); } if (options.assignee) { url.searchParams.append('assignee', options.assignee); } const response = await fetch(url.toString(), { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Search failed: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('Issue search error:', error); throw error; } } /** * Search pull requests with filters */ export async function searchPullRequests( apiEndpoint: string, token: string, owner: string, repo: string, options: SearchOptions & { page?: number; limit?: number } ): Promise { try { const url = new URL(`${apiEndpoint}/repos/${owner}/${repo}/pulls`); url.searchParams.append('page', (options.page || 1).toString()); url.searchParams.append('limit', (options.limit || 30).toString()); if (options.query) { url.searchParams.append('q', options.query); } if (options.state) { url.searchParams.append('state', options.state); } if (options.sort) { url.searchParams.append('sort', options.sort); } if (options.order) { url.searchParams.append('order', options.order); } if (options.assignee) { url.searchParams.append('assignee', options.assignee); } const response = await fetch(url.toString(), { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Search failed: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('PR search error:', error); throw error; } } /** * Get available labels for filtering */ export async function getLabels( apiEndpoint: string, token: string, owner: string, repo: string ): Promise { try { const response = await fetch(`${apiEndpoint}/repos/${owner}/${repo}/labels`, { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Failed to fetch labels: ${response.statusText}`); } return (await response.json()) as any[]; } catch (error) { console.error('Get labels error:', error); return []; } } /** * Get collaborators for assignee filtering */ export async function getCollaborators( apiEndpoint: string, token: string, owner: string, repo: string ): Promise { try { const response = await fetch(`${apiEndpoint}/repos/${owner}/${repo}/collaborators`, { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Failed to fetch collaborators: ${response.statusText}`); } return (await response.json()) as any[]; } catch (error) { console.error('Get collaborators error:', error); return []; } } /** * Show a quick filter picker for issues/PRs */ export async function showFilterQuickPick( apiEndpoint: string, token: string, owner: string, repo: string, type: 'issues' | 'pulls' ): Promise { const filterOptions: FilterOptions = {}; // State filter const stateSelection = await vscode.window.showQuickPick( ['All', 'Open', 'Closed'], { placeHolder: 'Filter by state (optional)' } ); if (stateSelection && stateSelection !== 'All') { filterOptions.state = stateSelection === 'Open' ? 'open' : 'closed'; } // Assignee filter const collaborators = await getCollaborators(apiEndpoint, token, owner, repo); if (collaborators.length > 0) { const assigneeItems = ['No filter', ...collaborators.map(c => c.login)]; const assigneeSelection = await vscode.window.showQuickPick( assigneeItems, { placeHolder: 'Filter by assignee (optional)' } ); if (assigneeSelection && assigneeSelection !== 'No filter') { filterOptions.assignee = assigneeSelection; } } // Labels filter (only for issues) if (type === 'issues') { const labels = await getLabels(apiEndpoint, token, owner, repo); if (labels.length > 0) { const labelItems = labels.map(l => l.name); const labelSelection = await vscode.window.showQuickPick( labelItems, { placeHolder: 'Filter by label (optional)', canPickMany: true } ); if (labelSelection && labelSelection.length > 0) { filterOptions.labels = labelSelection; } } } return Object.keys(filterOptions).length > 0 ? filterOptions : null; }