Skip to content

feat: create PR from issue#239

Merged
Flo0806 merged 3 commits intomainfrom
feat/issue-to-pr
Mar 16, 2026
Merged

feat: create PR from issue#239
Flo0806 merged 3 commits intomainfrom
feat/issue-to-pr

Conversation

@Flo0806
Copy link
Contributor

@Flo0806 Flo0806 commented Mar 16, 2026

Closes #234

Now we can create pull requests if we push a new branch (fork)

Summary by CodeRabbit

  • New Features

    • Repo Pending PR banner shows branches with outstanding commits, ahead count, and allows dismissing entries
    • Create pull requests from pending branches with title editing, issue-linking (autocomplete) and draft toggle
    • Mark draft pull requests as "ready for review" from the PR UI
    • Success/error toasts and navigation to created PRs
  • Localization

    • Added translations for draft PR creation, ready-for-review flow, and pending PR UI texts

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 05d0db2f-3599-4e3b-95ed-4dd17a663cb0

📥 Commits

Reviewing files that changed from the base of the PR and between 5442955 and 07e5fc5.

📒 Files selected for processing (1)
  • server/api/repository/[owner]/[repo]/pending-branches.get.ts

📝 Walkthrough

Walkthrough

Adds a pending-PR UX and server workflow: lists branches with user commits, lets users create PRs (including draft + issue linking), and adds API endpoints to create PRs and mark drafts ready; integrates draft state into merge status composable and work-item header with localization.

Changes

Cohort / File(s) Summary
UI: Pending PR component & integration
app/components/repo/PendingPrBanner.vue, app/pages/repos/[owner]/[repo]/index.vue
New Vue component showing pending branches, modal for creating PRs (title, draft toggle, issue link), dismiss behavior, and integrated into repo page.
UI: Work item / draft handling
app/components/work-item/WorkItemHeader.vue
Added draft-aware UI and handleMarkReady() flow; now uses isDraft flags and shows "Mark Ready" action with loading state.
Composable: merge status
app/composables/useMergeStatus.ts
Extended composable to expose markReady(number) and markingReady reactive flag for marking draft PRs ready.
Server: pending-branches & branch status
server/api/repository/[owner]/[repo]/pending-branches.get.ts, server/api/issues/branch-status.get.ts
New endpoint to return user's pending branches (aheadBy, defaultBranch) and branch-status extended with aheadBy and hasOpenPr; uses limited compares and PR filtering.
Server: PR creation & ready endpoints
server/api/pull-requests/create.post.ts, server/api/repository/[owner]/[repo]/pulls/[number]/ready.post.ts
New POST APIs: create pull requests (supports draft flag, validates repo/head) and mark pull requests ready for review via GraphQL mutation.
Localization & schema
i18n/locales/en.json, i18n/locales/de.json, i18n/schema.json
Added pendingPr strings and new merge.draft* keys; schema expanded to include the new translation keys and pendingPr object.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Client as Web Client
    participant Server as Flumen Server
    participant GitHub as GitHub API

    User->>Client: Open repo page
    Client->>Server: GET /api/repository/{owner}/{repo}/pending-branches
    Server->>GitHub: List branches, default branch, open PRs
    Server->>GitHub: Compare branch -> default (up to 5)
    Server-->>Client: PendingBranch[] (branch, aheadBy, defaultBranch)

    User->>Client: Click "Create PR"
    Client->>Client: Derive title, detect linked issue, edit form
    Client->>Server: POST /api/pull-requests/create {repo, head, base, title, body, draft}
    Server->>GitHub: Create pull request (with draft flag)
    GitHub-->>Server: PR details (number, url, node_id)
    Server-->>Client: Created PR info
    Client->>Client: Show toast, navigate to PR
Loading
sequenceDiagram
    participant User
    participant Client as Web Client
    participant Server as Flumen Server
    participant GitHub as GitHub API

    User->>Client: View work item with draft PR
    Client->>Client: Show draft hint + "Mark Ready" button
    User->>Client: Click "Mark Ready"
    Client->>Server: POST /api/repository/{owner}/{repo}/pulls/{number}/ready
    Server->>GitHub: Query PR node ID
    GitHub-->>Server: Return PR node ID
    Server->>GitHub: markPullRequestReadyForReview mutation
    GitHub-->>Server: Return updated PR (isDraft)
    Server-->>Client: Return isDraft status
    Client->>Client: Update UI, show toast, emit ciStatusChanged
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐇 Soft paws tap the commit tree,

branches ready, hopping free;
A draft takes shape, a PR takes flight,
Marked ready, glowing bright—
A rabbit cheers for code tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: create PR from issue' directly summarizes the main change and core functionality of the PR.
Description check ✅ Passed The description includes a related issue reference and a clear summary of the changes, meeting the core requirements of the template.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #234: direct Create PR action, pre-filled title/body, issue linking via Closes #N, and seamless ClaimFlow integration.
Out of Scope Changes check ✅ Passed All code changes directly support the issue-to-PR creation flow objective; no out-of-scope alterations were detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/issue-to-pr
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@Flo0806 Flo0806 marked this pull request as ready for review March 16, 2026 09:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
app/components/repo/PendingPrBanner.vue (1)

108-110: Consider persisting dismissed branches across page reloads.

The dismissed set is stored in component state, so dismissed branches will reappear after a page refresh. If this is intentional (temporary dismissal), this is fine. If users expect dismissed branches to stay hidden, consider using localStorage or a similar persistence mechanism.

Optional: Persist dismissed branches to localStorage
+const DISMISSED_KEY = `flumen:dismissed-branches:${props.owner}/${props.repo}`
+
+const dismissed = ref<Set<string>>(new Set(
+  JSON.parse(localStorage.getItem(DISMISSED_KEY) || '[]')
+))
+
 function dismiss(branch: string) {
   dismissed.value = new Set([...dismissed.value, branch])
+  localStorage.setItem(DISMISSED_KEY, JSON.stringify([...dismissed.value]))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/repo/PendingPrBanner.vue` around lines 108 - 110, The dismiss
logic in PendingPrBanner.vue currently only updates the in-memory dismissed Set
(dismissed.value), so dismissed branches reappear after reload; modify the
component to persist dismissed branches to localStorage (e.g., store an array or
comma-separated string) whenever dismiss(branch: string) updates
dismissed.value, and restore that persisted set on component mount/created
initialization so the dismissed state survives page reloads; reference the
dismissed reactive variable and the dismiss(branch: string) function to add
persistence and a restore step (and handle parsing/empty-state safely).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/repo/PendingPrBanner.vue`:
- Around line 218-225: Replace the hardcoded "Closes #" in PendingPrBanner.vue
(the span that renders UIcon and Closes #{{ selectedIssue.value }}) with an i18n
lookup (e.g., use the component's i18n function or $t call like
t('pendingPr.closesIssue', { number: selectedIssue.value }) or equivalent
interpolation) so the text is translated; then add the new key
pendingPr.closesIssue to all locale files and update the i18n schema/typing to
include it.

In `@server/api/issues/branch-status.get.ts`:
- Around line 37-45: The GraphQL query BranchStatus currently filters PRs only
by headRefName and then treats totalCount > 0 as hasOpenPr, causing false
positives from forks; update the BranchStatus query to fetch pullRequests {
nodes { headRepository { name owner { login } } headRefName } } (keep first: 1
or more) and then change the hasOpenPr check to verify the returned node's
headRepository.owner.login === $owner and headRepository.name === $repo (or
equivalent repo identifier) before setting hasOpenPr; apply the same change to
the other occurrence flagged (lines 135-136) so we only count PRs that originate
from this repository, not forks.

In `@server/api/pull-requests/create.post.ts`:
- Line 19: The code discards the contributor login from getSessionToken (const {
token, login: _login }) but later builds the PR head using owner instead of the
contributor, breaking forked PRs; update the destructuring to keep the
contributor login (e.g., const { token, login }) and when constructing the head
parameter (the variable built around head/owner/branch) prefix the branch with
the contributor login for forks (use login:branch) rather than owner:branch,
falling back to owner only when appropriate; locate this in the create.post
handler where getSessionToken, token, login/_login, owner, and head are
referenced and replace the usage of owner with the contributor login when
qualifying fork heads.

In `@server/api/repository/`[owner]/[repo]/pending-branches.get.ts:
- Around line 40-54: The current implementation limits candidate branches with
withoutPr.slice(0, 5) before checking ahead_by and author, which can miss
qualifying branches; change the logic to evaluate all candidates (use withoutPr
without the initial slice or iterate fully) and only apply the cap after
filtering (e.g., collect matches via the githubFetchWithToken checks in the
Promise.allSettled or a loop, pushing into results when data.ahead_by > 0 and
userCommitted is true) and then return or use results.slice(0, 5) (or stop early
once results.length reaches 5) so you still limit API returns but only after
filtering by aheadBy and login checks. Ensure you keep references to
githubFetchWithToken, token, owner, repo, defaultBranch, login and results.push
when implementing the change.
- Around line 44-46: The compare API call interpolates raw branch names into the
path in the githubFetchWithToken call inside pending-branches.get.ts (the line
with `/repos/${owner}/${repo}/compare/${defaultBranch}...${branch}`), which
breaks for refs containing `/`; percent-encode the refs before constructing the
path (e.g., apply encodeURIComponent or an equivalent encoder to defaultBranch
and branch) so the compare URL is valid and branches with slashes won't cause
the request to fail.

---

Nitpick comments:
In `@app/components/repo/PendingPrBanner.vue`:
- Around line 108-110: The dismiss logic in PendingPrBanner.vue currently only
updates the in-memory dismissed Set (dismissed.value), so dismissed branches
reappear after reload; modify the component to persist dismissed branches to
localStorage (e.g., store an array or comma-separated string) whenever
dismiss(branch: string) updates dismissed.value, and restore that persisted set
on component mount/created initialization so the dismissed state survives page
reloads; reference the dismissed reactive variable and the dismiss(branch:
string) function to add persistence and a restore step (and handle
parsing/empty-state safely).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dbdfe84f-86df-4d8a-908b-b9c54f8e0ff7

📥 Commits

Reviewing files that changed from the base of the PR and between 747efc2 and 5442955.

📒 Files selected for processing (11)
  • app/components/repo/PendingPrBanner.vue
  • app/components/work-item/WorkItemHeader.vue
  • app/composables/useMergeStatus.ts
  • app/pages/repos/[owner]/[repo]/index.vue
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/schema.json
  • server/api/issues/branch-status.get.ts
  • server/api/pull-requests/create.post.ts
  • server/api/repository/[owner]/[repo]/pending-branches.get.ts
  • server/api/repository/[owner]/[repo]/pulls/[number]/ready.post.ts

Comment on lines +37 to +45
query BranchStatus($owner: String!, $repo: String!, $branchRef: String!, $branchName: String!) {
repository(owner: $owner, name: $repo) {
viewerPermission
defaultBranchRef { name }
url
ref(qualifiedName: $branchRef) { name }
pullRequests(headRefName: $branchName, states: OPEN, first: 1) {
totalCount
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GitHub GraphQL repository.pullRequests headRefNamefilter: when multiple forks have open PRs with the same branch name, does filtering only byheadRefName distinguish the head repository owner, or do you need to inspect the PR's head repository separately?

💡 Result:

Filtering repository.pullRequests(headRefName: "...") matches only the branch name, and does not distinguish which fork/owner the PR’s head branch comes from. (2fd.github.io)

If multiple forks have open PRs whose head branch has the same name, they can all match that filter—so to distinguish them you must inspect each PR’s headRepository / headRepositoryOwner (or headRepository { nameWithOwner }) in the returned nodes. (2fd.github.io)

Citations:


Filter by both branch name and head repository to avoid false positives across forks.

The query filters pull requests only by headRefName. When multiple forks have open PRs with the same branch name (e.g., issue-123), all will match this filter regardless of fork owner. Since the code only checks totalCount > 0, a PR from another fork will incorrectly set hasOpenPr true for a contributor whose branch has no PR yet. Inspect the PR's headRepository or headRepositoryOwner to ensure the PR belongs to the current repository context.

Also applies to: 135-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/issues/branch-status.get.ts` around lines 37 - 45, The GraphQL
query BranchStatus currently filters PRs only by headRefName and then treats
totalCount > 0 as hasOpenPr, causing false positives from forks; update the
BranchStatus query to fetch pullRequests { nodes { headRepository { name owner {
login } } headRefName } } (keep first: 1 or more) and then change the hasOpenPr
check to verify the returned node's headRepository.owner.login === $owner and
headRepository.name === $repo (or equivalent repo identifier) before setting
hasOpenPr; apply the same change to the other occurrence flagged (lines 135-136)
so we only count PRs that originate from this repository, not forks.

}

export default defineEventHandler(async (event) => {
const { token, login: _login } = await getSessionToken(event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

GitHub REST create pull request headparameter: for a PR opened from a fork, shouldheaduse the contributor login (for exampleuser:branch) rather than the base repository owner?

💡 Result:

Yes. When creating a PR into the base repo, the REST API’s head is the source branch; for a PR from a fork you should namespace it with the fork owner, e.g. head: "contributorLogin:branchName" (or org:branch). If the branch is in the same repository, head can just be "branchName". For some cross-repo cases where both repos are owned by the same organization, you may also need to set head_repo. [1]

Sources: GitHub REST “Create a pull request” docs [1].


🏁 Script executed:

cd /repo && head -50 server/api/pull-requests/create.post.ts | cat -n

Repository: flumen-dev/flumen.dev

Length of output: 120


🏁 Script executed:

pwd && find . -name "create.post.ts" -type f 2>/dev/null | head -5

Repository: flumen-dev/flumen.dev

Length of output: 160


🏁 Script executed:

head -50 ./server/api/pull-requests/create.post.ts | cat -n

Repository: flumen-dev/flumen.dev

Length of output: 1733


Use the contributor login when qualifying fork heads.

Line 19 discards the contributor's login, but line 32 qualifies the head parameter with owner (the base repository owner, not the fork owner). For PRs opened from forks, the head parameter should use the contributor's login (e.g., "contributorLogin:branchName"). The current logic breaks for contributors without write access to the base repository.

The inline comment on line 31 states the intent ("prefix with user login") but the code does not match it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/pull-requests/create.post.ts` at line 19, The code discards the
contributor login from getSessionToken (const { token, login: _login }) but
later builds the PR head using owner instead of the contributor, breaking forked
PRs; update the destructuring to keep the contributor login (e.g., const {
token, login }) and when constructing the head parameter (the variable built
around head/owner/branch) prefix the branch with the contributor login for forks
(use login:branch) rather than owner:branch, falling back to owner only when
appropriate; locate this in the create.post handler where getSessionToken,
token, login/_login, owner, and head are referenced and replace the usage of
owner with the contributor login when qualifying fork heads.

Comment on lines +40 to +54
// 3. Check ahead count — only show branches with commits (limit to 5 to save API calls)
const results: PendingBranch[] = []

await Promise.allSettled(withoutPr.slice(0, 5).map(async (branch) => {
const { data } = await githubFetchWithToken<{ ahead_by: number, commits: { author: { login: string } | null }[] }>(
token, `/repos/${owner}/${repo}/compare/${defaultBranch}...${branch}`,
)
if (data.ahead_by === 0) return

// Only show if the current user authored at least one commit
const userCommitted = data.commits.some(c => c.author?.login === login)
if (!userCommitted) return

results.push({ branch, aheadBy: data.ahead_by, defaultBranch })
}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Limit after filtering, not before.

Line 43 caps the search to the first five candidate branches before you know whether any of them are ahead or authored by the current user. On a busy repo, the user's pending branch can sit later in the list, so this endpoint returns [] even though a qualifying branch exists.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/repository/`[owner]/[repo]/pending-branches.get.ts around lines 40
- 54, The current implementation limits candidate branches with
withoutPr.slice(0, 5) before checking ahead_by and author, which can miss
qualifying branches; change the logic to evaluate all candidates (use withoutPr
without the initial slice or iterate fully) and only apply the cap after
filtering (e.g., collect matches via the githubFetchWithToken checks in the
Promise.allSettled or a loop, pushing into results when data.ahead_by > 0 and
userCommitted is true) and then return or use results.slice(0, 5) (or stop early
once results.length reaches 5) so you still limit API returns but only after
filtering by aheadBy and login checks. Ensure you keep references to
githubFetchWithToken, token, owner, repo, defaultBranch, login and results.push
when implementing the change.

@Flo0806 Flo0806 merged commit fa0ef39 into main Mar 16, 2026
8 of 10 checks passed
@Flo0806 Flo0806 deleted the feat/issue-to-pr branch March 16, 2026 09:24
This was referenced Mar 16, 2026
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.

feat: improve issue-to-PR creation flow

1 participant