FocusPal
A playful desktop companion for Claude Code on macOS. A pixel‑art character (Ninja Frog by default) lives in your menu bar and occasionally hops onto the screen to let you know when your Claude Code sessions finish, need your input, or when it's time to take a break.
"Hey! solana-integration has been done for a while. Chrome isn't going anywhere, but your deadline is."

Why
If you run multiple Claude Code sessions in parallel, it's easy to wander off into Twitter/YouTube/Slack while agents finish their work in the background. FocusPal watches every active Claude Code session and a tiny character gently nags you back — funny, non‑disruptive, and context‑aware.
Features
- 🐸 Menu‑bar command center — see every active Claude Code session with its repo name and how long it's been running.
- ⚡ Reminders when tasks finish — the frog walks into the middle of your screen and tells you, only if you've drifted away from the terminal.
- ⏸ "Waiting for you" detection — when Claude Code pauses for a permission prompt (
Notification hook), the frog appears sooner (2 min) with a different message.
- 😴 Snooze options — click the frog: dismiss, 10 minutes, 1 hour, or tomorrow. Snoozes are per‑session, so other repos keep reminding you.
- 🧘 Hourly health nudges — every 2 hours the frog does a quick "stand up, drink water, look away from the screen" pop — only when you're actually working.
- 🪟 Window aware — the frog appears near the top‑right of whichever window is focused, on whichever monitor it lives on.
- ⚙️ Config‑driven — tune delays, messages, sites that count as distractions, and more in
config.json without rebuilding.
Preview
| Character | Sprite |
|---|
| Ninja Frog (default) |  |
| Mask Dude |  |
| Pink Man |  |
| Virtual Guy |  |
Sprites from the excellent Pixel Adventure 1 pack by Pixel Frog (free for commercial use).
Architecture
┌──────────────────────┐ ┌───────────────────────────┐
│ ~/.claude/hooks │ │ ~/.claude/sessions/*.json │
│ Stop / Notification │ │ (auto‑discovered PIDs) │
│ UserPromptSubmit │ └───────────────────────────┘
└──────────┬───────────┘ │
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ ClaudeCodeMonitor │ │ SessionTracker │
│ (events.jsonl watcher) │ │ (PID liveness poll) │
└──────────┬─────────────┘ └──────────┬─────────────┘
│ │
▼ ▼
┌─────────────────────────────────────┐
│ ReminderManager │
│ (per‑session delays, snooze) │
└──────────────────┬──────────────────┘
│
▼
┌─────────────────────────────────────┐
│ CharacterStateMachine (Ninja Frog) │
│ idle → alert → run → talk → hide │
│ └→ popAndSay (health) │
└─────────────────────────────────────┘
Key modules
| File | Role |
|---|
main.swift / AppDelegate.swift | NSApplication bootstrap, wiring, menu bar. |
SessionTracker.swift | Polls ~/.claude/sessions/*.json, verifies each PID with kill(pid, 0). |
ClaudeCodeMonitor.swift | Tails ~/.claude/focuspal/events.jsonl for Stop / Notification / UserPromptSubmit hooks. |
HookInstaller.swift | On first launch, adds the required hook lines to ~/.claude/settings.json (non‑destructive — preserves existing hooks). |
ReminderManager.swift | Per‑session reminder queue, snooze logic, upgrade from taskComplete → awaitingInput. |
HealthReminder.swift | Hourly break nudge, skipped when the user is idle / AFK. |
WindowTracker.swift | Finds the focused window via CGWindowListCopyWindowInfo (no Accessibility permissions needed). |
CharacterStateMachine.swift + CharacterView.swift + SpriteAnimator.swift | Idle / run / talk / appear / disappear sprite animations. |
SpeechBubbleWindow.swift / ActionBubblesWindow.swift | The speech bubble and the 4 snooze buttons that flank the frog when you click it. |
Requirements
- macOS 13+ (Ventura or later)
- Swift 5.9+ (comes with Xcode Command Line Tools — full Xcode is not required)
- Claude Code installed and at least one session open
Check with:
swift --version # expect: 5.9+
Getting started