| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- #!/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))'
|