Guidance for AI coding assistants (Claude, GitHub Copilot, Cursor, etc.) working on this repository.
You are a Senior Swift Engineer specializing in SwiftUI, Swift Concurrency, and macOS development. Your code must adhere to Apple's Human Interface Guidelines. Target Swift 6.0+ and macOS 26.0+.
A native macOS YouTube Music client built with Swift and SwiftUI.
- Apple Music-style UI: Liquid Glass player bar, clean sidebar navigation
- Browser-cookie authentication: Auto-extracts cookies from an in-app login WebView
- Hidden WebView playback: Singleton WebView for YouTube Music Premium (DRM content)
- Background audio: Audio continues when window is closed, stops on quit
- Native UI: SwiftUI sidebar navigation, player bar, and content views
- System integration: Now Playing in Control Center, media keys, Dock menu
App/ β App entry point, AppDelegate (window lifecycle)
Core/
βββ Models/ β Data models (Song, Playlist, Album, Artist, etc.)
βββ Services/
β βββ API/ β YTMusicClient, Parsers/ (response parsing)
β βββ Auth/ β AuthService (login state machine)
β βββ Player/ β PlayerService, NowPlayingManager (playback, media keys)
β βββ WebKit/ β WebKitManager (cookie store, persistent login)
βββ ViewModels/ β HomeViewModel, LibraryViewModel, SearchViewModel
βββ Utilities/ β DiagnosticsLogger, extensions
Views/
βββ macOS/ β SwiftUI views (MainWindow, Sidebar, PlayerBar, etc.)
Tests/ β Unit tests (KasetTests/)
Tools/ β Standalone CLI tools (api-explorer.swift)
docs/ β Detailed documentation
βββ adr/ β Architecture Decision Records
For detailed information, see the docs/ folder:
- docs/architecture.md β Services, state management, data flow
- docs/playback.md β WebView playback system, background audio
- docs/testing.md β Test commands, patterns, debugging
- docs/adr/ β Architecture Decision Records (ADRs)
- Read PLAN.md β Contains the phased implementation plan
- Understand the playback architecture β See docs/playback.md
- Check ADRs for past decisions β See docs/adr/ before proposing architectural changes
- Consult API documentation before implementing API features β See docs/api-discovery.md for endpoint reference
β οΈ MANDATORY: Before implementing ANY feature that requires a new or modified API call, you MUST explore the endpoint first using the API Explorer tool. Do NOT guess or assume API response structures.
Use the standalone CLI tool to explore endpoints before writing any code:
# Check authentication status
./Tools/api-explorer.swift auth
# List all known endpoints
./Tools/api-explorer.swift list
# Explore public browse endpoints
./Tools/api-explorer.swift browse FEmusic_charts
./Tools/api-explorer.swift browse FEmusic_moods_and_genres
# Explore authenticated endpoints (requires Kaset sign-in)
./Tools/api-explorer.swift browse FEmusic_liked_playlists
./Tools/api-explorer.swift browse FEmusic_history
# Explore with verbose output to see raw JSON
./Tools/api-explorer.swift browse FEmusic_home -v
# Explore action endpoints
./Tools/api-explorer.swift action search '{"query":"taylor swift"}'
./Tools/api-explorer.swift action player '{"videoId":"dQw4w9WgXcQ"}'The tool automatically uses cookies from the Kaset app for authenticated endpoints.
Review docs/api-discovery.md to see if the endpoint is already documented with its auth requirements and response structure.
If the endpoint requires authentication:
- Run
./Tools/api-explorer.swift authto check cookie status - If no cookies, run the Kaset app and sign in to YouTube Music
- The app saves cookies to
~/Library/Application Support/Kaset/cookies.dat - Run the API explorer again
If you discover new response structures or endpoint behaviors, update docs/api-discovery.md with your findings.
β οΈ Do NOT guess API structures β Always verify with the API Explorer tool or documentation before writing parsers. Incorrect assumptions lead to runtime failures.
π¨ NEVER leak secrets, cookies, API keys, or tokens β Under NO circumstances include real cookies, authentication tokens, API keys, SAPISID values, or any sensitive credentials in code, comments, logs, documentation, test fixtures, or any output. Always use placeholder values like
"REDACTED","mock-token", or"test-cookie"in examples and tests. This applies to all files including tests, docs, and ADRs. Violation of this rule is a critical security incident.
β οΈ NEVER rungit commitorgit pushβ Always leave committing and pushing to the human.
β οΈ ALWAYS confirm before running UI tests β UI tests launch the app and can be disruptive. Ask the human for permission before executing any UI test.
β οΈ No Third-Party Frameworks β Do not introduce third-party dependencies without asking first.
β οΈ Prefer API over WebView β Always useYTMusicClientAPI calls when functionality exists. Only use WebView for playback (DRM-protected audio) and authentication. API calls are faster, more testable, and reduce WebView complexity.
π Document Architectural Decisions β For significant design changes, create an ADR in
docs/adr/following the format in docs/adr/README.md.
β‘ Performance Awareness β For non-trivial features, run performance tests and verify no anti-patterns. When adding parsers or API calls, include
measure {}tests.
π§ Improve API Explorer, Don't Write One-Off Scripts β When exploring or debugging API-related functionality, always enhance
Tools/api-explorer.swiftinstead of writing temporary scripts. This ensures the tool grows with the project, maintains consistency, and provides reusable functionality for future API work. If you need to fetch raw JSON, test a new endpoint, or debug response parsing, add that capability to the API explorer.
After modifying code, verify the build:
xcodebuild -scheme Kaset -destination 'platform=macOS' buildswiftlint --strict && swiftformat .
β οΈ SwiftFormat--self insertrule: The project uses--self insertin.swiftformat. This means:
- In static methods, call other static methods with
Self.methodName()(not baremethodName())- In instance methods, use
self.propertyexplicitlyAlways run
swiftformat .before completing work to auto-fix these issues.
| β Avoid | β Use |
|---|---|
.foregroundColor() |
.foregroundStyle() |
.cornerRadius() |
.clipShape(.rect(cornerRadius:)) |
onChange(of:) { newValue in } |
onChange(of:) { _, newValue in } |
Task.sleep(nanoseconds:) |
Task.sleep(for: .seconds()) |
NavigationView |
NavigationSplitView or NavigationStack |
onTapGesture() |
Button (unless tap location needed) |
tabItem() |
Tab API |
AnyView |
Concrete types or @ViewBuilder |
print() |
DiagnosticsLogger |
DispatchQueue |
Swift concurrency (async/await) |
String(format: "%.2f", n) |
Text(n, format: .number.precision(...)) |
Force unwraps (!) |
Optional handling or guard |
| Image-only buttons without labels | Add .accessibilityLabel() |
.background(.ultraThinMaterial) |
.glassEffect() for macOS 26+ |
See docs/architecture.md#ui-design-macos-26 for detailed patterns.
Quick Rules:
- Use
.glassEffect(.regular.interactive(), in: .capsule)for interactive elements - Wrap multiple glass elements in
GlassEffectContainer - Add
PlayerBarviasafeAreaInsetto every navigable view - Avoid glass-on-glass (no
.buttonStyle(.glass)inside glass containers)
β Use Swift Testing for all new unit tests β See docs/testing.md and ADR-0006 for full patterns.
Quick Reference:
- Use
@Suite struct+@Test func(not XCTest) - Use
#expect(a == b)(notXCTAssertEqual) - Use
.serializedfor@MainActortest suites - Keep performance tests (
measure {}) and UI tests in XCTest
- Mark
@Observableclasses with@MainActor - Never use
DispatchQueueβ useasync/await,MainActor
- Use
WebKitManager's sharedWKWebsiteDataStorefor cookie persistence - Use
SingletonPlayerWebView.sharedfor playback (never create multiple WebViews) - Compute
SAPISIDHASHfresh per request using current cookies
- Throw
YTMusicError.authExpiredon HTTP 401/403 - Use
DiagnosticsLoggerfor all logging (notprint()) - Show user-friendly error messages with retry options
| File | Purpose |
|---|---|
Tools/api-explorer.swift |
Standalone API explorer CLI (run before implementing API features) |
App/AppDelegate.swift |
Window lifecycle, background audio support |
Core/Services/WebKit/WebKitManager.swift |
Cookie store & persistence |
Core/Services/Auth/AuthService.swift |
Login state machine |
Core/Services/Player/PlayerService.swift |
Playback state & control |
Views/macOS/MiniPlayerWebView.swift |
Singleton WebView, playback UI |
Views/macOS/MainWindow.swift |
Main app window |
Core/Utilities/DiagnosticsLogger.swift |
Logging |
See docs/testing.md for full test commands and patterns.
# Build
xcodebuild -scheme Kaset -destination 'platform=macOS' build
# Unit Tests
xcodebuild -scheme Kaset -destination 'platform=macOS' test -only-testing:KasetTests
# Lint & Format
swiftlint --strict && swiftformat .
β οΈ NEVER run unit tests and UI tests together β Always execute them separately.
UI Tests β Ask permission first, run ONE at a time:
xcodebuild -scheme Kaset -destination 'platform=macOS' test \
-only-testing:KasetUITests/TestClassName/testMethodNameSee docs/architecture.md and docs/playback.md for detailed flows.
Key Concepts:
- Singleton WebView for playback (DRM requires WebKit)
- Background audio via
windowShouldClosereturningfalse(hides instead of closes) - Cookie-based auth with
__Secure-3PAPISIDextracted from WebView - API-first β use
YTMusicClientfor data, WebView only for playback/auth
Before completing non-trivial features, verify:
- No
awaitcalls inside loops orForEach - Lists use
LazyVStack/LazyHStackfor large datasets - Network calls cancelled on view disappear (
.taskhandles this) - Parsers have
measure {}tests if processing large payloads - Images use
ImageCachewith appropriatetargetSize(not loading inline) - Search input is debounced (not firing on every keystroke)
- ForEach uses stable identity (avoid
Array(enumerated())unless rank is needed) - Frequently updating UI (e.g., progress) caches formatted strings
π See docs/architecture.md#performance-guidelines for detailed patterns.
For any non-trivial task, plan in phases with testable exit criteria before writing code. This ensures incremental progress and early detection of issues.
Every task should be broken into phases. Each phase must have:
- Clear deliverable β What artifact or change is produced
- Testable exit criteria β How to verify the phase is complete
- Rollback point β The phase should leave the codebase in a working state
| Deliverable | Exit Criteria |
|---|---|
| Identify affected files and dependencies | List all files to modify/create |
| Understand existing patterns | Can explain how similar features work |
| Read relevant docs | Confirmed patterns in docs/ apply |
Exit gate: Can articulate the implementation plan without ambiguity.
| Deliverable | Exit Criteria |
|---|---|
| Define new types/protocols | Type signatures compile |
| Plan public API surface | No breaking changes to existing callers (or changes identified) |
Exit gate: xcodebuild build succeeds with stub implementations.
| Deliverable | Exit Criteria |
|---|---|
| Implement business logic | Unit tests pass for new code |
| Handle error cases | Error paths have test coverage |
| Add logging | DiagnosticsLogger calls in place |
| Performance verified | Anti-pattern checklist passed, perf tests added if applicable |
Exit gate: xcodebuild test -only-testing:KasetTests passes.
| Deliverable | Exit Criteria |
|---|---|
| Linting passes | swiftlint --strict reports 0 errors |
| Formatting applied | swiftformat . makes no changes |
| Full test suite passes | xcodebuild test succeeds |
Exit gate: CI-equivalent checks pass locally.
Phase 1: Research
βββ Exit: Understand YTMusicClient pattern, confirm no existing solution
Phase 2: Interface
βββ Create NewService.swift with protocol + stub
βββ Exit: `xcodebuild build` passes
Phase 3: Implementation
βββ Implement methods, add error handling
βββ Create NewServiceTests.swift
βββ Exit: `xcodebuild test -only-testing:KasetTests/NewServiceTests` passes
Phase 4: QA
βββ Run swiftlint, swiftformat
βββ Exit: Full test suite passes, no lint errors
After each phase, briefly report:
- β What was completed
- π§ͺ Test/verification results
- β‘οΈ Next phase plan
This keeps the human informed and provides natural points to course-correct.