Summary
Add a right-click context-menu item on session rows in the left sidebar that adds the crow:merge label to the session's PR. The item should appear only when the session has an associated PR. Selecting it labels the PR so the existing auto-merge flow can pick it up — without having to leave Crow and open GitHub.
Desired behavior
- Right-click a session row in the left pane.
- If that session has a PR link, the context menu shows "Add label crow:merge to PR" (with an appropriate SF Symbol, e.g.
tag / arrow.triangle.merge).
- If the session has no PR, the item is hidden (not just disabled).
- Selecting it adds the
crow:merge label to that PR. Ideally disable/relabel the item while in-flight and surface failures the same way the other row actions do.
Where this goes (anchors)
UI — Packages/CrowUI/Sources/CrowUI/SessionListView.swift:
- The shared row context menu is
sessionContextMenu(_ session:) (around line 298). It's already attached via .contextMenu { sessionContextMenu(session) } for the active / in-review / completed / etc. session sections (lines ~121, 146, 199, 223). Adding the new Button inside sessionContextMenu makes it appear consistently across those sections.
- PR presence is already computed the way we need it:
SessionRow has
private var prLink: SessionLink? {
appState.links(for: session.id).first(where: { $0.linkType == .pr })
}
(SessionListView.swift:537). Use the same appState.links(for:).first(where: { $0.linkType == .pr }) lookup inside sessionContextMenu to gate the item and to get the PR URL.
- There's already a
PRBadge component (Packages/CrowUI/Sources/CrowUI/PRBadge.swift) and the row renders a PR badge when prLink != nil (line ~668) — so the "has a PR" signal is well established.
Action wiring — follow the existing callback pattern:
- The other row actions dispatch through optional
AppState closures, e.g. appState.onMarkInReview?(session.id) (line 305) and appState.onCompleteSession?(session.id) (line 314). Add a parallel closure such as appState.onAddMergeLabel?(session.id) and wire it in the app layer where onMarkInReview / onCompleteSession are assigned.
Backend — the label plumbing already exists in Sources/Crow/App/IssueTracker.swift:
- The label constant:
static let autoMergeLabel = "crow:merge" (line 112).
- Adding labels:
backend.setLabels(url:add:remove:) (used at line 559 — setLabels(url: issue.url, add: [], remove: [...])). The new handler should call setLabels(url: <prURL>, add: [Self.autoMergeLabel], remove: []).
- Lazily ensure the label exists in the repo first:
ensureMergeLabel(repo:) (line 1618), gated by backend.capabilities.contains(.autoMergeLabel) (line 1621). Mirror that gate so providers that don't support label-driven merge don't show/execute the action.
So the handler is roughly: resolve the session's .pr SessionLink → derive repoNameWithOwner → ensureMergeLabel(repo:) → setLabels(url: prURL, add: ["crow:merge"], remove: []).
Notes / open questions
- Capability gating: if
backend.capabilities lacks .autoMergeLabel, hide the item (consistent with how enableAutoMerge / ensureMergeLabel already bail out).
- Already-labeled PR: consider no-op'ing (or relabeling to "Remove label crow:merge") when the PR already carries
crow:merge — the auto-merge reconcile already checks pr.labels.contains(crow:merge) (line 1462), so adding it again is harmless but the menu copy could reflect current state. Adding-only is fine for a first cut.
- Sections with inline menus: one section uses an inline destructive-only
.contextMenu (around line 170) rather than sessionContextMenu. Decide whether that section's rows can have PRs and, if so, route it through the shared builder too.
- Scope: this is a manual trigger for the same label the auto-merge watcher consumes — it does not change auto-merge semantics, only gives a one-click way to opt a PR in from the sidebar.
Acceptance criteria
Summary
Add a right-click context-menu item on session rows in the left sidebar that adds the
crow:mergelabel to the session's PR. The item should appear only when the session has an associated PR. Selecting it labels the PR so the existing auto-merge flow can pick it up — without having to leave Crow and open GitHub.Desired behavior
tag/arrow.triangle.merge).crow:mergelabel to that PR. Ideally disable/relabel the item while in-flight and surface failures the same way the other row actions do.Where this goes (anchors)
UI —
Packages/CrowUI/Sources/CrowUI/SessionListView.swift:sessionContextMenu(_ session:)(around line 298). It's already attached via.contextMenu { sessionContextMenu(session) }for the active / in-review / completed / etc. session sections (lines ~121, 146, 199, 223). Adding the newButtoninsidesessionContextMenumakes it appear consistently across those sections.SessionRowhasappState.links(for:).first(where: { $0.linkType == .pr })lookup insidesessionContextMenuto gate the item and to get the PR URL.PRBadgecomponent (Packages/CrowUI/Sources/CrowUI/PRBadge.swift) and the row renders a PR badge whenprLink != nil(line ~668) — so the "has a PR" signal is well established.Action wiring — follow the existing callback pattern:
AppStateclosures, e.g.appState.onMarkInReview?(session.id)(line 305) andappState.onCompleteSession?(session.id)(line 314). Add a parallel closure such asappState.onAddMergeLabel?(session.id)and wire it in the app layer whereonMarkInReview/onCompleteSessionare assigned.Backend — the label plumbing already exists in
Sources/Crow/App/IssueTracker.swift:static let autoMergeLabel = "crow:merge"(line 112).backend.setLabels(url:add:remove:)(used at line 559 —setLabels(url: issue.url, add: [], remove: [...])). The new handler should callsetLabels(url: <prURL>, add: [Self.autoMergeLabel], remove: []).ensureMergeLabel(repo:)(line 1618), gated bybackend.capabilities.contains(.autoMergeLabel)(line 1621). Mirror that gate so providers that don't support label-driven merge don't show/execute the action.So the handler is roughly: resolve the session's
.prSessionLink → deriverepoNameWithOwner→ensureMergeLabel(repo:)→setLabels(url: prURL, add: ["crow:merge"], remove: []).Notes / open questions
backend.capabilitieslacks.autoMergeLabel, hide the item (consistent with howenableAutoMerge/ensureMergeLabelalready bail out).crow:merge— the auto-merge reconcile already checkspr.labels.contains(crow:merge)(line 1462), so adding it again is harmless but the menu copy could reflect current state. Adding-only is fine for a first cut..contextMenu(around line 170) rather thansessionContextMenu. Decide whether that section's rows can have PRs and, if so, route it through the shared builder too.Acceptance criteria
crow:mergelabel to the correct PR (ensuring the label exists in the repo first).autoMergeLabelcapability.