A macOS menu bar app for signing in to GitHub and creating new empty repositories from the menu bar — so you can hand a fresh repo URL to an AI agent (or a colleague) without opening github.com.
- Menu-bar-only app (
LSUIElement = true— no Dock icon). - Two sign-in paths: Personal Access Token (PAT) and OAuth (your own
GitHub OAuth App, PKCE,
ASWebAuthenticationSession). - Tokens are stored in the Keychain (never UserDefaults).
- Create a repo (user or org owner, private by default, optional README), copy the URL to the clipboard, get a macOS notification, and open it in your browser.
- macOS 15.0 or later
- Xcode 27 / Swift 6.4 toolchain
xcodegen(only needed to regenerate the project fromproject.yml; the generated.xcodeprojis committed so a plainxcodebuildworks without xcodegen)
# From the repo root:
xcodegen generate # regenerate GitHubMenuBar.xcodeproj (optional)
xcodebuild -scheme GitHubMenuBar -configuration Debug build
open "$(xcodebuild -scheme GitHubMenuBar -showBuildSettings 2>/dev/null \
| awk '/BUILT_PRODUCTS_DIR/{print $3}')/GitHubMenuBar.app"Or open GitHubMenuBar.xcodeproj in Xcode and press ⌘R.
The app runs as a menu-bar item (a GitHub-mark SF Symbol). Click it to open the menu.
xcodebuild test -scheme GitHubMenuBarTests use a stubbing URLProtocol and a mock Keychain — no real network or
Keychain access is required. (Two Keychain tests do hit the real Keychain in
a per-run-unique service; they clean up after themselves.)
- Create a PAT at https://github.com/settings/tokens (classic) or https://github.com/settings/personal-access-tokens (fine-grained).
- Required scopes:
repo— to list orgs and create repositories.- For fine-grained tokens: grant Repository administration: Read and write and Administration: Read on the account/org, plus Organization administration: Read if you want to create repos under orgs.
- Open the menu → Settings… → paste the token into the secure field →
Save Token. The token is validated via
GET /userand stored in the Keychain under your GitHub username.
-
Create an OAuth App at https://github.com/settings/developers → New OAuth App.
-
Set the Authorization callback URL to:
githubmenubar://oauth/callback(This exact value is shown in the Settings window with a Copy button.)
-
Open the menu → Settings… → OAuth App section.
-
Enter your Client ID (and Client Secret, if your app has one).
-
Click Save Config, then Sign in with GitHub. The app opens the GitHub authorize page via
ASWebAuthenticationSession; after you approve, the callback is intercepted and the code is exchanged for an access token using PKCE (S256). The token is validated viaGET /userand stored in the Keychain exactly like a PAT.
The OAuth App config (Client ID/Secret) is persisted in a separate
Keychain entry (service com.github-menubar.oauth) — never in UserDefaults.
- Sign in (PAT or OAuth).
- Menu → New Repository… (⌘N).
- Pick an owner (your account or one of your orgs), enter a name
(validated against GitHub's rules: alphanumeric plus
-,_,.; no consecutive special characters; ≤100 chars), an optional description, and choose Private (default on) and Add README (default off — an empty repo is the point). - Press Create (⏎).
- On success the new repo URL is copied to the clipboard, a macOS notification is posted, and the banner offers Open in Browser and Copy URL Again. On failure the GitHub error message is shown inline.
Menu → Sign Out clears the stored token from the Keychain.
GitHubMenuBar/
App/ App entry, AppDelegate (NSStatusItem), menu construction
Auth/ AuthManager (@MainActor @Observable), KeychainStore,
PKCE, OAuthConfig/OAuthConfigStore, OAuthCoordinator
GitHub/ GitHubClient (actor), Codable models, endpoints
UI/ NewRepoWindow, SettingsWindow, WindowHost, WindowEnvironment
Utilities/ Clipboard, Notifier, RepoNameValidator
GitHubMenuBarTests/ unit tests (GitHubClient, KeychainStore, AuthManager,
RepoNameValidator, OAuth/PKCE, scaffold)
- MV with
@Observable:AuthManageris@MainActor @Observable; SwiftUI views and the menu observe it directly. - Networking lives in the
GitHubClientactor (URLSession+ async/await, no third-party deps). The OAuth token exchange uses a form-encoded POST to/login/oauth/access_token. - Secrets are Keychain-only (
kSecClassGenericPassword,kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly). - Swift 6 strict concurrency: blocking
SecItem*calls run off the MainActor viaTask.detached; the OAuth browser step is abstracted behind anOAuthCoordinatingprotocol so tests can stub it.
The Xcode project is generated from project.yml with xcodegen. After
structural changes (new files/targets), regenerate:
xcodegen generateThe generated GitHubMenuBar.xcodeproj is committed, so contributors without
xcodegen can still build and test.