Add GitHub integration for PRs, issues and AI PR description (#205)

* feat: integrate GitHub OAuth device flow across runtimes

Add GitHub OAuth device flow endpoints across runtimes
Introduce GitHubSettings UI panel and sidebar entry
Persist GitHub auth state in per-runtime storage

* feat: add GitHub PR status and PR description generation

Show PR status for the current branch in the Git view
Generate a pull request description from the diff between base and head
Expose prStatus, prCreate, and prMerge APIs in web and desktop clients

* feat: add GitHub PR ready for review

Add API to mark pull requests as ready for review
Show a Ready button for draft PRs and reflect status in UI
Handle token expiration and GraphQL errors when marking ready
This commit is contained in:
Bohdan Triapitsyn
2026-01-23 16:08:58 +02:00
committed by GitHub
parent 0e715be7d6
commit 463e9ec4e3
43 changed files with 4999 additions and 106 deletions
+12
View File
@@ -10,6 +10,7 @@ import type {
GitDeleteBranchPayload,
GitDeleteRemoteBranchPayload,
GeneratedCommitMessage,
GeneratedPullRequestDescription,
GitWorktreeInfo,
GitAddWorktreePayload,
GitRemoveWorktreePayload,
@@ -108,6 +109,17 @@ export const createDesktopGitAPI = (): GitAPI => ({
return response;
},
async generatePullRequestDescription(
directory: string,
payload: { base: string; head: string }
): Promise<GeneratedPullRequestDescription> {
return safeGitInvoke<GeneratedPullRequestDescription>('generate_pr_description', {
directory,
base: payload.base,
head: payload.head,
});
},
async listGitWorktrees(directory: string): Promise<GitWorktreeInfo[]> {
return safeGitInvoke<GitWorktreeInfo[]>('list_git_worktrees', { directory });
},
+62
View File
@@ -0,0 +1,62 @@
import type {
GitHubAPI,
GitHubAuthStatus,
GitHubPullRequest,
GitHubPullRequestCreateInput,
GitHubPullRequestMergeInput,
GitHubPullRequestMergeResult,
GitHubPullRequestReadyInput,
GitHubPullRequestReadyResult,
GitHubPullRequestStatus,
GitHubDeviceFlowComplete,
GitHubDeviceFlowStart,
GitHubUserSummary,
} from '@openchamber/ui/lib/api/types';
export const createDesktopGitHubAPI = (): GitHubAPI => ({
async authStatus(): Promise<GitHubAuthStatus> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubAuthStatus>('github_auth_status', {}, { timeout: 8000 });
},
async authStart(): Promise<GitHubDeviceFlowStart> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubDeviceFlowStart>('github_auth_start', {}, { timeout: 8000 });
},
async authComplete(deviceCode: string): Promise<GitHubDeviceFlowComplete> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubDeviceFlowComplete>('github_auth_complete', { deviceCode }, { timeout: 12000 });
},
async authDisconnect(): Promise<{ removed: boolean }> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
const result = await safeInvoke<{ removed: boolean }>('github_auth_disconnect', {}, { timeout: 8000 });
return { removed: Boolean(result?.removed) };
},
async me(): Promise<GitHubUserSummary> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubUserSummary>('github_me', {}, { timeout: 8000 });
},
async prStatus(directory: string, branch: string): Promise<GitHubPullRequestStatus> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubPullRequestStatus>('github_pr_status', { directory, branch }, { timeout: 12000 });
},
async prCreate(payload: GitHubPullRequestCreateInput): Promise<GitHubPullRequest> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubPullRequest>('github_pr_create', payload, { timeout: 20000 });
},
async prMerge(payload: GitHubPullRequestMergeInput): Promise<GitHubPullRequestMergeResult> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubPullRequestMergeResult>('github_pr_merge', payload, { timeout: 20000 });
},
async prReady(payload: GitHubPullRequestReadyInput): Promise<GitHubPullRequestReadyResult> {
const { safeInvoke } = await import('../lib/tauriCallbackManager');
return safeInvoke<GitHubPullRequestReadyResult>('github_pr_ready', payload, { timeout: 20000 });
},
});
+2
View File
@@ -7,6 +7,7 @@ import { createDesktopPermissionsAPI } from './permissions';
import { createDesktopDiagnosticsAPI } from './diagnostics';
import { createDesktopNotificationsAPI } from './notifications';
import { createDesktopToolsAPI } from './tools';
import { createDesktopGitHubAPI } from './github';
const activeTerminalConnections = new Set<string>();
@@ -39,6 +40,7 @@ export const createDesktopAPIs = (): RuntimeAPIs & { cleanup?: () => void } => {
settings: createDesktopSettingsAPI(),
permissions: createDesktopPermissionsAPI(),
notifications: createDesktopNotificationsAPI(),
github: createDesktopGitHubAPI(),
diagnostics: createDesktopDiagnosticsAPI(),
tools: createDesktopToolsAPI(),
cleanup: () => {