aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/cross-agent-comms/cross-agent-watch
blob: f50ba26431ae9d105d0d732d87b83816b5d88638 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env bash
# cross-agent-watch — desktop-notify on new cross-agent messages.
#
# See cross-agent-watch.md. Watches every ~/projects/*/inbox/from-agents/ by
# default. inotifywait fires create + moved_to events; .tmp.* files are
# filtered out. HALT suppresses notifications but the watcher keeps running
# and logs each event with "(suppressed by HALT)".

set -uo pipefail

# Defaults.
PROJECTS_GLOB="${HOME}/projects/*/inbox/from-agents/"
LOG_FILE="${HOME}/.local/state/cross-agent-comms/watch.log"
HALT_FILE="${HOME}/.config/cross-agent-comms/HALT"
QUIET=0
NO_NOTIFY=0

# Arg parsing.
while [[ $# -gt 0 ]]; do
  case "$1" in
    --projects-glob)
      PROJECTS_GLOB="$2"; shift 2 ;;
    --log)
      LOG_FILE="$2"; shift 2 ;;
    --quiet)
      QUIET=1; shift ;;
    --no-notify)
      NO_NOTIFY=1; shift ;;
    -h|--help)
      cat <<EOF
Usage: cross-agent-watch [--projects-glob GLOB] [--log PATH] [--quiet] [--no-notify]

Watches inbox/from-agents/ directories for new cross-agent messages and fires
desktop notifications. See cross-agent-watch.md for details.
EOF
      exit 0 ;;
    *)
      echo "unknown flag: $1" >&2; exit 1 ;;
  esac
done

# Resolve glob to a concrete list of directories.
# shellcheck disable=SC2086
DIRS=( $PROJECTS_GLOB )
# Filter out non-existent paths (glob may include literal pattern when no match).
EXISTING=()
for d in "${DIRS[@]}"; do
  if [[ -d "$d" ]]; then
    EXISTING+=( "$d" )
  fi
done

if [[ ${#EXISTING[@]} -eq 0 ]]; then
  echo "cross-agent-watch: glob resolved 0 directories: $PROJECTS_GLOB" >&2
  exit 1
fi

# Ensure log dir exists.
mkdir -p "$(dirname "$LOG_FILE")"

[[ $QUIET -eq 0 ]] && echo "cross-agent-watch: watching ${#EXISTING[@]} dir(s); log: $LOG_FILE"

# Helper: project name from path like /home/.../projects/<name>/inbox/from-agents/...
project_name() {
  local path="$1"
  # Match ~/projects/<name>/...
  if [[ "$path" =~ ${HOME}/projects/([^/]+)/ ]]; then
    echo "${BASH_REMATCH[1]}"
  else
    basename "$(dirname "$(dirname "$path")")"
  fi
}

# Main loop. inotifywait emits one line per event in the format
# "<full-path>" because we passed --format '%w%f'.
inotifywait -m -e create,moved_to --format '%w%f' "${EXISTING[@]}" 2>/dev/null \
  | while IFS= read -r path; do
    filename="$(basename "$path")"

    # Filter .tmp.* staging files.
    case "$filename" in
      .tmp.*) continue ;;
    esac

    # Filter .asc sidecars — they land first per the atomic-write ordering;
    # the .org event will fire after.
    case "$filename" in
      *.asc) continue ;;
    esac

    proj="$(project_name "$path")"
    iso="$(date -u "+%Y-%m-%dT%H:%M:%SZ")"

    if [[ -e "$HALT_FILE" ]]; then
      printf '%s\t%s\t%s\t(suppressed by HALT)\n' "$iso" "$proj" "$filename" >> "$LOG_FILE"
      [[ $QUIET -eq 0 ]] && echo "[$iso] $proj: $filename (suppressed by HALT)"
      continue
    fi

    printf '%s\t%s\t%s\n' "$iso" "$proj" "$filename" >> "$LOG_FILE"
    [[ $QUIET -eq 0 ]] && echo "[$iso] $proj: $filename"

    if [[ $NO_NOTIFY -eq 0 ]]; then
      notify info "Cross-agent message" "${proj}: ${filename}" --persist 2>/dev/null || true
    fi
  done