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
|