---
phase: 09-swiftbar-integration
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - ~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py
autonomous: true
requirements: [BAR-01, BAR-02, BAR-03, BAR-04, BAR-05, EXT-03]

must_haves:
  truths:
    - "SwiftBar menu icon reflects unified service health (green=running, red=stopped)"
    - "Menu shows per-subsystem status (bot, tts, subtitle) from /health endpoint"
    - "Menu shows subtitle controls: font size S/M/L, position top/bottom, karaoke on/off"
    - "Menu shows TTS controls: enable/disable, test TTS button"
    - "Menu shows external monitor switching option"
    - "All menu actions reference HTTP API at localhost:8780 (not TOML or launchctl)"
  artifacts:
    - path: "~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"
      provides: "SwiftBar v3.0.0 plugin with HTTP API integration"
      contains: "swiftbar.version>3.0.0"
  key_links:
    - from: "claude-hq.10s.py"
      to: "localhost:8780/health"
      via: "urllib.request GET"
      pattern: "localhost:8780/health"
    - from: "claude-hq.10s.py"
      to: "localhost:8780/settings"
      via: "urllib.request GET"
      pattern: "localhost:8780/settings"
---

<objective>
Rewrite the SwiftBar plugin claude-hq.10s.py from v2.0.0 to v3.0.0. The new version monitors the single unified service (com.terryli.claude-tts-companion) via its HTTP control API at localhost:8780 instead of monitoring two separate launchd services via TOML config.

Purpose: Provide a unified control surface in the macOS menu bar for subtitle, TTS, and health monitoring of the companion binary.
Output: Updated claude-hq.10s.py with HTTP API integration, subtitle controls, TTS controls, health display, and external monitor switching.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/09-swiftbar-integration/09-CONTEXT.md
@.planning/phases/08-http-control-api/08-01-SUMMARY.md

<interfaces>
<!-- HTTP API contracts from Phase 8 that the SwiftBar plugin must call -->

GET /health response:

```json
{
  "status": "ok",
  "uptime_seconds": 1234,
  "rss_mb": 27.5,
  "subsystems": {
    "bot": "unknown",
    "tts": "ready",
    "subtitle": "ready"
  }
}
```

GET /settings response:

```json
{
  "subtitle": {
    "fontSize": "medium",
    "position": "bottom",
    "opacity": 0.3,
    "karaokeEnabled": true,
    "screen": "builtin"
  },
  "tts": {
    "enabled": true,
    "voice": "af_heart",
    "speed": 1.0
  }
}
```

POST /settings/subtitle — accepts SubtitleSettingsUpdate (all optional):

```json
{
  "fontSize": "large",
  "position": "top",
  "opacity": 0.3,
  "karaokeEnabled": false,
  "screen": "external"
}
```

POST /settings/tts — accepts TTSSettingsUpdate (all optional):

```json
{ "enabled": false, "voice": "af_heart", "speed": 1.3 }
```

POST /subtitle/show — accepts: {"text": "...", "duration": 3.0}
POST /subtitle/hide — no body needed

Config.httpPort = 8780
Config.serviceLabel = "com.terryli.claude-tts-companion"
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Rewrite claude-hq.10s.py v3.0.0 with HTTP API integration</name>
  <files>~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py</files>
  <read_first>
    - ~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py (current v2.0.0 for structure reference)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/HTTPControlServer.swift (API endpoints)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/SettingsStore.swift (settings structure)
  </read_first>
  <action>
Rewrite claude-hq.10s.py as v3.0.0. Keep the same shebang line pointing to mise python 3.13. Use ONLY stdlib (json, urllib.request, subprocess, os, pathlib) -- no pip dependencies.

**Architecture change:** Replace all TOML reading and dual-service launchctl monitoring with HTTP API calls to localhost:8780. The plugin now monitors ONE service: com.terryli.claude-tts-companion.

**HTTP helper functions:**

- `api_get(path)` -- GET localhost:8780/{path}, returns parsed JSON or None on failure (timeout 2s)
- `api_post(path, data)` -- POST localhost:8780/{path} with JSON body, returns parsed JSON or None (timeout 2s)
- Keep `svc_pid(label)` for checking if the unified service launchd process is running (BAR-01)

**State gathering:**

- Call GET /health to get subsystem status, uptime, RSS (BAR-05)
- Call GET /settings to get current subtitle and TTS settings (BAR-02, BAR-03)
- Check launchd PID for com.terryli.claude-tts-companion (BAR-01)
- If API unreachable, show degraded status in menu (service down)

**Menu bar icon:**

- Same SF Symbol `:gearshape.2.fill:` with green (#34c759) when service running + API healthy, red (#ff3b30) when service down

**Section 1: Service (BAR-01, BAR-05)**

```
:server.rack: Service
  {green/red dot} claude-tts-companion  PID {pid}
    -- Stop / Restart / Start (using nc-action.sh svc-stop/svc-restart/svc-start with label com.terryli.claude-tts-companion)
  Uptime: {X}h {Y}m   RSS: {Z} MB
  Subsystems:
    Bot: {status}   TTS: {status}   Subtitle: {status}
```

**Section 2: Subtitle Controls (BAR-02, EXT-03)**

```
:textformat.size: Subtitle
  Font Size: {current} (checkmark on current)
    -- Small | param1=set-subtitle param2=fontSize param3=small
    -- Medium | param1=set-subtitle param2=fontSize param3=medium
    -- Large | param1=set-subtitle param2=fontSize param3=large
  Position: {current}
    -- Top | param1=set-subtitle param2=position param3=top
    -- Bottom | param1=set-subtitle param2=position param3=bottom
  {toggle} Karaoke | param1=set-subtitle param2=karaokeEnabled param3=toggle
  Screen: {current}
    -- Built-in | param1=set-subtitle param2=screen param3=builtin
    -- External | param1=set-subtitle param2=screen param3=external
```

**Section 3: TTS Controls (BAR-03)**

```
:speaker.wave.3.fill: TTS
  {toggle} TTS Enabled | param1=set-tts param2=enabled param3=toggle
  Voice: {current}
  Speed: {current}x
    -- speed options (0.8, 1.0, 1.2, 1.3, 1.5, 2.0) with checkmarks
  Test TTS | param1=test-tts
```

**Section 4: Quick Actions**

```
---
Refresh Now | refresh=true
```

**Action format:** All menu actions use `bash="{ACTION_SH}" param1=... param2=... param3=... terminal=false refresh=true` pattern (same as v2.0.0). The action script (nc-action.sh) will be updated in Plan 02 to handle the new action names.

**Action names for nc-action.sh (Plan 02 will implement these):**

- `svc-stop`, `svc-start`, `svc-restart` -- keep existing (pass label as param2)
- `set-subtitle` -- param2=field, param3=value (calls POST /settings/subtitle)
- `set-tts` -- param2=field, param3=value (calls POST /settings/tts)
- `test-tts` -- calls POST /subtitle/show with test text

**Per BAR-04:** All HTTP calls use 2-second timeout to ensure menu renders quickly even if service is slow/down.

Remove ALL v2.0.0 code: TOML reading, dual-service SERVICES list, thinking sync section, Arc Summary/Tail Brief Telegram toggle sections, Kokoro TTS server references, log reading section, audio device section. These features either don't exist in the unified binary or are controlled differently.
</action>
<verify>
<automated>python3 -c "import py_compile; py_compile.compile('$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py', doraise=True)" && echo "SYNTAX OK"</automated>
</verify>
<acceptance_criteria> - Plugin file has swiftbar.version 3.0.0 - Uses urllib.request to call localhost:8780 endpoints (not TOML reading) - Monitors com.terryli.claude-tts-companion (not telegram-bot/kokoro-tts-server) - Menu has Service, Subtitle, TTS sections - Font size options: small/medium/large with checkmarks - Position options: top/bottom with checkmarks - Karaoke toggle present - Screen options: builtin/external (EXT-03) - TTS enable toggle + speed submenu + test button - Health subsystem status displayed (BAR-05) - All HTTP calls have 2-second timeout (BAR-04) - No TOML references remain - Python syntax valid
</acceptance_criteria>
<done>claude-hq.10s.py is v3.0.0, compiles without syntax errors, outputs SwiftBar menu format with HTTP API-backed subtitle/TTS/health sections, and references only the unified service</done>
</task>

<task type="auto">
  <name>Task 2: Verify menu output structure</name>
  <files>~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py</files>
  <read_first>
    - ~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py
  </read_first>
  <action>
Run the plugin script and verify the output structure. The service may not be running, so the output should show degraded/offline status gracefully (no Python tracebacks).

Verify:

1. Run `python3 "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"` -- must exit 0 even when service is down
2. Output must contain the menu bar icon line with SF Symbol
3. Output must contain "Subtitle" section header
4. Output must contain "TTS" section header
5. Output must contain font size options (small, medium, large)
6. Output must contain position options (top, bottom)
7. Output must contain screen options (builtin, external) for EXT-03
8. Output must contain "Karaoke" in the menu
9. No Python tracebacks in output
10. Verify `grep -c "3.0.0"` returns 1 (version bump)
11. Verify no references to "telegram-bot" or "kokoro-tts-server" service labels remain

If any check fails, fix the plugin file.
</action>
<verify>
<automated>python3 "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py" 2>&1 | head -50 && echo "---EXIT:$?"</automated>
</verify>
<acceptance_criteria> - Script exits 0 even when companion service is not running - Output contains service status section, subtitle controls, TTS controls - No tracebacks or import errors - Version is 3.0.0
</acceptance_criteria>
<done>Plugin renders complete SwiftBar menu output without errors, showing degraded status when service is offline and full controls when online</done>
</task>

</tasks>

<verification>
1. `python3 -c "import py_compile; py_compile.compile('$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py', doraise=True)"` succeeds
2. `python3 "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"` exits 0 with valid SwiftBar menu output
3. `grep "3.0.0" "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"` matches version header
4. `grep -c "com.terryli.claude-tts-companion" "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"` returns >= 1
5. `grep -c "telegram-bot\|kokoro-tts-server" "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py"` returns 0
</verification>

<success_criteria>

- claude-hq v3.0.0 plugin monitors single unified service via HTTP API
- Menu displays subtitle controls, TTS controls, and per-subsystem health
- External monitor switching option present in subtitle menu
- All actions reference HTTP API endpoints (not TOML)
- Plugin renders gracefully when service is offline
  </success_criteria>

<output>
After completion, create `.planning/phases/09-swiftbar-integration/09-01-SUMMARY.md`
</output>
