Recipes

GitHub Pull Requests

Open-PR count in the bar; popover lists each PR with click-to-open.

The canonical clickable-list pattern. Hover to see open PRs, click any row to jump to that PR in the browser.

Requires the GitHub CLI (brew install gh && gh auth login). The command runs in the shell's CWD by default — to pin to a specific repo, add -R owner/name to both gh pr list calls.

yaml
id: gh-prs
interval: 60
order: 30
location: tray
tooltip: "Open pull requests"
click:
    shell: "gh pr list --web 2>/dev/null"
command: |
    gh pr list --json number,title,url,state,headRefName,author,isDraft \
        --limit 10 2>/dev/null \
      | jq -c '{
          count: length,
          prs: [.[] | {
            number, title, url, state, draft: .isDraft,
            branch: .headRefName,
            author: .author.login
          }]
        }' \
      || echo '{"count":0,"prs":[]}'
view:
    hstack:
      spacing: 4
      children:
        - symbol: { name: arrow.triangle.pull, size: 13, color: "#A371F7" }
        - if:
            when: "${count} > 0"
            then:
              text:
                content: "${count}"
                size: 11
                weight: semibold
                design: rounded
                monospacedDigit: true
hover:
    vstack:
      spacing: 10
      alignment: leading
      frame: { width: 320 }
      children:
        - hstack:
            children:
              - text: { content: "Pull Requests", size: 11, color: secondary, weight: medium }
              - spacer: {}
              - text: { content: "${count}", size: 11, color: secondary, monospacedDigit: true }
        - if:
            when: "${count} == 0"
            then:
              text: { content: "No open PRs.", size: 11, color: secondary }
            else:
              forEach:
                in: "${prs}"
                stack: vstack
                spacing: 8
                alignment: leading
                template:
                  vstack:
                    alignment: leading
                    spacing: 2
                    padding: { vertical: 4, horizontal: 6 }
                    background: "#FFFFFF11"
                    cornerRadius: 6
                    children:
                      - hstack:
                          spacing: 6
                          children:
                            - if:
                                when: "${item.draft} == true"
                                then:
                                  symbol: { name: pencil.circle, size: 11, color: secondary }
                                else:
                                  symbol: { name: circle.fill, size: 8, color: green }
                            - text: { content: "#${item.number}", size: 11, design: monospaced, color: secondary }
                            - text: { content: "${item.title}", size: 12, lineLimit: 1 }
                            - spacer: {}
                      - hstack:
                          spacing: 6
                          children:
                            - text: { content: "${item.branch}", size: 10, color: secondary, design: monospaced, lineLimit: 1 }
                            - spacer: {}
                            - text: { content: "${item.author}", size: 10, color: secondary }
                    onTap:
                      url: "${item.url}"

What this teaches

  • onTap.url with bindings${item.url} resolves at click time, so each row jumps to its own PR. This is the killer use case for per-node tap actions.
  • Card-styled list rows — each row gets a subtle background + corner radius via modifiers, so rows feel tappable.
  • Conditional badge — the bar chip only shows the count when there are open PRs (if when: "${count} > 0").
  • Empty state — when count == 0, the popover shows a friendly "No open PRs" message instead of an empty list.

Building on this

  • Filter to PRs that need your review: change gh pr list to gh pr status --json ... and bind to currentUser.needsReview.
  • Show recent issues alongside PRs by adding a second gh issue list script and merging via jq.
  • Surface CI state per PR by extending the jq pipeline with .statusCheckRollup and rendering a tiny status icon next to each row.
  • Trigger a system notification when a new PR appears: keep a counter file, diff against the script, fire osascript -e 'display notification ...' when the count goes up.