惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Google DeepMind News
Google DeepMind News
S
SegmentFault 最新的问题
Project Zero
Project Zero
D
DataBreaches.Net
I
InfoQ
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Vercel News
Vercel News
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
The Hacker News
The Hacker News
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
Help Net Security
Help Net Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Scott Helme
Scott Helme
T
Threatpost
爱范儿
爱范儿
N
Netflix TechBlog - Medium
D
Docker
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
K
Kaspersky official blog
H
Help Net Security
S
Secure Thoughts
T
Threat Research - Cisco Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security @ Cisco Blogs
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
G
Google Developers Blog
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
博客园 - 叶小钗
B
Blog
Google DeepMind News
Google DeepMind News
Recent Announcements
Recent Announcements
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
P
Privacy International News Feed
Spread Privacy
Spread Privacy
The Last Watchdog
The Last Watchdog

Peter Steinberger

OpenClaw, OpenAI and the future | Peter Steinberger Shipping at Inference-Speed | Peter Steinberger The Signature Flicker | Peter Steinberger Just Talk To It - the no-bs Way of Agentic Engineering | Peter Steinberger Claude Code Anonymous | Peter Steinberger Live Coding Session: Building Arena | Peter Steinberger My Current AI Dev Workflow | Peter Steinberger Essential Reading for Agentic Engineers - August 2025 | Peter Steinberger Just One More Prompt | Peter Steinberger Poltergeist: The Ghost That Keeps Your Builds Fresh | Peter Steinberger Don't read this Startup Slop | Peter Steinberger Essential Reading for Agentic Engineers - July 2025 | Peter Steinberger Self-Hosting AI Models After Claude's Usage Limits | Peter Steinberger Logging Privacy Shenanigans | Peter Steinberger VibeTunnel's first AI-anniversary | Peter Steinberger Making AppleScript Work in macOS CLI Tools: The Undocumented Parts | Peter Steinberger Peekaboo 2.0 – Free the CLI from its MCP shackles | Peter Steinberger Command your Claude Code Army, Reloaded | Peter Steinberger Essential Reading for Agentic Engineers | Peter Steinberger Slot Machines for Programmers: How Peter Builds Apps 20x Faster with AI | Peter Steinberger My AI Workflow for Understanding Any Codebase | Peter Steinberger stats.store: Privacy-First Sparkle Analytics | Peter Steinberger Showing Settings from macOS Menu Bar Items: A 5-Hour Journey | Peter Steinberger VibeTunnel: Turn Any Browser into Your Mac's Terminal | Peter Steinberger Vibe Meter 2.0: Calculating Claude Code Usage with Token Counting | Peter Steinberger llm.codes: Make Apple Docs AI-Readable | Peter Steinberger Automatic Observation Tracking in UIKit and AppKit: The Feature Apple Forgot to Mention | Peter Steinberger Peekaboo MCP – lightning-fast macOS screenshots for AI agents | Peter Steinberger Migrating 700+ Tests to Swift Testing: A Real-World Experience | Peter Steinberger Commanding Your Claude Code Army | Peter Steinberger Code Signing and Notarization: Sparkle and Tears | Peter Steinberger Claude Code is My Computer | Peter Steinberger Stop Over-thinking AI Subscriptions | Peter Steinberger Introducing Demark: HTML in. MD out. Blink-fast. | Peter Steinberger The Future of Vibe Coding: Building with AI, Live and Unfiltered | Peter Steinberger MCP Best Practices | Peter Steinberger Finding My Spark Again | Peter Steinberger Top-Level Menu Visibility in SwiftUI for macOS | Peter Steinberger Fixing keyboardShortcut in SwiftUI | Peter Steinberger Supporting Both Tap and Long Press on a Button in SwiftUI | Peter Steinberger On Using Apple Silicon Mac Mini for Continuous Integration | Peter Steinberger Apple Silicon M1: A Developer's Perspective | Peter Steinberger Gardening Your Twitter: Curating Your Timeline | Peter Steinberger Gardening Your Twitter: Growing Your Followers | Peter Steinberger Forbidden Controls in Catalyst: Optimize Interface for Mac | Peter Steinberger Disabling Keyboard Avoidance in SwiftUI's UIHostingController | Peter Steinberger The State of SwiftUI | Peter Steinberger Logging in Swift | Peter Steinberger Building with Swift Trunk Development Snapshots | Peter Steinberger Calling Super at Runtime in Swift | Peter Steinberger zld — A Faster Version of Apple's Linker | Peter Steinberger How to Fix LLDB: Couldn't IRGen Expression | Peter Steinberger Updating macOS on a Hackintosh | Peter Steinberger InterposeKit — Elegant Swizzling in Swift | Peter Steinberger The Great Mac Catalyst Text Input Crash Hunt | Peter Steinberger Jailbreaking for iOS Developers | Peter Steinberger Network Kernel Core Dump | Peter Steinberger How to macOS Core Dump | Peter Steinberger Kernel Panics and Surprise boot-args | Peter Steinberger The LG UltraFine 5K, kernel_task, and Me | Peter Steinberger Let's Try This Again | Peter Steinberger How We Work at PSPDFKit | Peter Steinberger Swizzling in Swift | Peter Steinberger WWDC for First-Timers, 2019 Edition | Peter Steinberger Challenges of Adopting Drag and Drop | Peter Steinberger Marzipan: Porting iOS Apps to the Mac | Peter Steinberger How to Use Slack and Not Go Crazy | Peter Steinberger Hardcore Debugging - Heavy Weapons for Hard Bugs | Peter Steinberger Binary Frameworks in Swift | Peter Steinberger Even Swiftier Objective-C | Peter Steinberger The Case for Deprecating UITableView | Peter Steinberger Running tests with Clang Address Sanitizer | Peter Steinberger UI testing on iOS, without busy waiting | Peter Steinberger Hiring a distributed team | Peter Steinberger Writing Good Bug Reports | Peter Steinberger Real-time collaboration, Apple, and you | Peter Steinberger Converting Xcode Test Runs to JUnit, the Fast Way | Peter Steinberger Efficient iOS Version Checking | Peter Steinberger Investigating Thread Safety of UIImage | Peter Steinberger Swifty Objective-C | Peter Steinberger Running UI Tests on iOS With Ludicrous Speed | Peter Steinberger A Pragmatic Approach to Cross-Platform | Peter Steinberger Surprises with Swift Extensions | Peter Steinberger Using ccache for Fun and Profit | Peter Steinberger UITableViewController designated initializer woes | Peter Steinberger Researching ResearchKit | Peter Steinberger The curious case of rotation with multiple windows on iOS 8 | Peter Steinberger UIKit Debug Mode | Peter Steinberger Retrofitting containsString: on iOS 7 | Peter Steinberger A Story About Swizzling "the Right Way™" and Touch Forwarding | Peter Steinberger Hacking with Aspects | Peter Steinberger Fixing UITextView On iOS 7 | Peter Steinberger Fixing What Apple Doesn't | Peter Steinberger How To Inspect The View Hierarchy Of Third-Party Apps | Peter Steinberger Fixing UISearchDisplayController On iOS 7 | Peter Steinberger Smart Proxy Delegation | Peter Steinberger Adding Keyboard Shortcuts To UIAlertView | Peter Steinberger How To Center Content Within UIScrollView | Peter Steinberger UIAppearance for Custom Views | Peter Steinberger Hacking Block Support Into UIMenuItem | Peter Steinberger
Vibe Meter: Monitor Your AI Costs | Peter Steinberger
Peter Steinberger · 2025-06-04 · via Peter Steinberger

My Cursor bill hit $900 for the month. I needed a simple way to track AI spending without constantly checking dashboards. That frustration sparked Vibe Meter - my first shipped Mac app built in three intense days.

What started as a workshop demo became a sophisticated menu bar utility using Swift 6, SwiftUI, and extensive AI assistance.

What the AIs Say

I had several AI models analyze the VibeMeter codebase:

O3: “Name vs. purpose mismatch – it sounds like a mood tracker, but monitors AI-API spending.”

Claude Opus: “86% test coverage is extraordinary. Code quality rivals what I see in major tech companies. This isn’t just a ‘side project’ - it’s a case study in how to build macOS software the right way.”

Gemini: “Intentionally over-engineered for future growth.”

From Workshop Demo to Production App

I ran a live workshop demonstrating AI-assisted development. Since Cursor lacks a public API, we reverse-engineered their web interface, extracted cookies, and called internal endpoints. Built both Electron and macOS versions in parallel.

The feedback surprised me - people actually found it useful. When Gergely Orosz mentioned linking to it in his newsletter, I spent two more days polishing the rough demo into something shippable.

Cursor has no public API for cost control, and it was somewhat messy because I have an enterprise account while many people have individual accounts, and the API endpoints are quite different. Getting both right was tricky.

Special thanks to Oliver Drobnik for helping me fix the individual account parsing. The second 90% of finishing an app always takes longer than the first 90%.

Looking for testers: Need someone with multiple Cursor teams to help test. Current implementation assumes one team only.

Key Technical Challenges

SwiftUI vs AppKit: The Eternal Dance

I used SwiftUI wherever possible, but macOS menu bar apps expose the framework’s limitations. Need a popover without the arrow? Drop down to AppKit. Want to show settings from a menu item? More AppKit gymnastics.

I needed a custom NSPanel for precise menu bar control. Memory management got tricky in release builds - weak references to windows deallocated before appearing.

Even showing settings required a reflection hack accessing internal NSMenuItem properties. Apple deprecated the old methods, but the “correct” SwiftUI approach only works from SwiftUI views.

The Animation Breakthrough

I’m proud of the animated menu bar icon. Almost used Lottie, but SwiftUI was enough. ChatGPT converted a static icon into animatable code.

The gauge moves with spending levels, uses conic gradients for loading shimmer. ImageRenderer converts SwiftUI to NSImage for the status item.

Swift 6 Concurrency Deep Dive

This was my first production app using Swift 6’s strict concurrency model. The architecture heavily relies on actors for thread safety:

The learning curve was steep - debugging UNUserNotificationCenter delegate callbacks firing on background threads while trying to update @MainActor state was particularly painful.

One bug drove me crazy: perfect in debug, window wouldn’t show in release. SwiftUI view held weakly by AppKit disappeared before display. No crash, just nothing.

Claude figured it out when I explained the symptoms. Reminder: always test release builds.

Apple’s Distribution Gauntlet

The hardest part wasn’t coding - it was signing, notarization, and distribution. Apple’s process is error-prone: you can notarize an app but miss deep notarization of embedded frameworks (like Sparkle), leaving you with a partially notarized app that won’t run.

Rather than wrestle with Fastlane, I built custom shell scripts that are much more debuggable. Now Claude can create releases by simply running “make a new release 1.1 beta 1” - the automation is beautiful once set up.

The scripts handle everything: incrementing build numbers, creating DMGs, uploading to GitHub releases, generating Sparkle appcast files. The most complex part was handling Sparkle’s EdDSA signatures correctly - the error messages when you get it wrong are cryptic at best. Pro tip: always test your update feed locally before pushing.

Interesting Technical Details

Multi-Provider Architecture

Though Vibe Meter only supports Cursor currently, the architecture anticipates multiple providers. The MultiProviderDataOrchestrator and protocol-based design make adding OpenAI or Anthropic straightforward when the time comes.

I really had to hold myself back from implementing OpenAI and Anthropic support right away. When I realized this would be useful for tracking all AI costs - not just Cursor - the temptation was strong. But I knew if I started adding providers, I’d never ship. So I built the architecture to support it but forced myself to ship with just Cursor. This is definitely coming in a future update.

The multi-provider design might seem like overkill, but it’s already paying dividends. When debugging, I can easily add a mock provider that returns test data. The orchestrator handles provider failures gracefully - if Cursor’s API is down, the app doesn’t crash, it just shows cached data with a subtle indicator.

Polish in the Details

Small touches matter. The app includes:

Agent-Friendly Development

I constrained every file to under 300 lines - agents make fewer mistakes with smaller files. This forced better organization and created a more maintainable codebase for both humans and AI. Using Tuist for project generation also helped since agents struggle with Xcode’s complex .pbxproj files.

By the Numbers

What emerged surprised even me:

  • 174 Swift files totaling 28,599 lines of code
  • 86% test coverage with 628 individual test methods
  • 18 shell scripts (2,857 lines) for build automation
  • 437 occurrences of Swift 6 concurrency keywords
  • 15 protocols for loose coupling and testability

The largest files respect the 300-line constraint: ApplicationMover.swift (390 lines), SparkleUpdaterManager.swift (361 lines), and Color+Theme.swift (358 lines). The build infrastructure alone required comprehensive scripts for code signing (349 lines), release automation (314 lines), and notarization (312 lines).

The Testing Philosophy

I wanted to see how well AI generates tests. For a hobby project like this, I probably wouldn’t have written any. But they caught real bugs:

  • Currency conversion edge cases (what happens when the exchange rate API is down?)
  • Cookie expiration handling (Cursor cookies last 30 days, not forever)
  • Timezone bugs (monthly spending resets at UTC midnight, not local time)
  • The classic “what if the user has no internet?” scenarios

The UserDefaultsBackedAdvancedTests alone has 374 lines testing every possible edge case of property wrappers. Did we need all of them? Probably not. Did they catch bugs? Absolutely.

Lessons Learned

Context windows matter. I ran six Claude instances concurrently - one for linting, one for tests, one for UI, one for features, one for bugs. First time trying this strategy. Worked beautifully. The more an agent knows about previous related tasks, the better it performs.

Small files matter. The 300-line constraint prevented agent editing mistakes and forced better architecture. What seemed like a limitation became a forcing function for good design.

SwiftUI has come far. Despite the AppKit gymnastics required for menu bar specifics, SwiftUI handled 90% of the UI beautifully. The animation capabilities particularly impressed me.

Modern Swift is powerful. Swift 6’s actor model and structured concurrency made complex state management surprisingly manageable, even for a concurrency novice.

AI agents love writing tests. Embrace it. The comprehensive test suite they generated caught bugs I never would have thought to test for.

Performance Notes

A menu bar app needs to be lightweight. Vibe Meter uses about 45MB of memory and essentially zero CPU when idle. The BackgroundDataProcessor refreshes data every 5 minutes by default, but backs off exponentially if errors occur.

One optimization I’m proud of: the app only loads exchange rates when you actually use a non-USD currency. The ExchangeRateManager caches rates for 24 hours and falls back gracefully if the API is down. Small touches, but they matter for an app that runs 24/7.

If you’re tracking AI costs, try Vibe Meter. Source on GitHub. Watch the workshop recording to see where it started.

Three days. One person. Six AI assistants. The future is weird.