Skip to content

Commit 3030e91

Browse files
committed
docs: simplify agent instructions
1 parent 4563b99 commit 3030e91

1 file changed

Lines changed: 3 additions & 177 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,5 @@
1-
# AGENTS.md
1+
ClaudeMeter is a macOS 14+ SwiftUI menu bar app; keep UI state on `@MainActor @Observable` types and non-UI work in actor services/repositories.
22

3-
## Project Overview
3+
Build with `xcodebuild clean build -project ClaudeMeter.xcodeproj -scheme ClaudeMeter -configuration Debug`; test with `xcodebuild test -project ClaudeMeter.xcodeproj -scheme ClaudeMeter -configuration Debug`.
44

5-
macOS 14+ menu bar app monitoring Claude.ai usage limits. Swift 6 / SwiftUI with @MainActor @Observable state management and actor-isolated services.
6-
7-
## Setup
8-
9-
```bash
10-
# Clone and open
11-
git clone git@github.com:eddmann/ClaudeMeter.git
12-
cd ClaudeMeter
13-
open ClaudeMeter.xcodeproj
14-
15-
# Build from CLI (requires Xcode 16+)
16-
xcodebuild clean build \
17-
-project ClaudeMeter.xcodeproj \
18-
-scheme ClaudeMeter \
19-
-configuration Debug
20-
```
21-
22-
Press ⌘R in Xcode to run. App appears in menu bar (not Dock).
23-
24-
## Common Commands
25-
26-
| Task | Command |
27-
|------|---------|
28-
| Build (Debug) | `xcodebuild clean build -project ClaudeMeter.xcodeproj -scheme ClaudeMeter -configuration Debug` |
29-
| Build (Release) | `xcodebuild clean build -project ClaudeMeter.xcodeproj -scheme ClaudeMeter -configuration Release -derivedDataPath ./build -arch x86_64 -arch arm64` |
30-
| Run Tests | `xcodebuild test -project ClaudeMeter.xcodeproj -scheme ClaudeMeter -configuration Debug` |
31-
| Open in Xcode | `open ClaudeMeter.xcodeproj` |
32-
33-
No linting or formatting tools configured.
34-
35-
## Code Conventions
36-
37-
### File Organization
38-
```
39-
ClaudeMeter/
40-
├── App/ # AppModel.swift, ClaudeMeterApp.swift (entry point)
41-
├── Models/ # Data types, API/, Errors/
42-
├── Services/ # Actor-isolated business logic, Protocols/
43-
├── Repositories/ # Data persistence (Keychain, UserDefaults, Cache), Protocols/
44-
└── Views/ # SwiftUI components: MenuBar/, Settings/, Setup/
45-
```
46-
47-
### Naming
48-
- One type per file, filename matches type: `UsageData.swift`, `NetworkService.swift`
49-
- Protocols in `Protocols/` subdirectory: `UsageServiceProtocol.swift`
50-
- Error types suffixed: `AppError.swift`, `NetworkError.swift`
51-
52-
### Concurrency Model
53-
- `@MainActor @Observable` for AppModel and all views
54-
- `actor` for services and repositories (thread-safe)
55-
- `async/await` end-to-end
56-
- Mark non-published dependencies `@ObservationIgnored`
57-
58-
### Pattern Examples
59-
60-
**Observable state owner:**
61-
```swift
62-
@MainActor @Observable final class AppModel {
63-
var settings: AppSettings = .default
64-
@ObservationIgnored private let service: ServiceProtocol
65-
}
66-
```
67-
68-
**Actor-isolated service:**
69-
```swift
70-
actor UsageService: UsageServiceProtocol {
71-
private static let logger = Logger(subsystem: "com.claudemeter", category: "UsageService")
72-
func fetchUsage() async throws -> UsageData { }
73-
}
74-
```
75-
76-
**Dependency injection (constructor with defaults):**
77-
```swift
78-
init(
79-
settingsRepository: SettingsRepositoryProtocol = SettingsRepository(),
80-
keychainRepository: KeychainRepositoryProtocol = KeychainRepository()
81-
) { }
82-
```
83-
84-
### Imports
85-
System frameworks first, no third-party deps in main target:
86-
```swift
87-
import SwiftUI
88-
import Observation
89-
import os
90-
```
91-
92-
## Tests & CI
93-
94-
### Running Tests
95-
```bash
96-
# All tests
97-
xcodebuild test -project ClaudeMeter.xcodeproj -scheme ClaudeMeter
98-
99-
# Specific test class
100-
xcodebuild test -project ClaudeMeter.xcodeproj -scheme ClaudeMeter \
101-
-only-testing ClaudeMeterTests/AppModelTests
102-
```
103-
104-
### Test Structure
105-
- `ClaudeMeterTests/` - XCTest with `@MainActor` async tests
106-
- `TestDoubles/` - Stubs, fakes, spies for protocol-based DI
107-
- `__Snapshots__/` - Snapshot test reference images (SnapshotTesting library)
108-
109-
### CI
110-
- No automated PR checks (build/tests not run on PRs)
111-
- Release workflow: manual trigger via GitHub Actions
112-
- Builds universal binary, signs with Developer ID, notarizes with Apple
113-
114-
## PR & Workflow Rules
115-
116-
### Commit Format
117-
```
118-
type(scope): description
119-
```
120-
121-
Examples:
122-
```
123-
feat(menu-bar): add battery icon style
124-
fix(settings): remove icon preview flicker
125-
refactor(app): modernize app lifecycle
126-
docs: add session key instructions
127-
```
128-
129-
Types: `feat`, `fix`, `refactor`, `docs`, `test`
130-
131-
### Branch
132-
- `main` is the release branch
133-
- No PR templates or CODEOWNERS
134-
135-
### Release Process
136-
1. Trigger GitHub Actions workflow with version (e.g., `1.0.0`)
137-
2. Builds, signs, notarizes automatically
138-
3. Creates GitHub release with signed ZIP
139-
4. Updates Homebrew tap
140-
141-
## Security & Gotchas
142-
143-
### Session Keys
144-
- Format: `sk-ant-*` (validated by `SessionKey` initializer)
145-
- Stored in Keychain only (service: `com.claudemeter.sessionkey`)
146-
- Never serialize to disk or logs
147-
- May contain embedded org UUID
148-
149-
### Files to Never Commit
150-
- `*.p12`, `*.mobileprovision` (certificates)
151-
- API keys, session keys
152-
- `xcuserdata/`, `DerivedData/`
153-
- `build/` output directory
154-
155-
### Non-Obvious Patterns
156-
- Cache TTL is 55 seconds (< 60s minimum refresh interval)
157-
- Staleness threshold: 1200 seconds (shows "stale" indicator)
158-
- Retry: 2^n for network errors, 3^n for rate limits, no retry for auth failures
159-
- `@ObservationIgnored` required for non-UI dependencies in AppModel
160-
- Cancel existing `Task` before creating new ones (prevent duplicates)
161-
162-
### Constants
163-
Key values in `ClaudeMeter/Models/Constants.swift`:
164-
- `Constants.Cache.ttl` = 55 seconds
165-
- `Constants.Network.maxRetries` = 3
166-
- `Constants.Refresh.minimum` = 60 seconds
167-
- `Constants.Refresh.maximum` = 600 seconds
168-
169-
### Adding a New Setting
170-
1. Add property to `AppSettings` struct
171-
2. Settings auto-persist via `SettingsRepository` on change
172-
3. Add UI control in `SettingsView` bound to `appModel.settings`
173-
4. If behavior depends on setting, react in `AppModel.scheduleSettingsSave()`
174-
175-
### Adding a New API Endpoint
176-
1. Define response model in `Models/API/`
177-
2. Add method to protocol in `Services/Protocols/`
178-
3. Implement in service with retry logic
179-
4. Call from `AppModel` or helper
5+
New `AppSettings` keys must persist through `SettingsRepository`, appear in `SettingsView` when user-facing, and decode old saved settings safely.

0 commit comments

Comments
 (0)