{"owner":"farzaa","repo":"clicky","hasSkills":true,"totalSkillsCount":2,"totalTokensCount":3302,"categories":["root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["leanring-buddy/AGENTS.md","AGENTS.md"],"skills":{"leanring-buddy/AGENTS.md":"# AGENTS.md - leanring-buddy (Main App Target)\n\n## Source Files\n\n### FloatingSessionButton.swift\n- `FloatingSessionButtonManager` — `@MainActor` class managing the `NSPanel` lifecycle\n  - `showFloatingButton()` — Creates/shows the panel in top-right of primary screen\n  - `hideFloatingButton()` — Hides panel (keeps it alive for quick re-show)\n  - `destroyFloatingButton()` — Removes panel permanently (session ended)\n  - `onFloatingButtonClicked` — Callback closure, set by ContentView to bring main window to front\n  - `floatingButtonPanel` — Exposed `NSPanel` reference for screenshot exclusion\n- `FloatingButtonView` — Private SwiftUI view with gradient circle, scale+glow hover animation, pointer cursor\n\n### ContentView.swift\n- Receives `FloatingSessionButtonManager` via `@EnvironmentObject`\n- `isMainWindowCurrentlyFocused` — Tracks main window focus state\n- `configureFloatingButtonManager()` — Wires up the click callback\n- `startObservingMainWindowFocusChanges()` — Sets up `NSWindow` notification observers\n- `updateFloatingButtonVisibility()` — Core logic: show if running + not focused, hide otherwise\n- `bringMainWindowToFront()` — Activates app and orders main window front\n\n### ScreenshotManager.swift\n- `floatingButtonWindowToExcludeFromCaptures` — `NSWindow?` reference set by ContentView\n- `captureScreen()` — Matches the floating window to an `SCWindow` and excludes it from capture filter\n\n### leanring_buddyApp.swift\n- Owns `FloatingSessionButtonManager` as `@StateObject`\n- Injects it into ContentView via `.environmentObject()`\n","AGENTS.md":"# Clicky - Agent Instructions\n\n<!-- This is the single source of truth for all AI coding agents. CLAUDE.md is a symlink to this file. -->\n<!-- AGENTS.md spec: https://github.com/agentsmd/agents.md — supported by Claude Code, Cursor, Copilot, Gemini CLI, and others. -->\n\n## Overview\n\nmacOS menu bar companion app. Lives entirely in the macOS status bar (no dock icon, no main window). Clicking the menu bar icon opens a custom floating panel with companion voice controls. Uses push-to-talk (ctrl+option) to capture voice input, transcribes it via AssemblyAI streaming, and sends the transcript + a screenshot of the user's screen to Claude. Claude responds with text (streamed via SSE) and voice (ElevenLabs TTS). A blue cursor overlay can fly to and point at UI elements Claude references on any connected monitor.\n\nAll API keys live on a Cloudflare Worker proxy — nothing sensitive ships in the app.\n\n## Architecture\n\n- **App Type**: Menu bar-only (`LSUIElement=true`), no dock icon or main window\n- **Framework**: SwiftUI (macOS native) with AppKit bridging for menu bar panel and cursor overlay\n- **Pattern**: MVVM with `@StateObject` / `@Published` state management\n- **AI Chat**: Claude (Sonnet 4.6 default, Opus 4.6 optional) via Cloudflare Worker proxy with SSE streaming\n- **Speech-to-Text**: AssemblyAI real-time streaming (`u3-rt-pro` model) via websocket, with OpenAI and Apple Speech as fallbacks\n- **Text-to-Speech**: ElevenLabs (`eleven_flash_v2_5` model) via Cloudflare Worker proxy\n- **Screen Capture**: ScreenCaptureKit (macOS 14.2+), multi-monitor support\n- **Voice Input**: Push-to-talk via `AVAudioEngine` + pluggable transcription-provider layer. System-wide keyboard shortcut via listen-only CGEvent tap.\n- **Element Pointing**: Claude embeds `[POINT:x,y:label:screenN]` tags in responses. The overlay parses these, maps coordinates to the correct monitor, and animates the blue cursor along a bezier arc to the target.\n- **Concurrency**: `@MainActor` isolation, async/await throughout\n- **Analytics**: PostHog via `ClickyAnalytics.swift`\n\n### API Proxy (Cloudflare Worker)\n\nThe app never calls external APIs directly. All requests go through a Cloudflare Worker (`worker/src/index.ts`) that holds the real API keys as secrets.\n\n| Route | Upstream | Purpose |\n|-------|----------|---------|\n| `POST /chat` | `api.anthropic.com/v1/messages` | Claude vision + streaming chat |\n| `POST /tts` | `api.elevenlabs.io/v1/text-to-speech/{voiceId}` | ElevenLabs TTS audio |\n| `POST /transcribe-token` | `streaming.assemblyai.com/v3/token` | Fetches a short-lived (480s) AssemblyAI websocket token |\n\nWorker secrets: `ANTHROPIC_API_KEY`, `ASSEMBLYAI_API_KEY`, `ELEVENLABS_API_KEY`\nWorker vars: `ELEVENLABS_VOICE_ID`\n\n### Key Architecture Decisions\n\n**Menu Bar Panel Pattern**: The companion panel uses `NSStatusItem` for the menu bar icon and a custom borderless `NSPanel` for the floating control panel. This gives full control over appearance (dark, rounded corners, custom shadow) and avoids the standard macOS menu/popover chrome. The panel is non-activating so it doesn't steal focus. A global event monitor auto-dismisses it on outside clicks.\n\n**Cursor Overlay**: A full-screen transparent `NSPanel` hosts the blue cursor companion. It's non-activating, joins all Spaces, and never steals focus. The cursor position, response text, waveform, and pointing animations all render in this overlay via SwiftUI through `NSHostingView`.\n\n**Global Push-To-Talk Shortcut**: Background push-to-talk uses a listen-only `CGEvent` tap instead of an AppKit global monitor so modifier-based shortcuts like `ctrl + option` are detected more reliably while the app is running in the background.\n\n**Shared URLSession for AssemblyAI**: A single long-lived `URLSession` is shared across all AssemblyAI streaming sessions (owned by the provider, not the session). Creating and invalidating a URLSession per session corrupts the OS connection pool and causes \"Socket is not connected\" errors after a few rapid reconnections.\n\n**Transient Cursor Mode**: When \"Show Clicky\" is off, pressing the hotkey fades in the cursor overlay for the duration of the interaction (recording → response → TTS → optional pointing), then fades it out automatically after 1 second of inactivity.\n\n## Key Files\n\n| File | Lines | Purpose |\n|------|-------|---------|\n| `leanring_buddyApp.swift` | ~89 | Menu bar app entry point. Uses `@NSApplicationDelegateAdaptor` with `CompanionAppDelegate` which creates `MenuBarPanelManager` and starts `CompanionManager`. No main window — the app lives entirely in the status bar. |\n| `CompanionManager.swift` | ~1026 | Central state machine. Owns dictation, shortcut monitoring, screen capture, Claude API, ElevenLabs TTS, and overlay management. Tracks voice state (idle/listening/processing/responding), conversation history, model selection, and cursor visibility. Coordinates the full push-to-talk → screenshot → Claude → TTS → pointing pipeline. |\n| `MenuBarPanelManager.swift` | ~243 | NSStatusItem + custom NSPanel lifecycle. Creates the menu bar icon, manages the floating companion panel (show/hide/position), installs click-outside-to-dismiss monitor. |\n| `CompanionPanelView.swift` | ~761 | SwiftUI panel content for the menu bar dropdown. Shows companion status, push-to-talk instructions, model picker (Sonnet/Opus), permissions UI, DM feedback button, and quit button. Dark aesthetic using `DS` design system. |\n| `OverlayWindow.swift` | ~881 | Full-screen transparent overlay hosting the blue cursor, response text, waveform, and spinner. Handles cursor animation, element pointing with bezier arcs, multi-monitor coordinate mapping, and fade-out transitions. |\n| `CompanionResponseOverlay.swift` | ~217 | SwiftUI view for the response text bubble and waveform displayed next to the cursor in the overlay. |\n| `CompanionScreenCaptureUtility.swift` | ~132 | Multi-monitor screenshot capture using ScreenCaptureKit. Returns labeled image data for each connected display. |\n| `BuddyDictationManager.swift` | ~866 | Push-to-talk voice pipeline. Handles microphone capture via `AVAudioEngine`, provider-aware permission checks, keyboard/button dictation sessions, transcript finalization, shortcut parsing, contextual keyterms, and live audio-level reporting for waveform feedback. |\n| `BuddyTranscriptionProvider.swift` | ~100 | Protocol surface and provider factory for voice transcription backends. Resolves provider based on `VoiceTranscriptionProvider` in Info.plist — AssemblyAI, OpenAI, or Apple Speech. |\n| `AssemblyAIStreamingTranscriptionProvider.swift` | ~478 | Streaming transcription provider. Fetches temp tokens from the Cloudflare Worker, opens an AssemblyAI v3 websocket, streams PCM16 audio, tracks turn-based transcripts, and delivers finalized text on key-up. Shares a single URLSession across all sessions. |\n| `OpenAIAudioTranscriptionProvider.swift` | ~317 | Upload-based transcription provider. Buffers push-to-talk audio locally, uploads as WAV on release, returns finalized transcript. |\n| `AppleSpeechTranscriptionProvider.swift` | ~147 | Local fallback transcription provider backed by Apple's Speech framework. |\n| `BuddyAudioConversionSupport.swift` | ~108 | Audio conversion helpers. Converts live mic buffers to PCM16 mono audio and builds WAV payloads for upload-based providers. |\n| `GlobalPushToTalkShortcutMonitor.swift` | ~132 | System-wide push-to-talk monitor. Owns the listen-only `CGEvent` tap and publishes press/release transitions. |\n| `ClaudeAPI.swift` | ~291 | Claude vision API client with streaming (SSE) and non-streaming modes. TLS warmup optimization, image MIME detection, conversation history support. |\n| `OpenAIAPI.swift` | ~142 | OpenAI GPT vision API client. |\n| `ElevenLabsTTSClient.swift` | ~81 | ElevenLabs TTS client. Sends text to the Worker proxy, plays back audio via `AVAudioPlayer`. Exposes `isPlaying` for transient cursor scheduling. |\n| `ElementLocationDetector.swift` | ~335 | Detects UI element locations in screenshots for cursor pointing. |\n| `DesignSystem.swift` | ~880 | Design system tokens — colors, corner radii, shared styles. All UI references `DS.Colors`, `DS.CornerRadius`, etc. |\n| `ClickyAnalytics.swift` | ~121 | PostHog analytics integration for usage tracking. |\n| `WindowPositionManager.swift` | ~262 | Window placement logic, Screen Recording permission flow, and accessibility permission helpers. |\n| `AppBundleConfiguration.swift` | ~28 | Runtime configuration reader for keys stored in the app bundle Info.plist. |\n| `worker/src/index.ts` | ~142 | Cloudflare Worker proxy. Three routes: `/chat` (Claude), `/tts` (ElevenLabs), `/transcribe-token` (AssemblyAI temp token). |\n\n## Build & Run\n\n```bash\n# Open in Xcode\nopen leanring-buddy.xcodeproj\n\n# Select the leanring-buddy scheme, set signing team, Cmd+R to build and run\n\n# Known non-blocking warnings: Swift 6 concurrency warnings,\n# deprecated onChange warning in OverlayWindow.swift. Do NOT attempt to fix these.\n```\n\n**Do NOT run `xcodebuild` from the terminal** — it invalidates TCC (Transparency, Consent, and Control) permissions and the app will need to re-request screen recording, accessibility, etc.\n\n## Cloudflare Worker\n\n```bash\ncd worker\nnpm install\n\n# Add secrets\nnpx wrangler secret put ANTHROPIC_API_KEY\nnpx wrangler secret put ASSEMBLYAI_API_KEY\nnpx wrangler secret put ELEVENLABS_API_KEY\n\n# Deploy\nnpx wrangler deploy\n\n# Local dev (create worker/.dev.vars with your keys)\nnpx wrangler dev\n```\n\n## Code Style & Conventions\n\n### Variable and Method Naming\n\nIMPORTANT: Follow these naming rules strictly. Clarity is the top priority.\n\n- Be as clear and specific with variable and method names as possible\n- **Optimize for clarity over concision.** A developer with zero context on the codebase should immediately understand what a variable or method does just from reading its name\n- Use longer names when it improves clarity. Do NOT use single-character variable names\n- Example: use `originalQuestionLastAnsweredDate` instead of `originalAnswered`\n- When passing props or arguments to functions, keep the same names as the original variable. Do not shorten or abbreviate parameter names. If you have `currentCardData`, pass it as `currentCardData`, not `card` or `cardData`\n\n### Code Clarity\n\n- **Clear is better than clever.** Do not write functionality in fewer lines if it makes the code harder to understand\n- Write more lines of code if additional lines improve readability and comprehension\n- Make things so clear that someone with zero context would completely understand the variable names, method names, what things do, and why they exist\n- When a variable or method name alone cannot fully explain something, add a comment explaining what is happening and why\n\n### Swift/SwiftUI Conventions\n\n- Use SwiftUI for all UI unless a feature is only supported in AppKit (e.g., `NSPanel` for floating windows)\n- All UI state updates must be on `@MainActor`\n- Use async/await for all asynchronous operations\n- Comments should explain \"why\" not just \"what\", especially for non-obvious AppKit bridging\n- AppKit `NSPanel`/`NSWindow` bridged into SwiftUI via `NSHostingView`\n- All buttons must show a pointer cursor on hover\n- For any interactive element, explicitly think through its hover behavior (cursor, visual feedback, and whether hover should communicate clickability)\n\n### Do NOT\n\n- Do not add features, refactor code, or make \"improvements\" beyond what was asked\n- Do not add docstrings, comments, or type annotations to code you did not change\n- Do not try to fix the known non-blocking warnings (Swift 6 concurrency, deprecated onChange)\n- Do not rename the project directory or scheme (the \"leanring\" typo is intentional/legacy)\n- Do not run `xcodebuild` from the terminal — it invalidates TCC permissions\n\n## Git Workflow\n\n- Branch naming: `feature/description` or `fix/description`\n- Commit messages: imperative mood, concise, explain the \"why\" not the \"what\"\n- Do not force-push to main\n\n## Self-Update Instructions\n\n<!-- AI agents: follow these instructions to keep this file accurate. -->\n\nWhen you make changes to this project that affect the information in this file, update this file to reflect those changes. Specifically:\n\n1. **New files**: Add new source files to the \"Key Files\" table with their purpose and approximate line count\n2. **Deleted files**: Remove entries for files that no longer exist\n3. **Architecture changes**: Update the architecture section if you introduce new patterns, frameworks, or significant structural changes\n4. **Build changes**: Update build commands if the build process changes\n5. **New conventions**: If the user establishes a new coding convention during a session, add it to the appropriate conventions section\n6. **Line count drift**: If a file's line count changes significantly (>50 lines), update the approximate count in the Key Files table\n\nDo NOT update this file for minor edits, bug fixes, or changes that don't affect the documented architecture or conventions.\n"},"files":{"leanring-buddy/AGENTS.md":"# AGENTS.md - leanring-buddy (Main App Target)\n\n## Source Files\n\n### FloatingSessionButton.swift\n- `FloatingSessionButtonManager` — `@MainActor` class managing the `NSPanel` lifecycle\n  - `showFloatingButton()` — Creates/shows the panel in top-right of primary screen\n  - `hideFloatingButton()` — Hides panel (keeps it alive for quick re-show)\n  - `destroyFloatingButton()` — Removes panel permanently (session ended)\n  - `onFloatingButtonClicked` — Callback closure, set by ContentView to bring main window to front\n  - `floatingButtonPanel` — Exposed `NSPanel` reference for screenshot exclusion\n- `FloatingButtonView` — Private SwiftUI view with gradient circle, scale+glow hover animation, pointer cursor\n\n### ContentView.swift\n- Receives `FloatingSessionButtonManager` via `@EnvironmentObject`\n- `isMainWindowCurrentlyFocused` — Tracks main window focus state\n- `configureFloatingButtonManager()` — Wires up the click callback\n- `startObservingMainWindowFocusChanges()` — Sets up `NSWindow` notification observers\n- `updateFloatingButtonVisibility()` — Core logic: show if running + not focused, hide otherwise\n- `bringMainWindowToFront()` — Activates app and orders main window front\n\n### ScreenshotManager.swift\n- `floatingButtonWindowToExcludeFromCaptures` — `NSWindow?` reference set by ContentView\n- `captureScreen()` — Matches the floating window to an `SCWindow` and excludes it from capture filter\n\n### leanring_buddyApp.swift\n- Owns `FloatingSessionButtonManager` as `@StateObject`\n- Injects it into ContentView via `.environmentObject()`\n","AGENTS.md":"# Clicky - Agent Instructions\n\n<!-- This is the single source of truth for all AI coding agents. CLAUDE.md is a symlink to this file. -->\n<!-- AGENTS.md spec: https://github.com/agentsmd/agents.md — supported by Claude Code, Cursor, Copilot, Gemini CLI, and others. -->\n\n## Overview\n\nmacOS menu bar companion app. Lives entirely in the macOS status bar (no dock icon, no main window). Clicking the menu bar icon opens a custom floating panel with companion voice controls. Uses push-to-talk (ctrl+option) to capture voice input, transcribes it via AssemblyAI streaming, and sends the transcript + a screenshot of the user's screen to Claude. Claude responds with text (streamed via SSE) and voice (ElevenLabs TTS). A blue cursor overlay can fly to and point at UI elements Claude references on any connected monitor.\n\nAll API keys live on a Cloudflare Worker proxy — nothing sensitive ships in the app.\n\n## Architecture\n\n- **App Type**: Menu bar-only (`LSUIElement=true`), no dock icon or main window\n- **Framework**: SwiftUI (macOS native) with AppKit bridging for menu bar panel and cursor overlay\n- **Pattern**: MVVM with `@StateObject` / `@Published` state management\n- **AI Chat**: Claude (Sonnet 4.6 default, Opus 4.6 optional) via Cloudflare Worker proxy with SSE streaming\n- **Speech-to-Text**: AssemblyAI real-time streaming (`u3-rt-pro` model) via websocket, with OpenAI and Apple Speech as fallbacks\n- **Text-to-Speech**: ElevenLabs (`eleven_flash_v2_5` model) via Cloudflare Worker proxy\n- **Screen Capture**: ScreenCaptureKit (macOS 14.2+), multi-monitor support\n- **Voice Input**: Push-to-talk via `AVAudioEngine` + pluggable transcription-provider layer. System-wide keyboard shortcut via listen-only CGEvent tap.\n- **Element Pointing**: Claude embeds `[POINT:x,y:label:screenN]` tags in responses. The overlay parses these, maps coordinates to the correct monitor, and animates the blue cursor along a bezier arc to the target.\n- **Concurrency**: `@MainActor` isolation, async/await throughout\n- **Analytics**: PostHog via `ClickyAnalytics.swift`\n\n### API Proxy (Cloudflare Worker)\n\nThe app never calls external APIs directly. All requests go through a Cloudflare Worker (`worker/src/index.ts`) that holds the real API keys as secrets.\n\n| Route | Upstream | Purpose |\n|-------|----------|---------|\n| `POST /chat` | `api.anthropic.com/v1/messages` | Claude vision + streaming chat |\n| `POST /tts` | `api.elevenlabs.io/v1/text-to-speech/{voiceId}` | ElevenLabs TTS audio |\n| `POST /transcribe-token` | `streaming.assemblyai.com/v3/token` | Fetches a short-lived (480s) AssemblyAI websocket token |\n\nWorker secrets: `ANTHROPIC_API_KEY`, `ASSEMBLYAI_API_KEY`, `ELEVENLABS_API_KEY`\nWorker vars: `ELEVENLABS_VOICE_ID`\n\n### Key Architecture Decisions\n\n**Menu Bar Panel Pattern**: The companion panel uses `NSStatusItem` for the menu bar icon and a custom borderless `NSPanel` for the floating control panel. This gives full control over appearance (dark, rounded corners, custom shadow) and avoids the standard macOS menu/popover chrome. The panel is non-activating so it doesn't steal focus. A global event monitor auto-dismisses it on outside clicks.\n\n**Cursor Overlay**: A full-screen transparent `NSPanel` hosts the blue cursor companion. It's non-activating, joins all Spaces, and never steals focus. The cursor position, response text, waveform, and pointing animations all render in this overlay via SwiftUI through `NSHostingView`.\n\n**Global Push-To-Talk Shortcut**: Background push-to-talk uses a listen-only `CGEvent` tap instead of an AppKit global monitor so modifier-based shortcuts like `ctrl + option` are detected more reliably while the app is running in the background.\n\n**Shared URLSession for AssemblyAI**: A single long-lived `URLSession` is shared across all AssemblyAI streaming sessions (owned by the provider, not the session). Creating and invalidating a URLSession per session corrupts the OS connection pool and causes \"Socket is not connected\" errors after a few rapid reconnections.\n\n**Transient Cursor Mode**: When \"Show Clicky\" is off, pressing the hotkey fades in the cursor overlay for the duration of the interaction (recording → response → TTS → optional pointing), then fades it out automatically after 1 second of inactivity.\n\n## Key Files\n\n| File | Lines | Purpose |\n|------|-------|---------|\n| `leanring_buddyApp.swift` | ~89 | Menu bar app entry point. Uses `@NSApplicationDelegateAdaptor` with `CompanionAppDelegate` which creates `MenuBarPanelManager` and starts `CompanionManager`. No main window — the app lives entirely in the status bar. |\n| `CompanionManager.swift` | ~1026 | Central state machine. Owns dictation, shortcut monitoring, screen capture, Claude API, ElevenLabs TTS, and overlay management. Tracks voice state (idle/listening/processing/responding), conversation history, model selection, and cursor visibility. Coordinates the full push-to-talk → screenshot → Claude → TTS → pointing pipeline. |\n| `MenuBarPanelManager.swift` | ~243 | NSStatusItem + custom NSPanel lifecycle. Creates the menu bar icon, manages the floating companion panel (show/hide/position), installs click-outside-to-dismiss monitor. |\n| `CompanionPanelView.swift` | ~761 | SwiftUI panel content for the menu bar dropdown. Shows companion status, push-to-talk instructions, model picker (Sonnet/Opus), permissions UI, DM feedback button, and quit button. Dark aesthetic using `DS` design system. |\n| `OverlayWindow.swift` | ~881 | Full-screen transparent overlay hosting the blue cursor, response text, waveform, and spinner. Handles cursor animation, element pointing with bezier arcs, multi-monitor coordinate mapping, and fade-out transitions. |\n| `CompanionResponseOverlay.swift` | ~217 | SwiftUI view for the response text bubble and waveform displayed next to the cursor in the overlay. |\n| `CompanionScreenCaptureUtility.swift` | ~132 | Multi-monitor screenshot capture using ScreenCaptureKit. Returns labeled image data for each connected display. |\n| `BuddyDictationManager.swift` | ~866 | Push-to-talk voice pipeline. Handles microphone capture via `AVAudioEngine`, provider-aware permission checks, keyboard/button dictation sessions, transcript finalization, shortcut parsing, contextual keyterms, and live audio-level reporting for waveform feedback. |\n| `BuddyTranscriptionProvider.swift` | ~100 | Protocol surface and provider factory for voice transcription backends. Resolves provider based on `VoiceTranscriptionProvider` in Info.plist — AssemblyAI, OpenAI, or Apple Speech. |\n| `AssemblyAIStreamingTranscriptionProvider.swift` | ~478 | Streaming transcription provider. Fetches temp tokens from the Cloudflare Worker, opens an AssemblyAI v3 websocket, streams PCM16 audio, tracks turn-based transcripts, and delivers finalized text on key-up. Shares a single URLSession across all sessions. |\n| `OpenAIAudioTranscriptionProvider.swift` | ~317 | Upload-based transcription provider. Buffers push-to-talk audio locally, uploads as WAV on release, returns finalized transcript. |\n| `AppleSpeechTranscriptionProvider.swift` | ~147 | Local fallback transcription provider backed by Apple's Speech framework. |\n| `BuddyAudioConversionSupport.swift` | ~108 | Audio conversion helpers. Converts live mic buffers to PCM16 mono audio and builds WAV payloads for upload-based providers. |\n| `GlobalPushToTalkShortcutMonitor.swift` | ~132 | System-wide push-to-talk monitor. Owns the listen-only `CGEvent` tap and publishes press/release transitions. |\n| `ClaudeAPI.swift` | ~291 | Claude vision API client with streaming (SSE) and non-streaming modes. TLS warmup optimization, image MIME detection, conversation history support. |\n| `OpenAIAPI.swift` | ~142 | OpenAI GPT vision API client. |\n| `ElevenLabsTTSClient.swift` | ~81 | ElevenLabs TTS client. Sends text to the Worker proxy, plays back audio via `AVAudioPlayer`. Exposes `isPlaying` for transient cursor scheduling. |\n| `ElementLocationDetector.swift` | ~335 | Detects UI element locations in screenshots for cursor pointing. |\n| `DesignSystem.swift` | ~880 | Design system tokens — colors, corner radii, shared styles. All UI references `DS.Colors`, `DS.CornerRadius`, etc. |\n| `ClickyAnalytics.swift` | ~121 | PostHog analytics integration for usage tracking. |\n| `WindowPositionManager.swift` | ~262 | Window placement logic, Screen Recording permission flow, and accessibility permission helpers. |\n| `AppBundleConfiguration.swift` | ~28 | Runtime configuration reader for keys stored in the app bundle Info.plist. |\n| `worker/src/index.ts` | ~142 | Cloudflare Worker proxy. Three routes: `/chat` (Claude), `/tts` (ElevenLabs), `/transcribe-token` (AssemblyAI temp token). |\n\n## Build & Run\n\n```bash\n# Open in Xcode\nopen leanring-buddy.xcodeproj\n\n# Select the leanring-buddy scheme, set signing team, Cmd+R to build and run\n\n# Known non-blocking warnings: Swift 6 concurrency warnings,\n# deprecated onChange warning in OverlayWindow.swift. Do NOT attempt to fix these.\n```\n\n**Do NOT run `xcodebuild` from the terminal** — it invalidates TCC (Transparency, Consent, and Control) permissions and the app will need to re-request screen recording, accessibility, etc.\n\n## Cloudflare Worker\n\n```bash\ncd worker\nnpm install\n\n# Add secrets\nnpx wrangler secret put ANTHROPIC_API_KEY\nnpx wrangler secret put ASSEMBLYAI_API_KEY\nnpx wrangler secret put ELEVENLABS_API_KEY\n\n# Deploy\nnpx wrangler deploy\n\n# Local dev (create worker/.dev.vars with your keys)\nnpx wrangler dev\n```\n\n## Code Style & Conventions\n\n### Variable and Method Naming\n\nIMPORTANT: Follow these naming rules strictly. Clarity is the top priority.\n\n- Be as clear and specific with variable and method names as possible\n- **Optimize for clarity over concision.** A developer with zero context on the codebase should immediately understand what a variable or method does just from reading its name\n- Use longer names when it improves clarity. Do NOT use single-character variable names\n- Example: use `originalQuestionLastAnsweredDate` instead of `originalAnswered`\n- When passing props or arguments to functions, keep the same names as the original variable. Do not shorten or abbreviate parameter names. If you have `currentCardData`, pass it as `currentCardData`, not `card` or `cardData`\n\n### Code Clarity\n\n- **Clear is better than clever.** Do not write functionality in fewer lines if it makes the code harder to understand\n- Write more lines of code if additional lines improve readability and comprehension\n- Make things so clear that someone with zero context would completely understand the variable names, method names, what things do, and why they exist\n- When a variable or method name alone cannot fully explain something, add a comment explaining what is happening and why\n\n### Swift/SwiftUI Conventions\n\n- Use SwiftUI for all UI unless a feature is only supported in AppKit (e.g., `NSPanel` for floating windows)\n- All UI state updates must be on `@MainActor`\n- Use async/await for all asynchronous operations\n- Comments should explain \"why\" not just \"what\", especially for non-obvious AppKit bridging\n- AppKit `NSPanel`/`NSWindow` bridged into SwiftUI via `NSHostingView`\n- All buttons must show a pointer cursor on hover\n- For any interactive element, explicitly think through its hover behavior (cursor, visual feedback, and whether hover should communicate clickability)\n\n### Do NOT\n\n- Do not add features, refactor code, or make \"improvements\" beyond what was asked\n- Do not add docstrings, comments, or type annotations to code you did not change\n- Do not try to fix the known non-blocking warnings (Swift 6 concurrency, deprecated onChange)\n- Do not rename the project directory or scheme (the \"leanring\" typo is intentional/legacy)\n- Do not run `xcodebuild` from the terminal — it invalidates TCC permissions\n\n## Git Workflow\n\n- Branch naming: `feature/description` or `fix/description`\n- Commit messages: imperative mood, concise, explain the \"why\" not the \"what\"\n- Do not force-push to main\n\n## Self-Update Instructions\n\n<!-- AI agents: follow these instructions to keep this file accurate. -->\n\nWhen you make changes to this project that affect the information in this file, update this file to reflect those changes. Specifically:\n\n1. **New files**: Add new source files to the \"Key Files\" table with their purpose and approximate line count\n2. **Deleted files**: Remove entries for files that no longer exist\n3. **Architecture changes**: Update the architecture section if you introduce new patterns, frameworks, or significant structural changes\n4. **Build changes**: Update build commands if the build process changes\n5. **New conventions**: If the user establishes a new coding convention during a session, add it to the appropriate conventions section\n6. **Line count drift**: If a file's line count changes significantly (>50 lines), update the approximate count in the Key Files table\n\nDo NOT update this file for minor edits, bug fixes, or changes that don't affect the documented architecture or conventions.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/farzaa/clicky/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Clicky - Agent Instructions\n\n<!-- This is the single source of truth for all AI coding agents. CLAUDE.md is a symlink to this file. -->\n<!-- AGENTS.md spec: https://github.com/agentsmd/agents.md — supported by Claude Code, Cursor, Copilot, Gemini CLI, and others. -->\n\n## Overview\n\nmacOS menu bar companion app. Lives entirely in the macOS status bar (no dock icon, no main window). Clicking the menu bar icon opens a custom floating panel with companion voice controls. Uses push-to-talk (ctrl+option) to capture voice input, transcribes it via AssemblyAI streaming, and sends the transcript + a screenshot of the user's screen to Claude. Claude responds with text (streamed via SSE) and voice (ElevenLabs TTS). A blue cursor overlay can fly to and point at UI elements Claude references on any connected monitor.\n\nAll API keys live on a Cloudflare Worker proxy — nothing sensitive ships in the app.\n\n## Architecture\n\n- **App Type**: Menu bar-only (`LSUIElement=true`), no dock icon or main window\n- **Framework**: SwiftUI (macOS native) with AppKit bridging for menu bar panel and cursor overlay\n- **Pattern**: MVVM with `@StateObject` / `@Published` state management\n- **AI Chat**: Claude (Sonnet 4.6 default, Opus 4.6 optional) via Cloudflare Worker proxy with SSE streaming\n- **Speech-to-Text**: AssemblyAI real-time streaming (`u3-rt-pro` model) via websocket, with OpenAI and Apple Speech as fallbacks\n- **Text-to-Speech**: ElevenLabs (`eleven_flash_v2_5` model) via Cloudflare Worker proxy\n- **Screen Capture**: ScreenCaptureKit (macOS 14.2+), multi-monitor support\n- **Voice Input**: Push-to-talk via `AVAudioEngine` + pluggable transcription-provider layer. System-wide keyboard shortcut via listen-only CGEvent tap.\n- **Element Pointing**: Claude embeds `[POINT:x,y:label:screenN]` tags in responses. The overlay parses these, maps coordinates to the correct monitor, and animates the blue cursor along a bezier arc to the target.\n- **Concurrency**: `@MainActor` isolation, async/await throughout\n- **Analytics**: PostHog via `ClickyAnalytics.swift`\n\n### API Proxy (Cloudflare Worker)\n\nThe app never calls external APIs directly. All requests go through a Cloudflare Worker (`worker/src/index.ts`) that holds the real API keys as secrets.\n\n| Route | Upstream | Purpose |\n|-------|----------|---------|\n| `POST /chat` | `api.anthropic.com/v1/messages` | Claude vision + streaming chat |\n| `POST /tts` | `api.elevenlabs.io/v1/text-to-speech/{voiceId}` | ElevenLabs TTS audio |\n| `POST /transcribe-token` | `streaming.assemblyai.com/v3/token` | Fetches a short-lived (480s) AssemblyAI websocket token |\n\nWorker secrets: `ANTHROPIC_API_KEY`, `ASSEMBLYAI_API_KEY`, `ELEVENLABS_API_KEY`\nWorker vars: `ELEVENLABS_VOICE_ID`\n\n### Key Architecture Decisions\n\n**Menu Bar Panel Pattern**: The companion panel uses `NSStatusItem` for the menu bar icon and a custom borderless `NSPanel` for the floating control panel. This gives full control over appearance (dark, rounded corners, custom shadow) and avoids the standard macOS menu/popover chrome. The panel is non-activating so it doesn't steal focus. A global event monitor auto-dismisses it on outside clicks.\n\n**Cursor Overlay**: A full-screen transparent `NSPanel` hosts the blue cursor companion. It's non-activating, joins all Spaces, and never steals focus. The cursor position, response text, waveform, and pointing animations all render in this overlay via SwiftUI through `NSHostingView`.\n\n**Global Push-To-Talk Shortcut**: Background push-to-talk uses a listen-only `CGEvent` tap instead of an AppKit global monitor so modifier-based shortcuts like `ctrl + option` are detected more reliably while the app is running in the background.\n\n**Shared URLSession for AssemblyAI**: A single long-lived `URLSession` is shared across all AssemblyAI streaming sessions (owned by the provider, not the session). Creating and invalidating a URLSession per session corrupts the OS connection pool and causes \"Socket is not connected\" errors after a few rapid reconnections.\n\n**Transient Cursor Mode**: When \"Show Clicky\" is off, pressing the hotkey fades in the cursor overlay for the duration of the interaction (recording → response → TTS → optional pointing), then fades it out automatically after 1 second of inactivity.\n\n## Key Files\n\n| File | Lines | Purpose |\n|------|-------|---------|\n| `leanring_buddyApp.swift` | ~89 | Menu bar app entry point. Uses `@NSApplicationDelegateAdaptor` with `CompanionAppDelegate` which creates `MenuBarPanelManager` and starts `CompanionManager`. No main window — the app lives entirely in the status bar. |\n| `CompanionManager.swift` | ~1026 | Central state machine. Owns dictation, shortcut monitoring, screen capture, Claude API, ElevenLabs TTS, and overlay management. Tracks voice state (idle/listening/processing/responding), conversation history, model selection, and cursor visibility. Coordinates the full push-to-talk → screenshot → Claude → TTS → pointing pipeline. |\n| `MenuBarPanelManager.swift` | ~243 | NSStatusItem + custom NSPanel lifecycle. Creates the menu bar icon, manages the floating companion panel (show/hide/position), installs click-outside-to-dismiss monitor. |\n| `CompanionPanelView.swift` | ~761 | SwiftUI panel content for the menu bar dropdown. Shows companion status, push-to-talk instructions, model picker (Sonnet/Opus), permissions UI, DM feedback button, and quit button. Dark aesthetic using `DS` design system. |\n| `OverlayWindow.swift` | ~881 | Full-screen transparent overlay hosting the blue cursor, response text, waveform, and spinner. Handles cursor animation, element pointing with bezier arcs, multi-monitor coordinate mapping, and fade-out transitions. |\n| `CompanionResponseOverlay.swift` | ~217 | SwiftUI view for the response text bubble and waveform displayed next to the cursor in the overlay. |\n| `CompanionScreenCaptureUtility.swift` | ~132 | Multi-monitor screenshot capture using ScreenCaptureKit. Returns labeled image data for each connected display. |\n| `BuddyDictationManager.swift` | ~866 | Push-to-talk voice pipeline. Handles microphone capture via `AVAudioEngine`, provider-aware permission checks, keyboard/button dictation sessions, transcript finalization, shortcut parsing, contextual keyterms, and live audio-level reporting for waveform feedback. |\n| `BuddyTranscriptionProvider.swift` | ~100 | Protocol surface and provider factory for voice transcription backends. Resolves provider based on `VoiceTranscriptionProvider` in Info.plist — AssemblyAI, OpenAI, or Apple Speech. |\n| `AssemblyAIStreamingTranscriptionProvider.swift` | ~478 | Streaming transcription provider. Fetches temp tokens from the Cloudflare Worker, opens an AssemblyAI v3 websocket, streams PCM16 audio, tracks turn-based transcripts, and delivers finalized text on key-up. Shares a single URLSession across all sessions. |\n| `OpenAIAudioTranscriptionProvider.swift` | ~317 | Upload-based transcription provider. Buffers push-to-talk audio locally, uploads as WAV on release, returns finalized transcript. |\n| `AppleSpeechTranscriptionProvider.swift` | ~147 | Local fallback transcription provider backed by Apple's Speech framework. |\n| `BuddyAudioConversionSupport.swift` | ~108 | Audio conversion helpers. Converts live mic buffers to PCM16 mono audio and builds WAV payloads for upload-based providers. |\n| `GlobalPushToTalkShortcutMonitor.swift` | ~132 | System-wide push-to-talk monitor. Owns the listen-only `CGEvent` tap and publishes press/release transitions. |\n| `ClaudeAPI.swift` | ~291 | Claude vision API client with streaming (SSE) and non-streaming modes. TLS warmup optimization, image MIME detection, conversation history support. |\n| `OpenAIAPI.swift` | ~142 | OpenAI GPT vision API client. |\n| `ElevenLabsTTSClient.swift` | ~81 | ElevenLabs TTS client. Sends text to the Worker proxy, plays back audio via `AVAudioPlayer`. Exposes `isPlaying` for transient cursor scheduling. |\n| `ElementLocationDetector.swift` | ~335 | Detects UI element locations in screenshots for cursor pointing. |\n| `DesignSystem.swift` | ~880 | Design system tokens — colors, corner radii, shared styles. All UI references `DS.Colors`, `DS.CornerRadius`, etc. |\n| `ClickyAnalytics.swift` | ~121 | PostHog analytics integration for usage tracking. |\n| `WindowPositionManager.swift` | ~262 | Window placement logic, Screen Recording permission flow, and accessibility permission helpers. |\n| `AppBundleConfiguration.swift` | ~28 | Runtime configuration reader for keys stored in the app bundle Info.plist. |\n| `worker/src/index.ts` | ~142 | Cloudflare Worker proxy. Three routes: `/chat` (Claude), `/tts` (ElevenLabs), `/transcribe-token` (AssemblyAI temp token). |\n\n## Build & Run\n\n```bash\n# Open in Xcode\nopen leanring-buddy.xcodeproj\n\n# Select the leanring-buddy scheme, set signing team, Cmd+R to build and run\n\n# Known non-blocking warnings: Swift 6 concurrency warnings,\n# deprecated onChange warning in OverlayWindow.swift. Do NOT attempt to fix these.\n```\n\n**Do NOT run `xcodebuild` from the terminal** — it invalidates TCC (Transparency, Consent, and Control) permissions and the app will need to re-request screen recording, accessibility, etc.\n\n## Cloudflare Worker\n\n```bash\ncd worker\nnpm install\n\n# Add secrets\nnpx wrangler secret put ANTHROPIC_API_KEY\nnpx wrangler secret put ASSEMBLYAI_API_KEY\nnpx wrangler secret put ELEVENLABS_API_KEY\n\n# Deploy\nnpx wrangler deploy\n\n# Local dev (create worker/.dev.vars with your keys)\nnpx wrangler dev\n```\n\n## Code Style & Conventions\n\n### Variable and Method Naming\n\nIMPORTANT: Follow these naming rules strictly. Clarity is the top priority.\n\n- Be as clear and specific with variable and method names as possible\n- **Optimize for clarity over concision.** A developer with zero context on the codebase should immediately understand what a variable or method does just from reading its name\n- Use longer names when it improves clarity. Do NOT use single-character variable names\n- Example: use `originalQuestionLastAnsweredDate` instead of `originalAnswered`\n- When passing props or arguments to functions, keep the same names as the original variable. Do not shorten or abbreviate parameter names. If you have `currentCardData`, pass it as `currentCardData`, not `card` or `cardData`\n\n### Code Clarity\n\n- **Clear is better than clever.** Do not write functionality in fewer lines if it makes the code harder to understand\n- Write more lines of code if additional lines improve readability and comprehension\n- Make things so clear that someone with zero context would completely understand the variable names, method names, what things do, and why they exist\n- When a variable or method name alone cannot fully explain something, add a comment explaining what is happening and why\n\n### Swift/SwiftUI Conventions\n\n- Use SwiftUI for all UI unless a feature is only supported in AppKit (e.g., `NSPanel` for floating windows)\n- All UI state updates must be on `@MainActor`\n- Use async/await for all asynchronous operations\n- Comments should explain \"why\" not just \"what\", especially for non-obvious AppKit bridging\n- AppKit `NSPanel`/`NSWindow` bridged into SwiftUI via `NSHostingView`\n- All buttons must show a pointer cursor on hover\n- For any interactive element, explicitly think through its hover behavior (cursor, visual feedback, and whether hover should communicate clickability)\n\n### Do NOT\n\n- Do not add features, refactor code, or make \"improvements\" beyond what was asked\n- Do not add docstrings, comments, or type annotations to code you did not change\n- Do not try to fix the known non-blocking warnings (Swift 6 concurrency, deprecated onChange)\n- Do not rename the project directory or scheme (the \"leanring\" typo is intentional/legacy)\n- Do not run `xcodebuild` from the terminal — it invalidates TCC permissions\n\n## Git Workflow\n\n- Branch naming: `feature/description` or `fix/description`\n- Commit messages: imperative mood, concise, explain the \"why\" not the \"what\"\n- Do not force-push to main\n\n## Self-Update Instructions\n\n<!-- AI agents: follow these instructions to keep this file accurate. -->\n\nWhen you make changes to this project that affect the information in this file, update this file to reflect those changes. Specifically:\n\n1. **New files**: Add new source files to the \"Key Files\" table with their purpose and approximate line count\n2. **Deleted files**: Remove entries for files that no longer exist\n3. **Architecture changes**: Update the architecture section if you introduce new patterns, frameworks, or significant structural changes\n4. **Build changes**: Update build commands if the build process changes\n5. **New conventions**: If the user establishes a new coding convention during a session, add it to the appropriate conventions section\n6. **Line count drift**: If a file's line count changes significantly (>50 lines), update the approximate count in the Key Files table\n\nDo NOT update this file for minor edits, bug fixes, or changes that don't affect the documented architecture or conventions.\n","isInternal":false,"tokens":2945,"sizeBytes":13043},{"name":"AGENTS.md","path":"leanring-buddy/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/farzaa/clicky/HEAD/leanring-buddy/AGENTS.md","title":"leanring-buddy - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md - leanring-buddy (Main App Target)\n\n## Source Files\n\n### FloatingSessionButton.swift\n- `FloatingSessionButtonManager` — `@MainActor` class managing the `NSPanel` lifecycle\n  - `showFloatingButton()` — Creates/shows the panel in top-right of primary screen\n  - `hideFloatingButton()` — Hides panel (keeps it alive for quick re-show)\n  - `destroyFloatingButton()` — Removes panel permanently (session ended)\n  - `onFloatingButtonClicked` — Callback closure, set by ContentView to bring main window to front\n  - `floatingButtonPanel` — Exposed `NSPanel` reference for screenshot exclusion\n- `FloatingButtonView` — Private SwiftUI view with gradient circle, scale+glow hover animation, pointer cursor\n\n### ContentView.swift\n- Receives `FloatingSessionButtonManager` via `@EnvironmentObject`\n- `isMainWindowCurrentlyFocused` — Tracks main window focus state\n- `configureFloatingButtonManager()` — Wires up the click callback\n- `startObservingMainWindowFocusChanges()` — Sets up `NSWindow` notification observers\n- `updateFloatingButtonVisibility()` — Core logic: show if running + not focused, hide otherwise\n- `bringMainWindowToFront()` — Activates app and orders main window front\n\n### ScreenshotManager.swift\n- `floatingButtonWindowToExcludeFromCaptures` — `NSWindow?` reference set by ContentView\n- `captureScreen()` — Matches the floating window to an `SCWindow` and excludes it from capture filter\n\n### leanring_buddyApp.swift\n- Owns `FloatingSessionButtonManager` as `@StateObject`\n- Injects it into ContentView via `.environmentObject()`\n","isInternal":false,"tokens":357,"sizeBytes":1581}],"systemPromptSnippet":"<yakaai_skills repo=\"farzaa/clicky\">\n<!-- File: AGENTS.md (Tokens: ~2945 | Category: root-instruction) -->\n# Clicky - Agent Instructions\n\n<!-- This is the single source of truth for all AI coding agents. CLAUDE.md is a symlink to this file. -->\n<!-- AGENTS.md spec: https://github.com/agentsmd/agents.md — supported by Claude Code, Cursor, Copilot, Gemini CLI, and others. -->\n\n## Overview\n\nmacOS menu bar companion app. Lives entirely in the macOS status bar (no dock icon, no main window). Clicking the menu bar icon opens a custom floating panel with companion voice controls. Uses push-to-talk (ctrl+option) to capture voice input, transcribes it via AssemblyAI streaming, and sends the transcript + a screenshot of the user's screen to Claude. Claude responds with text (streamed via SSE) and voice (ElevenLabs TTS). A blue cursor overlay can fly to and point at UI elements Claude references on any connected monitor.\n\nAll API keys live on a Cloudflare Worker proxy — nothing sensitive ships in the app.\n\n## Architecture\n\n- **App Type**: Menu bar-only (`LSUIElement=true`), no dock icon or main window\n- **Framework**: SwiftUI (macOS native) with AppKit bridging for menu bar panel and cursor overlay\n- **Pattern**: MVVM with `@StateObject` / `@Published` state management\n- **AI Chat**: Claude (Sonnet 4.6 default, Opus 4.6 optional) via Cloudflare Worker proxy with SSE streaming\n- **Speech-to-Text**: AssemblyAI real-time streaming (`u3-rt-pro` model) via websocket, with OpenAI and Apple Speech as fallbacks\n- **Text-to-Speech**: ElevenLabs (`eleven_flash_v2_5` model) via Cloudflare Worker proxy\n- **Screen Capture**: ScreenCaptureKit (macOS 14.2+), multi-monitor support\n- **Voice Input**: Push-to-talk via `AVAudioEngine` + pluggable transcription-provider layer. System-wide keyboard shortcut via listen-only CGEvent tap.\n- **Element Pointing**: Claude embeds `[POINT:x,y:label:screenN]` tags in responses. The overlay parses these, maps coordinates to the correct monitor, and animates the blue cursor along a bezier arc to the target.\n- **Concurrency**: `@MainActor` isolation, async/await throughout\n- **Analytics**: PostHog via `ClickyAnalytics.swift`\n\n### API Proxy (Cloudflare Worker)\n\nThe app never calls external APIs directly. All requests go through a Cloudflare Worker (`worker/src/index.ts`) that holds the real API keys as secrets.\n\n| Route | Upstream | Purpose |\n|-------|----------|---------|\n| `POST /chat` | `api.anthropic.com/v1/messages` | Claude vision + streaming chat |\n| `POST /tts` | `api.elevenlabs.io/v1/text-to-speech/{voiceId}` | ElevenLabs TTS audio |\n| `POST /transcribe-token` | `streaming.assemblyai.com/v3/token` | Fetches a short-lived (480s) AssemblyAI websocket token |\n\nWorker secrets: `ANTHROPIC_API_KEY`, `ASSEMBLYAI_API_KEY`, `ELEVENLABS_API_KEY`\nWorker vars: `ELEVENLABS_VOICE_ID`\n\n### Key Architecture Decisions\n\n**Menu Bar Panel Pattern**: The companion panel uses `NSStatusItem` for the menu bar icon and a custom borderless `NSPanel` for the floating control panel. This gives full control over appearance (dark, rounded corners, custom shadow) and avoids the standard macOS menu/popover chrome. The panel is non-activating so it doesn't steal focus. A global event monitor auto-dismisses it on outside clicks.\n\n**Cursor Overlay**: A full-screen transparent `NSPanel` hosts the blue cursor companion. It's non-activating, joins all Spaces, and never steals focus. The cursor position, response text, waveform, and pointing animations all render in this overlay via SwiftUI through `NSHostingView`.\n\n**Global Push-To-Talk Shortcut**: Background push-to-talk uses a listen-only `CGEvent` tap instead of an AppKit global monitor so modifier-based shortcuts like `ctrl + option` are detected more reliably while the app is running in the background.\n\n**Shared URLSession for AssemblyAI**: A single long-lived `URLSession` is shared across all AssemblyAI streaming sessions (owned by the provider, not the session). Creating and invalidating a URLSession per session corrupts the OS connection pool and causes \"Socket is not connected\" errors after a few rapid reconnections.\n\n**Transient Cursor Mode**: When \"Show Clicky\" is off, pressing the hotkey fades in the cursor overlay for the duration of the interaction (recording → response → TTS → optional pointing), then fades it out automatically after 1 second of inactivity.\n\n## Key Files\n\n| File | Lines | Purpose |\n|------|-------|---------|\n| `leanring_buddyApp.swift` | ~89 | Menu bar app entry point. Uses `@NSApplicationDelegateAdaptor` with `CompanionAppDelegate` which creates `MenuBarPanelManager` and starts `CompanionManager`. No main window — the app lives entirely in the status bar. |\n| `CompanionManager.swift` | ~1026 | Central state machine. Owns dictation, shortcut monitoring, screen capture, Claude API, ElevenLabs TTS, and overlay management. Tracks voice state (idle/listening/processing/responding), conversation history, model selection, and cursor visibility. Coordinates the full push-to-talk → screenshot → Claude → TTS → pointing pipeline. |\n| `MenuBarPanelManager.swift` | ~243 | NSStatusItem + custom NSPanel lifecycle. Creates the menu bar icon, manages the floating companion panel (show/hide/position), installs click-outside-to-dismiss monitor. |\n| `CompanionPanelView.swift` | ~761 | SwiftUI panel content for the menu bar dropdown. Shows companion status, push-to-talk instructions, model picker (Sonnet/Opus), permissions UI, DM feedback button, and quit button. Dark aesthetic using `DS` design system. |\n| `OverlayWindow.swift` | ~881 | Full-screen transparent overlay hosting the blue cursor, response text, waveform, and spinner. Handles cursor animation, element pointing with bezier arcs, multi-monitor coordinate mapping, and fade-out transitions. |\n| `CompanionResponseOverlay.swift` | ~217 | SwiftUI view for the response text bubble and waveform displayed next to the cursor in the overlay. |\n| `CompanionScreenCaptureUtility.swift` | ~132 | Multi-monitor screenshot capture using ScreenCaptureKit. Returns labeled image data for each connected display. |\n| `BuddyDictationManager.swift` | ~866 | Push-to-talk voice pipeline. Handles microphone capture via `AVAudioEngine`, provider-aware permission checks, keyboard/button dictation sessions, transcript finalization, shortcut parsing, contextual keyterms, and live audio-level reporting for waveform feedback. |\n| `BuddyTranscriptionProvider.swift` | ~100 | Protocol surface and provider factory for voice transcription backends. Resolves provider based on `VoiceTranscriptionProvider` in Info.plist — AssemblyAI, OpenAI, or Apple Speech. |\n| `AssemblyAIStreamingTranscriptionProvider.swift` | ~478 | Streaming transcription provider. Fetches temp tokens from the Cloudflare Worker, opens an AssemblyAI v3 websocket, streams PCM16 audio, tracks turn-based transcripts, and delivers finalized text on key-up. Shares a single URLSession across all sessions. |\n| `OpenAIAudioTranscriptionProvider.swift` | ~317 | Upload-based transcription provider. Buffers push-to-talk audio locally, uploads as WAV on release, returns finalized transcript. |\n| `AppleSpeechTranscriptionProvider.swift` | ~147 | Local fallback transcription provider backed by Apple's Speech framework. |\n| `BuddyAudioConversionSupport.swift` | ~108 | Audio conversion helpers. Converts live mic buffers to PCM16 mono audio and builds WAV payloads for upload-based providers. |\n| `GlobalPushToTalkShortcutMonitor.swift` | ~132 | System-wide push-to-talk monitor. Owns the listen-only `CGEvent` tap and publishes press/release transitions. |\n| `ClaudeAPI.swift` | ~291 | Claude vision API client with streaming (SSE) and non-streaming modes. TLS warmup optimization, image MIME detection, conversation history support. |\n| `OpenAIAPI.swift` | ~142 | OpenAI GPT vision API client. |\n| `ElevenLabsTTSClient.swift` | ~81 | ElevenLabs TTS client. Sends text to the Worker proxy, plays back audio via `AVAudioPlayer`. Exposes `isPlaying` for transient cursor scheduling. |\n| `ElementLocationDetector.swift` | ~335 | Detects UI element locations in screenshots for cursor pointing. |\n| `DesignSystem.swift` | ~880 | Design system tokens — colors, corner radii, shared styles. All UI references `DS.Colors`, `DS.CornerRadius`, etc. |\n| `ClickyAnalytics.swift` | ~121 | PostHog analytics integration for usage tracking. |\n| `WindowPositionManager.swift` | ~262 | Window placement logic, Screen Recording permission flow, and accessibility permission helpers. |\n| `AppBundleConfiguration.swift` | ~28 | Runtime configuration reader for keys stored in the app bundle Info.plist. |\n| `worker/src/index.ts` | ~142 | Cloudflare Worker proxy. Three routes: `/chat` (Claude), `/tts` (ElevenLabs), `/transcribe-token` (AssemblyAI temp token). |\n\n## Build & Run\n\n```bash\n# Open in Xcode\nopen leanring-buddy.xcodeproj\n\n# Select the leanring-buddy scheme, set signing team, Cmd+R to build and run\n\n# Known non-blocking warnings: Swift 6 concurrency warnings,\n# deprecated onChange warning in OverlayWindow.swift. Do NOT attempt to fix these.\n```\n\n**Do NOT run `xcodebuild` from the terminal** — it invalidates TCC (Transparency, Consent, and Control) permissions and the app will need to re-request screen recording, accessibility, etc.\n\n## Cloudflare Worker\n\n```bash\ncd worker\nnpm install\n\n# Add secrets\nnpx wrangler secret put ANTHROPIC_API_KEY\nnpx wrangler secret put ASSEMBLYAI_API_KEY\nnpx wrangler secret put ELEVENLABS_API_KEY\n\n# Deploy\nnpx wrangler deploy\n\n# Local dev (create worker/.dev.vars with your keys)\nnpx wrangler dev\n```\n\n## Code Style & Conventions\n\n### Variable and Method Naming\n\nIMPORTANT: Follow these naming rules strictly. Clarity is the top priority.\n\n- Be as clear and specific with variable and method names as possible\n- **Optimize for clarity over concision.** A developer with zero context on the codebase should immediately understand what a variable or method does just from reading its name\n- Use longer names when it improves clarity. Do NOT use single-character variable names\n- Example: use `originalQuestionLastAnsweredDate` instead of `originalAnswered`\n- When passing props or arguments to functions, keep the same names as the original variable. Do not shorten or abbreviate parameter names. If you have `currentCardData`, pass it as `currentCardData`, not `card` or `cardData`\n\n### Code Clarity\n\n- **Clear is better than clever.** Do not write functionality in fewer lines if it makes the code harder to understand\n- Write more lines of code if additional lines improve readability and comprehension\n- Make things so clear that someone with zero context would completely understand the variable names, method names, what things do, and why they exist\n- When a variable or method name alone cannot fully explain something, add a comment explaining what is happening and why\n\n### Swift/SwiftUI Conventions\n\n- Use SwiftUI for all UI unless a feature is only supported in AppKit (e.g., `NSPanel` for floating windows)\n- All UI state updates must be on `@MainActor`\n- Use async/await for all asynchronous operations\n- Comments should explain \"why\" not just \"what\", especially for non-obvious AppKit bridging\n- AppKit `NSPanel`/`NSWindow` bridged into SwiftUI via `NSHostingView`\n- All buttons must show a pointer cursor on hover\n- For any interactive element, explicitly think through its hover behavior (cursor, visual feedback, and whether hover should communicate clickability)\n\n### Do NOT\n\n- Do not add features, refactor code, or make \"improvements\" beyond what was asked\n- Do not add docstrings, comments, or type annotations to code you did not change\n- Do not try to fix the known non-blocking warnings (Swift 6 concurrency, deprecated onChange)\n- Do not rename the project directory or scheme (the \"leanring\" typo is intentional/legacy)\n- Do not run `xcodebuild` from the terminal — it invalidates TCC permissions\n\n## Git Workflow\n\n- Branch naming: `feature/description` or `fix/description`\n- Commit messages: imperative mood, concise, explain the \"why\" not the \"what\"\n- Do not force-push to main\n\n## Self-Update Instructions\n\n<!-- AI agents: follow these instructions to keep this file accurate. -->\n\nWhen you make changes to this project that affect the information in this file, update this file to reflect those changes. Specifically:\n\n1. **New files**: Add new source files to the \"Key Files\" table with their purpose and approximate line count\n2. **Deleted files**: Remove entries for files that no longer exist\n3. **Architecture changes**: Update the architecture section if you introduce new patterns, frameworks, or significant structural changes\n4. **Build changes**: Update build commands if the build process changes\n5. **New conventions**: If the user establishes a new coding convention during a session, add it to the appropriate conventions section\n6. **Line count drift**: If a file's line count changes significantly (>50 lines), update the approximate count in the Key Files table\n\nDo NOT update this file for minor edits, bug fixes, or changes that don't affect the documented architecture or conventions.\n\n<!-- File: leanring-buddy/AGENTS.md (Tokens: ~357 | Category: root-instruction) -->\n# AGENTS.md - leanring-buddy (Main App Target)\n\n## Source Files\n\n### FloatingSessionButton.swift\n- `FloatingSessionButtonManager` — `@MainActor` class managing the `NSPanel` lifecycle\n  - `showFloatingButton()` — Creates/shows the panel in top-right of primary screen\n  - `hideFloatingButton()` — Hides panel (keeps it alive for quick re-show)\n  - `destroyFloatingButton()` — Removes panel permanently (session ended)\n  - `onFloatingButtonClicked` — Callback closure, set by ContentView to bring main window to front\n  - `floatingButtonPanel` — Exposed `NSPanel` reference for screenshot exclusion\n- `FloatingButtonView` — Private SwiftUI view with gradient circle, scale+glow hover animation, pointer cursor\n\n### ContentView.swift\n- Receives `FloatingSessionButtonManager` via `@EnvironmentObject`\n- `isMainWindowCurrentlyFocused` — Tracks main window focus state\n- `configureFloatingButtonManager()` — Wires up the click callback\n- `startObservingMainWindowFocusChanges()` — Sets up `NSWindow` notification observers\n- `updateFloatingButtonVisibility()` — Core logic: show if running + not focused, hide otherwise\n- `bringMainWindowToFront()` — Activates app and orders main window front\n\n### ScreenshotManager.swift\n- `floatingButtonWindowToExcludeFromCaptures` — `NSWindow?` reference set by ContentView\n- `captureScreen()` — Matches the floating window to an `SCWindow` and excludes it from capture filter\n\n### leanring_buddyApp.swift\n- Owns `FloatingSessionButtonManager` as `@StateObject`\n- Injects it into ContentView via `.environmentObject()`\n</yakaai_skills>"}