A cyberpunk-styled macOS menu bar monitor for CPU, memory, disk, and Claude process usage.
The repository is named cyberpunk-performance-monitor, while the built app bundle is still named Performance Monitor.
Widget
|
Menu bar popover
|
Most system monitors are either too heavyweight for quick menu bar checks, too generic to explain what Claude is doing, or disconnected from macOS widgets.
This project is intentionally narrow:
- It is menu bar first, so you can check system pressure without opening a full dashboard.
- It is Claude-aware, so you can quickly tell whether CPU or memory pressure is coming from Claude-related process groups.
- It is local only, using native macOS APIs instead of background agents or remote services.
- It is widget-friendly, so the desktop widget can act as a quick entry point back into the menu bar HUD.
- Menu bar HUD: A translucent popover with circular gauges and usage bars for CPU, memory, and disk.
- 2-second live refresh: The menu bar app refreshes system stats every 2 seconds while it is running.
- Process list: Running regular apps are searchable and sorted by memory usage.
- Process controls: Each process row exposes
QuitandForce Quitin a context menu. - Claude monitoring: Aggregates CPU and RSS memory usage for Claude CLI and Claude desktop related process groups.
- Stale Claude detection: Flags runaway Claude process groups when a single process group exceeds 15 Claude processes.
- Release Memory: Kills stale Claude process groups, then asks the kernel to purge reclaimable memory.
- Widget support: Includes small and medium desktop widgets for CPU, memory, and disk at a glance.
- Widget deep link: Clicking the widget opens the menu bar popover through
perfmonitor://show. - Launch at Login: Can be toggled from the gear menu.
- Optional disk cleanup shortcut: The
CLEAN UP DISKbutton tries to open/Applications/DiskCleanupTool.appif it exists.
Core monitoring uses native Darwin and AppKit APIs. It does not shell out to external tools for CPU, memory, disk, or Claude process inspection.
PerformanceMonitor/
├── App/ # NSStatusItem app delegate and widget URL handler
├── Services/
│ ├── MemoryPurgeService # Stale Claude cleanup + vm_purgable_control
│ └── ProcessManager # Claude process discovery, grouping, and termination
├── ViewModels/
│ └── MonitorViewModel # 2s app refresh + 30s widget push
└── Views/ # Popover UI, overview cards, process list
PerformanceMonitorWidget/ # WidgetKit extension and widget views
Shared/
├── Extensions/ # Theme and formatting helpers
├── Models/ # SystemStats and AppProcessInfo
└── Services/ # CPU, memory, disk, and shared stats store
- CPU monitoring: Uses
host_statistics(..., HOST_CPU_LOAD_INFO, ...)and computes a delta between samples. - Memory monitoring: Uses
host_statistics64(..., HOST_VM_INFO64, ...)and reports active + wired + compressed memory. - Disk monitoring: Reads filesystem attributes from
NSHomeDirectory()viaFileManager. - Claude process detection: Uses
proc_listallpids,proc_pidinfo, andproc_pidpathto find Claude-related process groups. - Stale cleanup threshold: Any single process group with more than 15 Claude processes is treated as stale.
- Widget data sharing: The app and widget share
latestStats.jsonthrough the app groupgroup.com.alanfeng.performance-monitor. - Widget freshness model:
- When the menu bar app is running, it pushes fresh stats to the shared store every 30 seconds and reloads widget timelines.
- When the app is not running, the widget falls back to its own timeline refresh, scheduled every 15 minutes.
- Permissions: Core monitoring itself does not require shell access, Accessibility, or
osascript. App icon lookup and macOS policy checks are still subject to normal platform behavior.
This repo is currently source-first. There is no packaged installer or notarized release flow documented in the repo yet.
For a working local install:
- Install Xcode 15 or newer.
- Make sure you have an Apple Developer account available for signing the app, widget extension, and app group container.
- Build the app locally.
- Copy
Performance Monitor.appinto/Applications. - Launch the app once before adding or testing the widget.
- If macOS blocks the first launch, go to
System Settings > Privacy & Securityand clickOpen AnywayforPerformance Monitor.
This project currently ships DMGs signed with a local Apple Development identity and not notarized, so the first launch on another Mac may require that manual Open Anyway step.
If you only want to hack on the code, building from Xcode is enough. If you want the widget deep link to behave like a normal installed app, keep the signed build in /Applications.
The repo already includes a generated PerformanceMonitor.xcodeproj. You only need XcodeGen if you change project.yml.
# Optional: install XcodeGen if you plan to regenerate the project
brew install xcodegen
# Optional: regenerate the Xcode project after editing project.yml
xcodegen generate
# Build a signed Debug app
xcodebuild -scheme PerformanceMonitor -configuration Debug build -allowProvisioningUpdates
# Install the built app into /Applications
cp -R "$(xcodebuild -scheme PerformanceMonitor -configuration Debug -showBuildSettings 2>/dev/null | grep ' BUILT_PRODUCTS_DIR' | awk '{print $3}')/Performance Monitor.app" "/Applications/Performance Monitor.app"
# Launch it
open -a "/Applications/Performance Monitor.app"If macOS warns that the app cannot be opened because it is from an unidentified or unverified developer, move it into /Applications, try launching it once, then go to System Settings > Privacy & Security and use Open Anyway.
You can also open PerformanceMonitor.xcodeproj in Xcode and run the PerformanceMonitor scheme directly.
- macOS 14.0+
- Xcode 15.0+
- Swift 5.9+
- An Apple Developer account for signing the app, widget extension, and app group container
- Optional:
/Applications/DiskCleanupTool.appif you want the disk cleanup shortcut to do something
- No release packaging workflow yet: The documented install path is still local build plus local signing.
- Widget updates are best when the app is running: Without the menu bar app pushing fresh data, widget refresh falls back to a slower 15-minute timeline.
- Widget deep linking depends on a healthy signed install: Expired local provisioning profiles or multiple registered copies can break widget launch behavior.
- Claude detection is heuristic-based: It currently identifies Claude by process path and process-group behavior, so future Claude packaging changes may require code updates.
- Process list is intentionally scoped: It shows regular running apps, not every daemon, helper, or launch agent on the machine.
- Disk cleanup is external: The
CLEAN UP DISKbutton only opens/Applications/DiskCleanupTool.app; this repo does not implement disk cleanup itself.
- The widget says the app cannot be opened: Rebuild with
-allowProvisioningUpdates, reinstall the app to/Applications, then remove and re-add the widget if needed. Local development provisioning profiles for the widget can expire. - The widget looks stale: Launch the menu bar app once. While the app is running, it refreshes the shared widget data every 30 seconds.
- The
CLEAN UP DISKbutton does nothing: InstallDiskCleanupTool.appinto/Applicationsor remove that integration from the UI. - Widget deep link opens the wrong copy during development: Keep the signed app in
/Applicationsand avoid leaving multiple registered copies inDerivedDataand/Applicationsat the same time.
Issues and pull requests are welcome.
If you are filing a bug, include:
- macOS version
- Xcode version
- whether you ran the app from Xcode or from
/Applications - whether the problem is in the menu bar app, the widget, or the deep link between them
- screenshots or logs when available
If you are sending a PR:
- keep changes aligned with the current native SwiftUI/AppKit/Darwin approach
- update
project.ymlif you change project structure, then regeneratePerformanceMonitor.xcodeproj - update this README when behavior, installation steps, or limitations change
MIT

