Skip to content

feat(app): add GitHub PR integration#15785

Draft
tamarazuk wants to merge 46 commits intoanomalyco:devfrom
tamarazuk:feat/github-pr-integration
Draft

feat(app): add GitHub PR integration#15785
tamarazuk wants to merge 46 commits intoanomalyco:devfrom
tamarazuk:feat/github-pr-integration

Conversation

@tamarazuk
Copy link
Copy Markdown

@tamarazuk tamarazuk commented Mar 3, 2026

Issue for this PR

Closes #

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds first-class GitHub PR support to OpenCode’s wonderful web and desktop apps. With this new integration, users will automatically see the status of a pull the number of the pull request, be able to directly link to it in the GitHub UI. On top of these very helpful features. Users can also create a pull request directly from open code. They can see the status of any current CI checks. and they can ask agents to address any of the unresolved review comments.

AI's version of a "succinct" summary The feature spans three layers:
  1. Backend (packages/opencode) — New pr.ts and pr-comments.ts modules handle all GitHub interaction via the gh CLI (REST for mutations, GraphQL for review threads). A routes/vcs.ts file exposes REST endpoints for all PR operations. The VCS state manager polls for PR updates and publishes changes over the event bus.

  2. SDK (packages/sdk) — Regenerated TypeScript client and types from the updated OpenAPI spec to expose the new PR/VCS endpoints.

  3. Frontend (packages/app) — PR button in the session header with status pill, dropdown menu (copy link, view CI, merge, mark as ready, address comments). Three new dialogs: create PR (with inline commit-before-push flow and uncommitted-changes guard), merge PR (strategy picker, conflict/checks warnings, post-merge branch deletion), and address review comments (thread selector with bot detection that prefills the agent prompt). PR status badge shown in the sidebar workspace list.

What changed

Backend (packages/opencode)

  • pr.ts — PR create, merge, mark-as-ready, delete-branch operations, plus fetchForBranch (moved from vcs.ts). Throws on unparseable PR numbers, validates branch names.
  • pr-comments.ts — GraphQL-based review thread fetching with typed response interfaces and authorIsBot detection via __typename.
  • routes/vcs.ts — REST endpoints for all VCS/PR operations (create, merge, ready, delete-branch, comments).
  • vcs.ts — GitHub capability detection, default branch fetching, remote branch listing, dirty count tracking, adaptive PR polling (2min active / 4min idle), file watcher integration, refresh mutex to prevent interleaved refreshes.
  • server.ts — Registered VCS routes.

SDK (packages/sdk)

  • Regenerated sdk.gen.ts and types.gen.ts from updated OpenAPI spec.

Frontend (packages/app)

  • pr-button.tsx — PR status button with dropdown menu in session header.
  • dialog-create-pr.tsx — Create PR dialog with commit flow, uncommitted-changes guard, and auto-expanding commit input.
  • dialog-merge-pr.tsx — Merge PR dialog with strategy selection, conflict/checks warnings, post-merge branch deletion.
  • dialog-address-comments.tsx — Review comment selector with bot author detection, circular checkboxes, muted deselected cards, and prompt builder that walks through each comment individually (fix → commit → push → reply with SHA, or skip → reply with rationale). Pre-fills prompt in build mode.
  • session-header.tsx — Added PR button to header.
  • use-session-commands.tsx — PR session commands (create, merge, address comments, mark as ready, copy link, open in browser).
  • sidebar-workspace.tsx — PR status badge matching sidebar unread badge style.
  • global-sync/ — Wired VCS state updates into the sync store.
  • pr-errors.ts — Shared API error parsing utility.
  • pr-style.ts — PR status color helpers.
  • en.ts — All PR-related i18n keys.

UI library (packages/ui)

  • checkbox.tsx — Fixed class prop passthrough to Kobalte root element.
  • dialog.css — Added large dialog size variant.
  • icon.tsx — Added branch icon.

How it works

  • gh CLI is used for all GitHub operations (auth, PR creation, merge via REST API, review comments via GraphQL)
  • VCS state is initialized at startup and kept current through: file watcher (debounced local refreshes on file changes, full refreshes on git ref changes), adaptive polling, and manual refresh after mutations
  • PR status updates flow through the existing event bus → SSE → client sync store pipeline
  • The address-comments dialog fetches unresolved review threads, lets the user select which to address, and prefills the chat prompt with file paths, comment bodies, and gh api reply instructions
  • Bot-authored comments are detected via GraphQL __typename and flagged with authorIsBot

Known Limitations

  • No server-side concurrency protection on mutation routes. The POST endpoints (/pr, /pr/merge, /pr/ready, /pr/delete-branch, /commit) don't have request-level locks. In practice this is mitigated by: UI submitting flags that disable buttons during requests, PR.create being idempotent (returns existing PR), and GitHub's API rejecting duplicate merges. A server-side mutex can be added in a follow-up if needed.

How did you verify your code works?

  • Manually tested: create PR, merge PR (squash/merge/rebase), mark as ready, delete branch, copy link, view CI status, address review comments flow, uncommitted-changes guard
  • No as any or e: any in new code — proper error typing throughout- tsc --noEmit passes in both packages/opencode and packages/app
  • Full bun turbo typecheck passes

Screenshots / recordings

If this is a UI change, please include a screenshot or recording.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

If you do not follow this template your PR will be automatically rejected.

@github-actions github-actions bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels Mar 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 3, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@anduimagui
Copy link
Copy Markdown
Contributor

@adamdotdevin I found this while prototyping the exact same feature. Strong +1 from me — any chance this could be prioritized?

…e, and comment handling

- Extend Vcs namespace with PrInfo, GithubCapability types and enriched VcsInfo
- Add pr.ts for PR creation/merge/update operations via gh CLI
- Add pr-comments.ts for fetching and addressing PR review comments
- Extract VCS routes into dedicated routes/vcs.ts with full PR endpoints
…on, and session commands

- Add PR button component with status display in sidebar
- Add dialogs for creating PRs, merging, and addressing review comments
- Wire up VCS updated event in global sync for PR state tracking
- Add PR-related session commands and i18n strings
- Add git-pull-request icon and dialog styling updates
- Remove `as any` casts in session commands and route handlers
- Extract shared API error parsing into pr-errors.ts utility
- Add GraphQL response type interface for pr-comments.ts
- Fix non-null assertions in dialog-address-comments.tsx
- Add branch name validation regex to DeleteBranchInput
- Remove dead `typeof authCmd === "string"` code path in vcs.ts
- Standardize catch blocks with proper error typing
- Add i18n keys for reply count strings
- Move fetchPrForBranch and fetchUnresolvedCommentCount from vcs.ts to pr.ts
…per comment

The agent prompt now walks through each review comment individually:
fix → commit → push → reply with SHA, or skip → reply with rationale.
Use the same size, color, and positioning as the project unread
indicator for visual consistency.
Soften comment card borders using border-weaker-base/border-weak-base,
switch to rounded cards with gap spacing, add circular checkboxes with
interactive colors, use authorIsBot flag, and improve typography/spacing.
…ts dialog

The field only pre-fills text into the prompt input, which users can
edit directly after the prompt is generated. Removing it simplifies
the dialog without losing any functionality.
The class prop was being split out but not forwarded to the Kobalte
root, preventing consumers from applying custom styles.
Query __typename from GitHub GraphQL to detect bot authors and expose
as a boolean on ReviewComment, replacing the fragile string-based check.
- Fix merge branch deletion: remove no-op `delete_branch` API field,
  call `deleteBranch()` after successful merge instead
- Guard `handleSubmit` against undefined PR/repo to prevent "PR #undefined"
- Remove unused params from `buildQuery()` in pr-comments
- Throw on unparseable PR number in `create()` instead of returning 0
- Remove dead `UPSTREAM_MISSING` error code
- Remove dead "no pull requests found" string check (stderr, not stdout)
- Add refresh mutex to prevent interleaved `refreshLocal`/`refreshFull`
Disable the Push & Create button when there are uncommitted changes,
auto-expand the commit input when the dialog opens with dirty files,
and show a helper text to guide users to commit first.
- Add backend PR.ready() function and API endpoint
- Add dropdown menu item for draft PRs to mark as ready
- Show loading spinner on dropdown trigger while action is in progress
- Add success/error toast notifications
- Switch warning box from yellow/warning to red/critical tokens
- Move branch name inside warning box as cohesive info block
- Use fit dialog to eliminate excess bottom whitespace
- Use proper theme tokens for destructive button styling
- Remove local branch cleanup from backend (user is typically on the branch)
- Simplify i18n to single warning message about remote-only deletion
Move inline delete branch handler in pr-button to open the new
DeleteBranchDialog instead of executing the deletion directly.
When a PR has merge conflicts, the merge dialog now shows an inline
button that pre-fills the prompt with conflict resolution instructions
and switches to build mode, matching the address-comments pattern.
One-way sync from commit message to PR title while the commit input
is visible. Stops when user manually edits the PR title.
Route said "remote (and local) branch" but only remote deletion is implemented.
- Reject '..' anywhere in branch name regex (path traversal)
- Require non-empty title on CreateInput via .min(1)
- Check mergeable status before calling GitHub merge API
- Return 404 instead of 400 for NO_PR errors in route handlers
…nt field, sanitize error output

- Remove unsafe Required<GithubCapability> cast in ensureGithub(); use
  narrowed intersection type instead so repo/host remain optional
- Use clearTimeout instead of clearInterval for setTimeout handle
- Add missing branches field to vcs.updated event type in reducer
- Redact GitHub token prefixes (ghp_, github_pat_, etc.) and truncate
  subprocess error output before surfacing in PrError messages
…nch delete failure

- Replace hand-rolled PrError class with NamedError.create() convention
- Add PrError to global onError handler; remove per-route try/catch blocks
- Wrap all gh CLI subprocess calls with withTimeout (30s default, 60s for create/merge)
- Add branchDeleteFailed field to PrInfo so callers know when post-merge cleanup fails
- Log stderr from gh pr view instead of silently returning undefined (anomalyco#10)
- Cap pr-comments pagination at 10 pages to prevent runaway requests (anomalyco#18)
- Use resolveApiErrorMessage in delete-branch dialog for consistent error UX (anomalyco#19)
- Default remoteBranchExists to false while loading to avoid UI flash (anomalyco#20)
…odies

- Refresh VCS state before idempotency check in create() to avoid TOCTOU race (anomalyco#4, anomalyco#12)
- Use refreshed branch/defaultBranch in fallback PR return (anomalyco#9)
- Validate base branch exists before attempting gh pr create (anomalyco#22)
- Wrap review comment bodies in fenced blocks for structural separation (anomalyco#8)
The unresolved-comments dot badge remained visible when hovering over
the workspace name, appearing as a stray purple dot after the PR pill
animated away.
…m properties

Define --pr-color-{open,merged,closed,draft} tokens in index.css and
consume them via var() in pr-style.ts, giving a single source of truth
for PR state colors that can be overridden per-theme if needed.
@tamarazuk tamarazuk force-pushed the feat/github-pr-integration branch from 43acda4 to e6ffbd3 Compare March 21, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants