---
phase: 09-swiftbar-integration
plan: 02
type: execute
wave: 2
depends_on: ["09-01"]
files_modified:
  - ~/Library/Application Support/SwiftBar/Plugins/nc-action.sh
autonomous: false
requirements: [BAR-04]

must_haves:
  truths:
    - "Clicking a subtitle menu item in SwiftBar changes the setting via HTTP API"
    - "Clicking a TTS menu item in SwiftBar changes the setting via HTTP API"
    - "Service start/stop/restart actions work for the unified service"
    - "Test TTS action shows a subtitle on screen via HTTP API"
  artifacts:
    - path: "~/Library/Application Support/SwiftBar/Plugins/nc-action.sh"
      provides: "Action handler for SwiftBar menu items using HTTP API curl calls"
      contains: "localhost:8780"
  key_links:
    - from: "nc-action.sh set-subtitle"
      to: "localhost:8780/settings/subtitle"
      via: "curl POST"
      pattern: "curl.*settings/subtitle"
    - from: "nc-action.sh set-tts"
      to: "localhost:8780/settings/tts"
      via: "curl POST"
      pattern: "curl.*settings/tts"
---

<objective>
Rewrite nc-action.sh to handle the new action names from claude-hq v3.0.0. Replace TOML-based toggles with curl calls to the HTTP control API at localhost:8780. Verify the full SwiftBar menu works visually.

Purpose: Complete the SwiftBar integration by connecting menu actions to the HTTP API backend.
Output: Updated nc-action.sh with HTTP API curl actions + visual verification of the menu.
</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/phases/09-swiftbar-integration/09-CONTEXT.md
@.planning/phases/09-swiftbar-integration/09-01-SUMMARY.md

<interfaces>
<!-- Action names from Plan 01 that this script must handle -->

From claude-hq.10s.py v3.0.0 menu actions:

- svc-stop $label -- launchctl bootout for unified service
- svc-start $label -- launchctl bootstrap for unified service
- svc-restart $label -- launchctl kickstart -k for unified service
- set-subtitle $field $value -- POST /settings/subtitle with {field: value}
- set-tts $field $value -- POST /settings/tts with {field: value}
- test-tts -- POST /subtitle/show with test text + duration

HTTP API at localhost:8780:

- POST /settings/subtitle -- JSON body with optional fields: fontSize, position, opacity, karaokeEnabled, screen
- POST /settings/tts -- JSON body with optional fields: enabled, voice, speed
- POST /subtitle/show -- JSON body: {"text": "...", "duration": 3.0}
- POST /subtitle/hide -- no body

Special handling for "toggle" value:

- When param3="toggle" for boolean fields (karaokeEnabled, enabled):
  First GET /settings, read current value, then POST the opposite
  </interfaces>

</context>

<tasks>

<task type="auto">
  <name>Task 1: Rewrite nc-action.sh for HTTP API actions</name>
  <files>~/Library/Application Support/SwiftBar/Plugins/nc-action.sh</files>
  <read_first>
    - ~/Library/Application Support/SwiftBar/Plugins/nc-action.sh (current v2.0.0 action script)
    - ~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py (v3.0.0 to see action names used)
  </read_first>
  <action>
Rewrite nc-action.sh. Keep: shebang, set -euo pipefail, LOG path, log_event function, svc-stop/svc-start/svc-restart cases (update to use com.terryli.claude-tts-companion as default label). Remove: ALL TOML functions (toml_read, toml_set), restart_bot function, all toggle-* cases, old test-tts, audio cases.

**New case handlers:**

`set-subtitle)` -- $2=field name, $3=value

- If $3="toggle" AND field is boolean (karaokeEnabled):
  - GET localhost:8780/settings, parse current value with python3 one-liner, compute opposite
  - POST localhost:8780/settings/subtitle with {"karaokeEnabled": !current}
- Else for string/number fields (fontSize, position, screen, opacity):
  - POST localhost:8780/settings/subtitle with {$2: $3}
- Use curl with --max-time 3, -sf flags
- Log event with log_event

`set-tts)` -- $2=field name, $3=value

- If $3="toggle" AND field is boolean (enabled):
  - GET localhost:8780/settings, parse current value, compute opposite
  - POST localhost:8780/settings/tts with {"enabled": !current}
- Else for string/number fields (voice, speed):
  - POST localhost:8780/settings/tts with {$2: $3}
  - For speed, $3 is a float string like "1.30" -- pass as number in JSON
- Use curl with --max-time 3, -sf flags

`test-tts)` -- no extra params

- POST localhost:8780/subtitle/show with {"text": "Claude TTS companion is working.", "duration": 5.0}
- Log event

**JSON construction:** Use python3 -c one-liners for JSON encoding (avoid shell quoting issues). Example:

```bash
JSON=$(python3 -c "import json; print(json.dumps({'$FIELD': '$VALUE'}))")
curl -sf --max-time 3 -X POST -H 'Content-Type: application/json' -d "$JSON" http://localhost:8780/settings/subtitle
```

For boolean toggle, handle type correctly:

```bash
# For karaokeEnabled toggle
CUR=$(curl -sf --max-time 2 http://localhost:8780/settings | python3 -c "import json,sys; print(json.load(sys.stdin)['subtitle']['karaokeEnabled'])")
NEW=$(python3 -c "print('true' if '$CUR' == 'False' else 'false')")
```

For speed (float), ensure JSON has number not string:

```bash
JSON=$(python3 -c "import json; print(json.dumps({'speed': float('$VALUE')}))")
```

Keep the catch-all `*)` case with log_event for unknown actions.
</action>
<verify>
<automated>bash -n "$HOME/Library/Application Support/SwiftBar/Plugins/nc-action.sh" && echo "SYNTAX OK"</automated>
</verify>
<acceptance_criteria> - Script has set-subtitle, set-tts, test-tts case handlers - All HTTP calls use curl to localhost:8780 - Boolean toggle logic reads current value then posts opposite - Speed values passed as JSON numbers (not strings) - No TOML references remain (no toml_read, toml_set, MISE_TOML) - No restart_bot function - svc-stop/start/restart still work for any launchd label - bash -n syntax check passes
</acceptance_criteria>
<done>nc-action.sh handles all v3.0.0 SwiftBar action names via HTTP API curl calls, with no TOML dependencies</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Visual verification of SwiftBar menu</name>
  <files>~/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py</files>
  <action>
Present the SwiftBar menu for visual verification. Run the plugin script to show current output, then ask user to click the menu bar icon to verify the live rendering.
  </action>
  <verify>
    <automated>python3 "$HOME/Library/Application Support/SwiftBar/Plugins/claude-hq.10s.py" 2>&1 | head -50 && echo "---EXIT:$?"</automated>
  </verify>
  <what-built>
    Complete SwiftBar integration: claude-hq v3.0.0 plugin + nc-action.sh with HTTP API actions. The SwiftBar menu should show the unified service status, subtitle controls (font size, position, karaoke, screen), and TTS controls (enable, speed, test).
  </what-built>
  <how-to-verify>
    1. Click the gear icon in the macOS menu bar (SwiftBar claude-hq plugin)
    2. Verify the menu shows:
       - Service section with claude-tts-companion status (likely red/offline if service not running)
       - Subtitle section with font size (S/M/L), position (top/bottom), karaoke toggle, screen (builtin/external)
       - TTS section with enable toggle, speed submenu, test button
    3. If the companion service IS running (port 8780): try clicking a subtitle font size option and verify it changes on next menu refresh
    4. If service is NOT running: verify the menu renders gracefully without errors (shows offline status)
    5. Verify no old v2.0.0 items appear (no Thinking Sync, no Arc Summary toggle, no Kokoro TTS server, no Audio device section)
  </how-to-verify>
  <done>User confirms SwiftBar menu renders correctly with all v3.0.0 sections visible</done>
  <resume-signal>Type "approved" or describe issues</resume-signal>
</task>

</tasks>

<verification>
1. `bash -n "$HOME/Library/Application Support/SwiftBar/Plugins/nc-action.sh"` passes
2. `grep -c "localhost:8780" "$HOME/Library/Application Support/SwiftBar/Plugins/nc-action.sh"` returns >= 3
3. `grep -c "toml" "$HOME/Library/Application Support/SwiftBar/Plugins/nc-action.sh"` returns 0
4. Visual: SwiftBar menu renders with subtitle and TTS controls
</verification>

<success_criteria>

- nc-action.sh handles set-subtitle, set-tts, test-tts via HTTP API
- SwiftBar menu visually displays all control sections
- No v2.0.0 remnants in either file
- Menu actions successfully call HTTP API when service is running
  </success_criteria>

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