#!/bin/bash # Wrapper script for copilot to get clickable focus notifications via swaync set -euo pipefail notify_focus() { local action="$1" local win_addr="$2" [ -n "$action" ] || return 0 [ "$action" = "f" ] && [ -n "$win_addr" ] && hyprctl dispatch focuswindow address:"$win_addr" >/dev/null 2>&1 || true } notify_action_required() { local win_addr="$1" local action action=$(notify-send "Copilot" "✋ Action required" --action="f=Focus" -u critical -a "Copilot" -i ~/dl/copilot.png || true) notify_focus "$action" "$win_addr" } notify_task_finished() { local win_addr="$1" local action action=$(notify-send "Copilot" "✅ Task finished" --action="f=Focus" -u normal -a "Copilot" -i ~/dl/copilot.png || true) notify_focus "$action" "$win_addr" } start_interactive_monitor() { local win_addr="$1" local started_epoch="$2" local log_file local line local busy=0 local ask_user_pending=0 # Wait for the process log created by this copilot invocation. for _ in $(seq 1 60); do log_file=$(find "$HOME/.copilot/logs" -maxdepth 1 -name 'process-*.log' -newermt "@$started_epoch" -print 2>/dev/null | sort | tail -n1 || true) if [ -n "${log_file:-}" ]; then break fi sleep 0.1 done [ -n "${log_file:-}" ] || return 0 tail -n0 -F "$log_file" 2>/dev/null | while IFS= read -r line; do if [ "$ask_user_pending" -gt 0 ]; then ask_user_pending=$((ask_user_pending - 1)) fi case "$line" in *"kind: assistant_turn_start"*) busy=1 ;; *'"name": "ask_user"'*) # "ask_user" appears in tool schema on every turn; only alert if a # real tool call follows immediately with a question payload. ask_user_pending=8 ;; *'"arguments": "{\"question\":'*) if [ "$ask_user_pending" -gt 0 ]; then notify_action_required "$win_addr" ask_user_pending=0 fi ;; *"kind: session_idle"*) if [ "$busy" -eq 1 ]; then notify_task_finished "$win_addr" busy=0 fi ask_user_pending=0 ;; esac done } is_prompt_run=0 for arg in "$@"; do case "$arg" in -p | --prompt | -i | --interactive) is_prompt_run=1 break ;; esac done win_addr="$(hyprctl activewindow -j | jq -r '.address // empty')" if [ "$is_prompt_run" -eq 0 ] || [ "$#" -eq 0 ]; then started_epoch=$(date +%s) if [ -d "$HOME/.copilot/logs" ]; then start_interactive_monitor "$win_addr" "$started_epoch" & monitor_pid=$! trap 'kill "$monitor_pid" 2>/dev/null || true' EXIT fi # Debug logs include structured telemetry markers needed by the monitor. has_log_level=0 for arg in "$@"; do case "$arg" in --log-level | --log-level=*) has_log_level=1 break ;; esac done set +e if [ "$has_log_level" -eq 1 ]; then copilot "$@" else copilot --log-level debug "$@" fi exit_code=$? set -e kill "${monitor_pid:-}" 2>/dev/null || true exit "$exit_code" fi copilot --output-format json "$@" | tee >( jq -Rrc ' (fromjson? | select(. != null)) as $e | if $e.type=="result" then {"kind":"done","code":($e.exitCode // 0)} elif $e.type=="tool.execution_start" and ($e.data.toolName=="ask_user") then {"kind":"action"} else empty end ' | while read -r ev; do kind="$(jq -r '.kind' <<<"$ev")" if [ "$kind" = "action" ]; then notify_action_required "$win_addr" else notify_task_finished "$win_addr" fi done ) | jq -Rr '(fromjson? | select(.type=="assistant.message") | (.data.content // empty))'