Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
6301ac0
Initial commit: add project configuration, CI/CD workflows, and core …
awjdean Mar 30, 2026
e67d1ab
docs: add review-state triage precedence to MVP spec
awjdean Mar 30, 2026
70dfb54
feat: implement core data models with triage, urgency, and session li…
awjdean Mar 30, 2026
50854e5
feat: implement GitHub REST client with ETag caching and rate limit h…
awjdean Mar 30, 2026
a0868a1
feat: implement polling engine with adaptive intervals and error backoff
awjdean Mar 30, 2026
595fd66
feat: implement PR state transition notifications
awjdean Mar 30, 2026
e6112dc
feat: build PR list and settings views
awjdean Mar 30, 2026
c58c2e6
test: add unit test target to XcodeGen config and regenerate project
awjdean Mar 30, 2026
1ebeec0
test: add comprehensive unit test suite for all layers
awjdean Mar 30, 2026
db20dfd
ci: enable real test execution now that test suite exists
awjdean Mar 30, 2026
995d3be
chore: add .gitignore and stop tracking generated/system files
awjdean Mar 30, 2026
e3250ed
ci: fix release workflow guard and clean up gitignore
awjdean Mar 30, 2026
2414a5e
docs: add project context to CLAUDE.md and add testing guide
awjdean Mar 30, 2026
260c5ba
fix: add incompleteSearchResults error and fix urgency scoring
awjdean Mar 30, 2026
0ca09f0
fix: make KeychainService injectable and improve error handling
awjdean Mar 30, 2026
e30f85a
fix: REST client — 304 ETag retry, incomplete results, search rate limit
awjdean Mar 30, 2026
f12cbdd
fix: polling and notification correctness
awjdean Mar 30, 2026
f06860c
refactor: extract clearPublishedSessionState in AppState
awjdean Mar 30, 2026
cf33d2e
fix: view layer corrections and polish
awjdean Mar 30, 2026
c0ef4ea
test: migrate to Swift Testing and add new test cases
awjdean Mar 30, 2026
738ab6c
chore: add mise task runner and style tool configuration
awjdean Mar 30, 2026
69d5086
ci: migrate workflows to use mise tasks
awjdean Mar 30, 2026
367194c
docs: update CLAUDE.md and testing guide for mise-based workflow
awjdean Mar 30, 2026
e1783de
style: apply swift-format and swiftlint formatting across codebase
awjdean Mar 30, 2026
67ca127
refactor: make AppState and PollingEngine injectable for testing
awjdean Mar 30, 2026
4e8a041
feat: add loadError panel state with retry button
awjdean Mar 30, 2026
92c0da2
fix: check-run 403/404 fallback, CI priority, and review dismissal
awjdean Mar 30, 2026
b01557e
fix: fire CI notification on pending→failing transition
awjdean Mar 30, 2026
48a2868
test: extract test harness and add coverage for new behaviors
awjdean Mar 30, 2026
7d8d09d
docs: update fine-grained token scope requirements
awjdean Mar 30, 2026
4df0687
ci: improve release workflow error message
awjdean Mar 30, 2026
da0597a
docs: add gstack-sketch diagram to documentation
awjdean Mar 30, 2026
64543ec
feat: add start task to build and launch the app
awjdean Mar 30, 2026
7ff50e9
chore: bump minimum deployment target to macOS 14.0
awjdean Mar 30, 2026
7b383dc
refactor: replace openSettings() helper with SettingsLink
awjdean Mar 30, 2026
2c80db3
feat: replace Settings scene with inline panel mode navigation
awjdean Mar 30, 2026
df9d2a6
docs: add improvements backlog
awjdean Mar 30, 2026
6443ddd
chore: clean up existing xcodeproj before regenerating
awjdean Mar 30, 2026
902db7d
feat: add pagination safety limits to search and review fetching
awjdean Mar 30, 2026
11ba91b
refactor: move TriageCategory enum inside PRState
awjdean Mar 30, 2026
83af3c7
refactor: consolidate rate-limit state into AppError and cache triage…
awjdean Mar 30, 2026
56294f8
fix: use add-then-update pattern in KeychainService.save
awjdean Mar 30, 2026
abfd679
feat: group PR notifications under a shared thread identifier
awjdean Mar 30, 2026
dfc93c7
refactor: extract PRListView helpers and use static PanelMode.width
awjdean Mar 30, 2026
9d7141f
fix: use adaptive foreground color for accessibility letters in Statu…
awjdean Mar 30, 2026
9748fee
refactor: improve SettingsView FlowLayout, error handling, and minor …
awjdean Mar 30, 2026
eb67c0e
fix: log failed URL opens in PRRowView
awjdean Mar 30, 2026
fbdc6e9
test: add pagination limit tests and make MockURLProtocol thread-safe
awjdean Mar 30, 2026
e3dd3f5
ci: add SwiftPM caching and fix release workflow path detection
awjdean Mar 30, 2026
74c0f22
chore: add sync dependency to check task and tighten SwiftLint identi…
awjdean Mar 30, 2026
37bdd4f
docs: update improvements backlog
awjdean Mar 30, 2026
9370b4d
feat: add EnvironmentTokenBootstrapper for dev token preloading
awjdean Mar 31, 2026
c9cc5e0
feat: show token access details in settings and improve authenticated UX
awjdean Mar 31, 2026
a07308c
docs: update testing guide for token bootstrap and fine-grained PATs
awjdean Mar 31, 2026
5c9629c
docs: add new improvement items to backlog
awjdean Mar 31, 2026
2a85300
feat: scope PR open-in-browser to PR number click only
awjdean Mar 31, 2026
946b8cc
docs: update improvements backlog with completed items
awjdean Mar 31, 2026
d87b248
feat: show attention-count in menu bar badge instead of total PR count
awjdean Mar 31, 2026
2756e90
docs: update improvements backlog
awjdean Mar 31, 2026
7219f76
docs: mark menu bar count improvement as completed
awjdean Mar 31, 2026
657916d
feat: enhance PRListView with dynamic panel subtitle
awjdean Mar 31, 2026
df9af07
feat: replace status dot hover tooltips with click popovers
awjdean Mar 31, 2026
f0c643b
docs: add new UX improvement items to backlog
awjdean Mar 31, 2026
a4cb091
feat: improve settings view — collapsible repos, remove subtitle, tal…
awjdean Mar 31, 2026
133670f
docs: mark completed settings improvements in backlog and spec
awjdean Mar 31, 2026
3fabd85
feat: add "Your Draft PRs" section for drafts you own
awjdean Mar 31, 2026
36a6205
docs: add PR triage categories decision doc and tidy backlog
awjdean Mar 31, 2026
a41238b
feat: increase panel dimensions and scale up typography
awjdean Mar 31, 2026
407a340
docs: mark draft PRs item done and add new backlog items
awjdean Mar 31, 2026
cd91981
feat: enrich PR status model — FailingCheck struct, MergeStatus.behin…
awjdean Mar 31, 2026
d28b75e
feat: show animated loading indicator in menu bar during initial load
awjdean Mar 31, 2026
33f4490
feat: show descriptive error on token save failure and fix scope requ…
awjdean Mar 31, 2026
4725c15
fix: update panel width test constant and add sync dependency to chec…
awjdean Mar 31, 2026
0b9425d
docs: mark completed items and expand backlog
awjdean Mar 31, 2026
ea0819b
docs: document GH_TOKEN alias in .env.example
awjdean Mar 31, 2026
4eacc95
ci: use maxim-lobanov/setup-xcode and improve release bundle discovery
awjdean Mar 31, 2026
b9167af
docs: add resizable panel and sticky headers to backlog; add PR reord…
awjdean Mar 31, 2026
8a0a551
refactor: add Spacing enum and logSubsystem constant to Theme
awjdean Mar 31, 2026
737ee56
refactor: replace compareForNeedsAction with SortComparator NeedsActi…
awjdean Mar 31, 2026
ed40ea3
fix: thread-safe RelativeDateFormatter in AppError
awjdean Mar 31, 2026
779bff3
fix: reject unterminated quoted tokens in EnvironmentTokenBootstrapper
awjdean Mar 31, 2026
e524e23
fix: thread-safe NotificationService handler access and add prsByID h…
awjdean Mar 31, 2026
44a5708
refactor: overhaul GitHubRESTClient — HTTP infra, CI dedup, token val…
awjdean Mar 31, 2026
a99f641
feat: surface token validation warnings in AppState, PollingEngine, a…
awjdean Mar 31, 2026
83b065e
feat: enrich PRRowView with accessibility labels and URL open error a…
awjdean Mar 31, 2026
cf45b9e
refactor: apply Spacing constants and improve loading animations acro…
awjdean Mar 31, 2026
b765d55
fix: thread-safe MockGitHubDataSource and XCTFail on MockURLProtocol …
awjdean Mar 31, 2026
7e74796
refactor: extract REST client model types and token validation tests …
awjdean Mar 31, 2026
9f56e2f
ci: pin setup-xcode to v1.6.0 and correct macOS requirement in releas…
awjdean Mar 31, 2026
85a21e2
docs: update macOS/Xcode version refs, backlog, and mise fix-style co…
awjdean Mar 31, 2026
e7f7cad
feat: harden KeychainService with kSecAttrAccessibleAfterFirstUnlockT…
awjdean Mar 31, 2026
1d4f928
refactor: replace whole-cache eviction with LRU, paginate check-runs,…
awjdean Mar 31, 2026
72c639e
refactor: migrate AppState to @Observable, centralize logging, adopt …
awjdean Mar 31, 2026
581543b
ci: fix cache key to include project.yml and tidy build config
awjdean Mar 31, 2026
60ae18c
fix: use defer to guarantee lock/unlock in AppError rate-limit format…
awjdean Mar 31, 2026
d16c30c
fix: add error logging and clarify empty-quoted-value behaviour in En…
awjdean Mar 31, 2026
f703c64
fix: propagate fatal errors from concurrent PR state builds and harde…
awjdean Mar 31, 2026
4f03ab6
fix: atomically read all notification state in a single queue sync
awjdean Mar 31, 2026
82a6316
fix: harden PollingEngine task tracking and cap rate-limit sleep dura…
awjdean Mar 31, 2026
75e35bc
fix: guard token-access task cleanup with task ID in SettingsView
awjdean Mar 31, 2026
dc5e99b
fix: increase StatusDotView tap target with 8pt padding
awjdean Mar 31, 2026
4cc02e7
docs: correct SwiftLint identifier note, backlog wording, and plan te…
awjdean Mar 31, 2026
e5bdd60
docs: clarify PR review indicator behavior in draft state
awjdean Mar 31, 2026
400a39f
ci: pin Xcode to 16.4, update action versions, fix xcodeVersion typo
awjdean Mar 31, 2026
e4d9498
refactor: introduce Theme.Colors namespace and migrate all view call …
awjdean Mar 31, 2026
cc0df5c
feat: healthStatus accounts for auth failures, panel errors, and stal…
awjdean Mar 31, 2026
662a158
refactor: extract RateLimitContext, harden LRU cache, propagate PR bu…
awjdean Mar 31, 2026
0b7587d
docs: reorder triage sections and refine bug descriptions
awjdean Mar 31, 2026
6c8affe
fix: pin CI action versions to match release workflow
awjdean Mar 31, 2026
3e3d3f3
chore: bump version and changelog (v0.1.0.0)
awjdean Mar 31, 2026
7d1d446
ci: upgrade Xcode to 26.4, add auto-fix verification step, restrict t…
awjdean Mar 31, 2026
481bba3
chore: fix version format from 0.1.0.0 to 0.1.0
awjdean Mar 31, 2026
f159802
refactor: consolidate Spacing and Fonts into Theme namespace
awjdean Mar 31, 2026
a1cced8
feat: cap GitHub search results at 1,000 and surface warning banner
awjdean Mar 31, 2026
71e209b
test: update for PRFetchResult protocol and add polling/search coverage
awjdean Mar 31, 2026
b62695c
docs: add mise task comments and refine PR ordering plan
awjdean Mar 31, 2026
d5f5bd4
chore: rename check-style→check, fix-style→fix; add verify task
awjdean Mar 31, 2026
0915df2
feat: add AppError.keychainError case with localized description
awjdean Mar 31, 2026
a4e905b
refactor: make NotificationService.requestPermission return Bool and …
awjdean Mar 31, 2026
0a1af92
feat: handle KeychainError distinctly in PollingEngine; inject loadTo…
awjdean Mar 31, 2026
af79ed0
refactor: unify triage sort into single comparator; surface keychainE…
awjdean Mar 31, 2026
5d3f268
refactor: add predicate-based stub matching to MockURLProtocol
awjdean Mar 31, 2026
020efbe
fix: fall back to distantPast for unparseable updatedAt; trim cache t…
awjdean Mar 31, 2026
06121e2
test: use defer for keychain token cleanup in KeychainServiceTests
awjdean Mar 31, 2026
594c5b0
feat: add searchDepthLimit parameter to EnvironmentTokenBootstrapper
awjdean Mar 31, 2026
0d417e5
refactor: extract RowOpacity constants and ExternalURLPresentation; h…
awjdean Mar 31, 2026
af35e33
fix: give ScopeItem a stable explicit id instead of deriving it from …
awjdean Mar 31, 2026
1ddbbfa
docs: add version plan and mark CI action item as complete
awjdean Mar 31, 2026
ac0eaeb
feat: use injected sleep closure in PollingEngine loop
awjdean Mar 31, 2026
b19c5d7
feat: truncate long reviewer lists in status dot tooltip
awjdean Mar 31, 2026
c9bf936
refactor: add @Sendable to MockURLProtocol closures and types
awjdean Mar 31, 2026
6f174b3
test: fix URL stub matching, timing, and assertion precision
awjdean Mar 31, 2026
d95fce8
ci: downgrade Xcode version from 26.4 to 26.3
awjdean Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# EnvironmentTokenBootstrapper reads either GITHUB_TOKEN or GH_TOKEN.
# They are equivalent for local token bootstrapping, so set only one.
GITHUB_TOKEN=ghp_your_token_here
# Optional alias if you prefer GH_TOKEN instead of GITHUB_TOKEN:
# GH_TOKEN=ghp_your_token_here
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI

on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
check:
name: fix-and-check
runs-on: macos-15
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
env:
XCODE_VERSION: "26.3"
steps:
- uses: actions/checkout@v4

# Keep CI on the Xcode 26.3 toolchain expected by the project.
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1.7.0
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Setup mise
uses: jdx/mise-action@v4.0.1
with:
install: true

- name: Cache SwiftPM and DerivedData
uses: actions/cache@v4
with:
path: |
~/.cache/org.swift.swiftpm
~/Library/Caches/org.swift.swiftpm
~/Library/Developer/Xcode/DerivedData
build/DerivedData
.build
key: ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-${{ hashFiles('project.yml', 'Package.resolved') }}
restore-keys: |
${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-
${{ runner.os }}-xcode-

- name: Run auto-fixes
run: mise run fix

- name: Verify auto-fixes produced no changes
run: |
if [[ -n "$(git status --short --untracked-files=all)" ]]; then
git status --short --untracked-files=all
exit 1
fi

- name: Run repo checks
run: mise run check

- name: Verify checks produced no changes
run: |
if [[ -n "$(git status --short --untracked-files=all)" ]]; then
git status --short --untracked-files=all
exit 1
fi
74 changes: 74 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
build:
runs-on: macos-15
env:
APP_NAME: GitHubCommandCenter.app
DERIVED_DATA_ROOT: build/DerivedData
XCODE_VERSION: "26.3"
steps:
- uses: actions/checkout@v4

- uses: jdx/mise-action@v4.0.1
with:
install: true

# Release builds are produced with the Xcode 26.3 toolchain used in CI.
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1.7.0
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Build (unsigned)
run: mise run build-release

- name: Create .zip for distribution
run: |
MATCHES="$(find "$DERIVED_DATA_ROOT" -type d -path "*/Build/Products/Release/$APP_NAME" -print)"
MATCH_COUNT="$(printf '%s\n' "$MATCHES" | sed '/^$/d' | wc -l | tr -d ' ')"

if [ "$MATCH_COUNT" -eq 0 ]; then
echo "App bundle not found under $DERIVED_DATA_ROOT"
exit 1
fi

if [ "$MATCH_COUNT" -gt 1 ]; then
echo "Multiple app bundles found under $DERIVED_DATA_ROOT:"
printf '%s\n' "$MATCHES"
exit 1
fi

APP_PATH="$(printf '%s\n' "$MATCHES" | head -n 1)"
APP_BUNDLE_NAME="$(basename "$APP_PATH")"
echo "Found app at: $APP_PATH"
cp -R "$APP_PATH" .
zip -r "GitHubCommandCenter-${{ github.ref_name }}.zip" "$APP_BUNDLE_NAME"
# Print macOS installation/quarantine note to the job log.
echo "To install: unzip, move to /Applications, then right-click > Open on first launch (unsigned build)."

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: "GitHubCommandCenter-${{ github.ref_name }}.zip"
body: |
## GitHub Command Center ${{ github.ref_name }}

**Install:**
1. Download the `.zip` below and unzip it
2. Move `GitHubCommandCenter.app` to `/Applications`
3. First launch: right-click the app → **Open** (bypasses Gatekeeper for unsigned builds)

**Requirements:** macOS 14 (Sonoma) or later

**Setup:** Click the menu bar icon → enter your GitHub Personal Access Token (needs `repo` read scope)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Xcode
build/
DerivedData/
xcuserdata/
*.xcuserstate

# Swift Package Manager
.build/

# macOS
.DS_Store
*.swp
*~

# XcodeGen (project is regenerated from project.yml)
*.xcodeproj/project.pbxproj

# Claude Code
.claude/settings.local.json
.mise.local.toml
.env
.env.local

# gstack
.gstack/
.conductor/
75 changes: 75 additions & 0 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"indentConditionalCompilationBlocks" : true,
"indentSwitchCaseLabels" : false,
"indentation" : {
"spaces" : 4
},
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : true,
"lineBreakBeforeEachGenericRequirement" : false,
"lineBreakBetweenDeclarationAttributes" : false,
"lineLength" : 120,
"maximumBlankLines" : 1,
"multiElementCollectionTrailingCommas" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether" : false,
"reflowMultilineStringLiterals" : "never",
"respectsExistingLineBreaks" : true,
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLiteralForEmptyCollectionInit" : false,
"AlwaysUseLowerCamelCase" : true,
"AmbiguousTrailingClosureOverload" : true,
"AvoidRetroactiveConformances" : true,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : false,
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : true,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyLinesOpeningClosingBraces" : false,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoPlaygroundLiterals" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : false,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"TypeNamesShouldBeCapitalized" : true,
"UseEarlyExits" : false,
"UseExplicitNilCheckInConditions" : true,
"UseLetInEveryBoundCaseVariable" : true,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : true,
"UseSynthesizedInitializer" : true,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false
},
"spacesAroundRangeFormationOperators" : false,
"spacesBeforeEndOfLineComments" : 2,
"tabWidth" : 8,
"version" : 1
}
69 changes: 69 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
included:
- GitHubCommandCenter
- GitHubCommandCenterTests

excluded:
- GitHubCommandCenter.xcodeproj
- build
- DerivedData

disabled_rules:
- closing_brace
- colon
- comma
- control_statement
- empty_parentheses_with_trailing_closure
- indentation_width
- opening_brace
- operator_usage_whitespace
- return_arrow_whitespace
- static_over_final_class
- statement_position
- trailing_comma
- trailing_newline
- trailing_semicolon
- trailing_whitespace
- vertical_parameter_alignment
- vertical_parameter_alignment_on_call
- vertical_whitespace
- vertical_whitespace_between_cases

opt_in_rules:
- empty_count
- first_where
- force_unwrapping
- implicitly_unwrapped_optional
- modifier_order
- toggle_bool

line_length:
warning: 120
error: 200

file_length:
warning: 700
error: 1000

function_body_length:
warning: 110
error: 140

type_body_length:
warning: 600
error: 700

cyclomatic_complexity:
warning: 20
error: 30

identifier_name:
min_length:
warning: 2
error: 1
# Keep global exclusions empty. For legitimate API-shaped short names such as `id`,
# use scoped inline disables instead; do not add generic shortcuts like id/x/y/i/j/k here.
excluded: []

nesting:
type_level:
warning: 2
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Changelog

All notable changes to GitHub Command Center will be documented in this file.

## [0.1.0] - 2026-03-31

Initial MVP release. A macOS menu bar app that monitors your GitHub PRs and tells you what needs attention.

### Added

- **PR triage dashboard** in the menu bar — PRs are split into "Needs Your Action", "Waiting on Others", and "Your Drafts" so you always know what to look at first
- **Smart attention badge** — the menu bar icon shows how many PRs need your action, not the total count
- **Status dots** with click popovers showing CI, review, and merge status for each PR at a glance
- **Real-time notifications** for CI failures on your PRs, review requests, changes requested, approval, and merge conflicts
- **Adaptive polling** — polls every 60s for small workloads, backs off to 5min for large ones, with ETag caching to minimize API usage
- **Rate limit awareness** — detects GitHub API rate limits and pauses polling with a recovery timer
- **Token validation** — checks that your PAT has the right scopes and warns about missing permissions
- **Environment token bootstrapper** — automatically loads tokens from `GITHUB_TOKEN`, `GH_TOKEN`, or `.env.local` files during development
- **Inline settings panel** — configure your token and manage repos without leaving the menu bar popover
- **Draft PR section** — your drafts are tracked separately so they don't clutter the action list
- **Pagination safety** — caps API pagination to prevent runaway requests on very active accounts
- **Keychain storage** with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` for secure token persistence
- **Notification threading** — PR notifications are grouped under a shared thread identifier

### Changed

- Menu bar icon animates during initial load to show the app is working

### Fixed

- CI notification fires correctly on pending-to-failing transitions (not just passing-to-failing)
- Check-run API 403/404 falls back gracefully instead of failing the entire PR fetch
- Thread-safe access to notification handlers, date formatters, and mock test infrastructure
- Unterminated quoted tokens in `.env` files are rejected instead of silently truncated
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## Project

Swift/SwiftUI macOS 14+ menu bar app monitoring GitHub PR status. No external Swift dependencies.

## Build & Test (CLI)

- First-time setup: `mise install`
- Sync tools and regenerate the Xcode project: `mise run sync`
- Build: `mise run build`
- Test: `mise run test`
- Style checks: `mise run check`
- Fix, then re-run style checks and tests: `mise run verify`
- After editing `project.yml`: run `mise run sync`

## Skill routing

When the user's request matches an available skill, ALWAYS invoke it using the Skill
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions GitHubCommandCenter/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Loading