The PRs and issues assigned to you, in the menu bar.
A native macOS tray of your open GitHub assignments: they appear when assigned, disappear when closed or unassigned, and you get one toast the moment something new lands on your plate. A worklist, not an inbox — GitHub's assignment state IS the state.
Warning
Alpha, personal tool — provided as-is, without warranty or support. There is no release cadence, no security-patch SLA, and no compatibility promise. No packaged releases — build from source. The app is ad-hoc signed, not notarized. Use at your own risk.
- Your assigned work, always one click away. Polls one GitHub Search query
—
assignee:@me is:open— every 60 seconds and renders it as a menu-bar count plus a popover grouped org → repo, newest activity first. Click any row to open it on GitHub. - Live assignment toasts. One native notification per newly assigned item, within one poll; the banner's Open button (or a plain click) jumps straight to the PR/issue. Structurally storm-proof: first launch, relaunches, and re-authentication are always silent.
- Reviewed marks. Flag any open item as reviewed with the row's check button — a local-only mark (never written to GitHub) — and flip the header filter between All and To review. The menu-bar count follows the filter, so it reads as "things still needing me". Marks clear themselves when an item leaves your list.
- Filters that never refetch. Show/hide PRs, issues, and toasts from Settings; everything re-renders instantly from the retained snapshot.
- Graceful degradation. Network blips and rate limits serve the last-good list with a "cached data" banner; rate limiting shows a live retry countdown and slows polling automatically; every error kind gets its own actionable banner (missing token, revoked token, org policy, and so on).
- Native and tiny. Rust CLI + SwiftUI shell — no Electron, zero third-party Swift dependencies, an icon rendered from code at build time.
- Security-first by construction.
- 100% read-only: the codebase contains only GET requests — verifiable with
one grep of
crates/my-github-assignments-client/src/. - A pasted token lives in your login Keychain, handled exclusively by the
Rust layer; never in argv, files, or logs (tested with a sentinel token).
A token sourced from the
ghCLI instead (see below) is resolved in memory per run and never written by this app — though it may live in gh's own storage and may originate from an inheritedGH_TOKEN/GITHUB_TOKEN. - Only egress is
api.github.com; an overridden endpoint can never receive the real token (exfil guard, tested). No telemetry. - Cache files (auth metadata + last-good list) are readable by your user only, written crash-safely (temp file + atomic rename), and symlink attacks are refused.
- Launch-at-login uses the system's
SMAppServicestate as the single source of truth.
- 100% read-only: the codebase contains only GET requests — verifiable with
one grep of
macOS 13+, Rust, and the Xcode Command Line Tools
(xcode-select --install).
git clone https://github.com/randymorales/my-github-assignments.git my-github-assignments && cd my-github-assignments
cargo test --workspace # everything should be green
macos/build-app.sh --install # builds + installs + launches /Applications/MyGitHubAssignments.appThen authenticate: right-click the menu-bar icon → Settings… → paste a token (see Authenticate).
The build and install are safely delegable; the token is not. Hand your agent this prompt:
Install My GitHub Assignments from
https://github.com/randymorales/my-github-assignmentsby following the "Agent install runbook" in its README exactly. Verify every checkpoint, report the outputs, and stop at the human-only step — never ask me for, accept, or handle a GitHub token.
- Verify prerequisites (install what's missing):
xcode-select -pprints a developer-dir path.cargo --version≥ 1.89 ·swift --versionreports Swift ≥ 5.9.
- Clone and test:
git clone https://github.com/randymorales/my-github-assignments.git my-github-assignments && cd my-github-assignments, thencargo test --workspace. ✅ Checkpoint: every suite printstest result: ok. … 0 failed. - Fixture integrity:
dev/check-fixtures.sh. ✅ Checkpoint:fixtures OK (synthetic constraints hold). - Build + install:
macos/build-app.sh --install. ✅ Checkpoint: output containsbuilt:andinstalled: /Applications/MyGitHubAssignments.app; the tray icon appears in the menu bar (the app auto-launches). - Verify the artifact:
codesign -dv /Applications/MyGitHubAssignments.app→Identifier=dev.randymorales.MyGitHubAssignments./Applications/MyGitHubAssignments.app/Contents/Resources/my-github-assignments --version→{"schema_version": 1, "version": "…"}.
- STOP — hand back to the human. The remaining step is authentication, and it is human-only by design: the token must go from the human's clipboard directly into the app's Settings field (it travels stdin → Keychain and never touches disk). An AI agent must never request, receive, store, or paste the token — a credential shared with an agent ends up in conversation logs.
| Symptom | Fix |
|---|---|
| Gatekeeper blocks a downloaded copy | Right-click the .app → Open (once), or xattr -dr com.apple.quarantine /Applications/MyGitHubAssignments.app |
| Keychain prompt after an update | Expected — ad-hoc identity changes per build. Click "Always Allow". |
| No toasts | System Settings → Notifications → allow My GitHub Assignments; if you use Focus/Do Not Disturb, add it to the allowed apps. |
| Empty list, "token was denied access" | Org token policy — see Authenticate. |
| Updating the app | Re-run macos/build-app.sh --install; it quits, replaces, and relaunches. |
Right-click the menu-bar icon → Settings… → paste a token. Two accepted forms:
| Form | What one token can see | Trade-off |
|---|---|---|
Fine-grained PAT (github_pat_…) |
One resource owner only: pick the org, select "All repositories" explicitly, grant read-only Issues + Pull requests + Metadata → that org's private repos plus all public repos. Private assignments in personal repos or other orgs are invisible. | Truly read-only, least privilege. Some orgs gate fine-grained PATs behind an approval flow. |
Classic PAT (ghp_…) with repo |
All repos you can access, all owners. | Broad read/write grant — the app never writes, but the token could. |
The app probes any pasted token (the canonical query + GET /user) before
storing it in your login Keychain; a failing probe stores nothing. The
probe proves the token authenticates — it can NOT prove repo coverage: repos a
token can't see are silently absent from results, never an error. After
authenticating, compare the count against
https://github.com/pulls/assigned once.
If you're logged in with the gh CLI
(gh auth login), the app uses that login automatically when the Keychain is
empty — zero configuration. A pasted token always wins; the gh token is
resolved fresh on each poll (gh auth token --hostname github.com) and never
written to disk by this app. Re-running gh auth login heals the app.
Two things worth knowing:
- Scopes come from your gh login. Coverage matches whatever your
ghtoken can see. A gh web-flow login typically has broadreposcope; verify againsthttps://github.com/pulls/assignedas above. ghhonorsGH_TOKEN/GITHUB_TOKEN. If either is set in the app's environment, gh returns that value ahead of your stored login — the app does not read those variables itself.
Turn the fallback off in Settings → "Use the gh CLI login when no token is
pasted" (this makes Clear token fully de-authenticate). Settings shows the
active source, so you always know whether you're on a pasted token or the gh
login. The gh binary is found at $MY_GITHUB_ASSIGNMENTS_GH_BIN,
/opt/homebrew/bin/gh, /usr/local/bin/gh, or on PATH.
The app is a thin shell over a one-shot CLI you can use directly:
./target/release/my-github-assignments auth set # token via stdin
./target/release/my-github-assignments list # the JSON contract below
./target/release/my-github-assignments auth status # no network call
./target/release/my-github-assignments auth clear # removes token + cached datalist and auth status fall back to the gh CLI when no token is stored; set
MY_GITHUB_ASSIGNMENTS_DISABLE_GH_CLI=1 to skip it. token_source in the
output reports which store was used (keychain / gh_cli / null).
Every subcommand prints JSON on every outcome (exit 0 ok/stale · 1 hard error · 2 usage). The committed contract golden, verbatim:
{
"schema_version": 1,
"user": {
"login": "octocat",
"id": 1
},
"token_source": "keychain",
"fetched_at": "2026-07-24T15:04:00Z",
"poll_interval_secs": 60,
"items": [
{
"id": "1000000001",
"type": "pr",
"org": "acme",
"repo": "widgets",
"number": 42,
"title": "fix(api): synthetic example title",
"url": "https://github.com/acme/widgets/pull/42",
"author": "hubot",
"created_at": "2026-07-20T10:00:00Z",
"updated_at": "2026-07-23T17:59:12Z"
}
],
"stale": null,
"stale_error": null,
"error": null
} ┌──────────────────────────────────┐
│ MyGitHubAssignments.app (Swift) │ NEVER links Rust
│ status item · grouped list │ NEVER persists/reads back the token
│ toasts · settings │
└────────┬─────────▲───────────────┘
spawn: │ │ stdout: frozen JSON contract
PAT via│ │ stderr: diagnostics · exit 0/1/2
stdin ►▼ │
┌──────────────────────────────────┐
│ my-github-assignments CLI (Rust) │ crates: core ← client ← cli
└────┬──────────┬───────┬──────────┘
▼ ▼ ▼
GitHub Keychain ~/.cache/my-github-assignments
Search API (token) (state.json · snapshot.json)
my-github-assignments-core is pure logic (no I/O deps by construction); my-github-assignments-client
is the only crate that touches the network or Keychain; my-github-assignments-cli owns the
JSON contract. Details: AGENTS.md and docs/ADR/.
Read files in this order to audit the tool:
docs/ADR/001-two-process-architecture.md— the token boundary (I7).crates/my-github-assignments-core/src/error.rs+schema.rs— the vocabulary and status precedence.crates/my-github-assignments-cli/src/output.rs— the JSON contract everything hinges on.crates/my-github-assignments-client/src/auth.rs+gh.rs— token handling, Keychain, theghCLI fallback + resolution chain, probe, storage lifecycle. Security-critical.crates/my-github-assignments-client/src/http.rs+search.rs— status mapping, exfil guard, canonical query, completeness rule, read-only verification (GET-only).macos/MyGitHubAssignments/Sources/MyGitHubAssignments/CLIRunner.swift+DataController.swift+Notifier.swift— subprocess seam, toast baseline, UN delegate flow.- Tests:
#[cfg(test)]modules,dev/fixtures/, thelistgoldens.
cargo fmt --all --check && cargo clippy --all-targets -- -D warnings
cargo test --workspace # unit + goldens + wire (httpmock) + assert_cmd
dev/check-fixtures.sh # synthetic-fixture constraints
macos/build-app.sh [--debug] # stage the bundle only (what CI runs)
# Swift UI dev without Rust, network, or token:
MY_GITHUB_ASSIGNMENTS_BIN="$PWD/dev/mock-cli.sh" MY_GITHUB_ASSIGNMENTS_SCENARIO=list-stale \
swift run --package-path macos/MyGitHubAssignmentsMIT © Randy Morales 2026