@codefilabs/tq 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -3
- package/package.json +4 -1
- package/scripts/tq +126 -17
- package/scripts/tq-converse +726 -0
- package/scripts/tq-cron-sync +92 -0
- package/scripts/tq-install.sh +75 -11
- package/scripts/tq-message +96 -27
- package/scripts/tq-setup +1 -1
- package/scripts/tq-telegram-poll +216 -34
- package/skills/tq/SKILL.md +42 -6
- package/skills/tq/references/session-naming.md +16 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Ensure homebrew binaries are available (cron has minimal PATH)
|
|
5
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
6
|
+
|
|
7
|
+
INTERVAL=20
|
|
8
|
+
|
|
9
|
+
while [[ $# -gt 0 ]]; do
|
|
10
|
+
case "${1:-}" in
|
|
11
|
+
--interval)
|
|
12
|
+
if [[ $# -lt 2 ]]; then
|
|
13
|
+
echo "Error: --interval requires a value" >&2; exit 1
|
|
14
|
+
fi
|
|
15
|
+
INTERVAL="$2"; shift; shift ;;
|
|
16
|
+
*) echo "Unknown flag: $1" >&2; exit 1 ;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
|
|
20
|
+
QUEUES_DIR="${HOME}/.tq/queues"
|
|
21
|
+
LOGS_DIR="${HOME}/.tq/logs"
|
|
22
|
+
mkdir -p "$QUEUES_DIR" "$LOGS_DIR"
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Scan ~/.tq/queues/*.yaml for schedule: keys via embedded Python temp file
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
SCAN_SCRIPT=$(mktemp /tmp/tq-scan-XXXXXX)
|
|
28
|
+
CRONTAB_NEW=$(mktemp /tmp/tq-crontab-XXXXXX)
|
|
29
|
+
trap 'rm -f "$SCAN_SCRIPT" "$CRONTAB_NEW"' EXIT
|
|
30
|
+
|
|
31
|
+
cat > "$SCAN_SCRIPT" <<'PYEOF'
|
|
32
|
+
import sys, os, re, json, glob
|
|
33
|
+
|
|
34
|
+
queues_dir = sys.argv[1]
|
|
35
|
+
results = []
|
|
36
|
+
|
|
37
|
+
for yaml_path in sorted(glob.glob(os.path.join(queues_dir, '*.yaml'))):
|
|
38
|
+
name = os.path.basename(yaml_path)[:-5] # strip .yaml
|
|
39
|
+
schedule = None
|
|
40
|
+
try:
|
|
41
|
+
with open(yaml_path) as f:
|
|
42
|
+
for line in f:
|
|
43
|
+
m = re.match(r'^schedule:\s*["\']?([^"\'#\n]+?)["\']?\s*$', line)
|
|
44
|
+
if m:
|
|
45
|
+
schedule = m.group(1).strip()
|
|
46
|
+
break
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
if schedule:
|
|
50
|
+
results.append({'name': name, 'schedule': schedule, 'path': yaml_path})
|
|
51
|
+
|
|
52
|
+
for r in results:
|
|
53
|
+
print(json.dumps(r))
|
|
54
|
+
PYEOF
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Detect installed tq binary path
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
if [[ -x "/opt/homebrew/bin/tq" ]]; then
|
|
60
|
+
TQ_BIN="/opt/homebrew/bin/tq"
|
|
61
|
+
SELF_BIN="/opt/homebrew/bin/tq-cron-sync"
|
|
62
|
+
elif [[ -x "/usr/local/bin/tq" ]]; then
|
|
63
|
+
TQ_BIN="/usr/local/bin/tq"
|
|
64
|
+
SELF_BIN="/usr/local/bin/tq-cron-sync"
|
|
65
|
+
else
|
|
66
|
+
echo "Error: tq not found in /opt/homebrew/bin or /usr/local/bin" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# Read existing crontab, strip all tq-managed lines into temp file
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
{ crontab -l 2>/dev/null || true; } | grep -v '# tq-managed:' > "$CRONTAB_NEW" || true
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# Append new managed lines for each scheduled queue
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
while IFS= read -r JSON_LINE; do
|
|
79
|
+
[[ -z "$JSON_LINE" ]] && continue
|
|
80
|
+
NAME="$(python3 -c "import sys,json; print(json.loads(sys.argv[1])['name'])" "$JSON_LINE")"
|
|
81
|
+
SCHEDULE="$(python3 -c "import sys,json; print(json.loads(sys.argv[1])['schedule'])" "$JSON_LINE")"
|
|
82
|
+
YAML_PATH="$(python3 -c "import sys,json; print(json.loads(sys.argv[1])['path'])" "$JSON_LINE")"
|
|
83
|
+
echo "${SCHEDULE} ${TQ_BIN} ${YAML_PATH} >> ${LOGS_DIR}/tq.log 2>&1 # tq-managed:${NAME}:run" >> "$CRONTAB_NEW"
|
|
84
|
+
echo "*/30 * * * * ${TQ_BIN} --status ${YAML_PATH} >> ${LOGS_DIR}/tq.log 2>&1 # tq-managed:${NAME}:status" >> "$CRONTAB_NEW"
|
|
85
|
+
done < <(python3 "$SCAN_SCRIPT" "$QUEUES_DIR")
|
|
86
|
+
|
|
87
|
+
# Self-watcher entry
|
|
88
|
+
echo "*/${INTERVAL} * * * * ${SELF_BIN} >> ${LOGS_DIR}/tq-cron-sync.log 2>&1 # tq-managed:tq-cron-sync" >> "$CRONTAB_NEW"
|
|
89
|
+
|
|
90
|
+
# Write merged crontab
|
|
91
|
+
crontab "$CRONTAB_NEW"
|
|
92
|
+
echo "tq-cron-sync: crontab synced ($(grep -c '# tq-managed:' "$CRONTAB_NEW" || true) managed entries)"
|
package/scripts/tq-install.sh
CHANGED
|
@@ -5,10 +5,10 @@ set -euo pipefail
|
|
|
5
5
|
# Step 1: Register marketplace and install Claude plugin
|
|
6
6
|
# ---------------------------------------------------------------------------
|
|
7
7
|
if command -v claude &>/dev/null; then
|
|
8
|
-
echo "Adding
|
|
9
|
-
claude plugin marketplace add
|
|
8
|
+
echo "Adding codefilabs marketplace..."
|
|
9
|
+
claude plugin marketplace add codefilabs/marketplace
|
|
10
10
|
echo "Installing tq plugin..."
|
|
11
|
-
claude plugin install tq@
|
|
11
|
+
claude plugin install tq@codefilabs
|
|
12
12
|
else
|
|
13
13
|
echo "Warning: 'claude' CLI not found — skipping plugin registration." >&2
|
|
14
14
|
echo " Install Claude Code first: https://claude.ai/code" >&2
|
|
@@ -24,11 +24,11 @@ elif [[ -n "${BASH_SOURCE[0]:-}" && -f "${BASH_SOURCE[0]}" ]]; then
|
|
|
24
24
|
PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
25
25
|
else
|
|
26
26
|
# Running via curl | bash — find the installed plugin cache
|
|
27
|
-
CACHE_DIR="$HOME/.claude/plugins/cache/tq/
|
|
27
|
+
CACHE_DIR="$HOME/.claude/plugins/cache/tq/codefilabs"
|
|
28
28
|
PLUGIN_ROOT="$(ls -d "$CACHE_DIR"/*/ 2>/dev/null | sort -V | tail -1)"
|
|
29
29
|
if [[ -z "${PLUGIN_ROOT:-}" ]]; then
|
|
30
30
|
echo "Error: could not locate tq plugin files in $CACHE_DIR" >&2
|
|
31
|
-
echo " Try running: claude plugin marketplace add
|
|
31
|
+
echo " Try running: claude plugin marketplace add codefilabs/marketplace && claude plugin install tq@codefilabs" >&2
|
|
32
32
|
exit 1
|
|
33
33
|
fi
|
|
34
34
|
fi
|
|
@@ -48,7 +48,7 @@ else
|
|
|
48
48
|
exit 1
|
|
49
49
|
fi
|
|
50
50
|
|
|
51
|
-
for SCRIPT in tq tq-message tq-setup tq-telegram-poll tq-telegram-watchdog; do
|
|
51
|
+
for SCRIPT in tq tq-message tq-setup tq-telegram-poll tq-telegram-watchdog tq-cron-sync tq-converse; do
|
|
52
52
|
SRC="$PLUGIN_ROOT/scripts/$SCRIPT"
|
|
53
53
|
DEST="$INSTALL_DIR/$SCRIPT"
|
|
54
54
|
if [[ -L "$DEST" ]]; then
|
|
@@ -63,16 +63,80 @@ done
|
|
|
63
63
|
|
|
64
64
|
mkdir -p ~/.tq/queues ~/.tq/logs ~/.tq/config
|
|
65
65
|
|
|
66
|
+
# Run initial cron sync to pick up any existing queue schedules
|
|
67
|
+
# Use $INSTALL_DIR directly — PATH may not reflect the just-created symlink yet
|
|
68
|
+
"$INSTALL_DIR/tq-cron-sync" --interval 20
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# Step 4: Set up Telegram long-poll daemon via launchd (if bot is configured)
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
PLIST_LABEL="com.codefi.tq-telegram"
|
|
74
|
+
PLIST_PATH="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"
|
|
75
|
+
|
|
76
|
+
if [[ -f "$HOME/.tq/config/message.yaml" ]] && grep -q 'bot_token' "$HOME/.tq/config/message.yaml" 2>/dev/null; then
|
|
77
|
+
# Remove old cron-based poll entries (replaced by launchd daemon)
|
|
78
|
+
CRONTAB_CURRENT="$(crontab -l 2>/dev/null || true)"
|
|
79
|
+
if echo "$CRONTAB_CURRENT" | grep -q 'tq-telegram-poll'; then
|
|
80
|
+
echo "$CRONTAB_CURRENT" | grep -v 'tq-telegram-poll' | grep -v 'tq-telegram-watchdog' | crontab -
|
|
81
|
+
echo " removed cron-based telegram poll (replaced by launchd daemon)"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
cat > "$PLIST_PATH" <<PLISTEOF
|
|
85
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
86
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
87
|
+
<plist version="1.0">
|
|
88
|
+
<dict>
|
|
89
|
+
<key>Label</key>
|
|
90
|
+
<string>${PLIST_LABEL}</string>
|
|
91
|
+
<key>ProgramArguments</key>
|
|
92
|
+
<array>
|
|
93
|
+
<string>${INSTALL_DIR}/tq-telegram-poll</string>
|
|
94
|
+
<string>--daemon</string>
|
|
95
|
+
</array>
|
|
96
|
+
<key>RunAtLoad</key>
|
|
97
|
+
<true/>
|
|
98
|
+
<key>KeepAlive</key>
|
|
99
|
+
<true/>
|
|
100
|
+
<key>StandardOutPath</key>
|
|
101
|
+
<string>${HOME}/.tq/logs/tq-telegram.log</string>
|
|
102
|
+
<key>StandardErrorPath</key>
|
|
103
|
+
<string>${HOME}/.tq/logs/tq-telegram.log</string>
|
|
104
|
+
<key>EnvironmentVariables</key>
|
|
105
|
+
<dict>
|
|
106
|
+
<key>PATH</key>
|
|
107
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
108
|
+
</dict>
|
|
109
|
+
</dict>
|
|
110
|
+
</plist>
|
|
111
|
+
PLISTEOF
|
|
112
|
+
|
|
113
|
+
# (Re)load the daemon
|
|
114
|
+
launchctl bootout "gui/$(id -u)/${PLIST_LABEL}" 2>/dev/null || true
|
|
115
|
+
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
|
116
|
+
echo " telegram daemon started (launchd: ${PLIST_LABEL})"
|
|
117
|
+
else
|
|
118
|
+
echo ""
|
|
119
|
+
echo "Telegram bot not configured yet. After running tq-setup, re-run this"
|
|
120
|
+
echo "installer to start the long-poll daemon automatically."
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
echo ""
|
|
124
|
+
echo "tq installed. Cron schedules are managed automatically."
|
|
125
|
+
echo ""
|
|
126
|
+
echo "Add a schedule: key to any queue file in ~/.tq/queues/ to auto-schedule it:"
|
|
66
127
|
echo ""
|
|
67
|
-
echo "
|
|
128
|
+
echo " schedule: \"0 9 * * *\" # runs daily at 9am"
|
|
129
|
+
echo " cwd: /path/to/project"
|
|
130
|
+
echo " tasks: ..."
|
|
68
131
|
echo ""
|
|
69
|
-
echo "
|
|
70
|
-
echo "
|
|
132
|
+
echo "tq-cron-sync runs every 20 minutes and syncs all queue schedules to crontab."
|
|
133
|
+
echo "To change the sync interval: tq-cron-sync --interval <minutes>"
|
|
71
134
|
echo ""
|
|
72
135
|
echo "To configure Telegram notifications:"
|
|
73
136
|
echo " tq-setup"
|
|
74
137
|
echo ""
|
|
75
138
|
echo "Or from Claude Code: /setup-telegram"
|
|
76
139
|
echo ""
|
|
77
|
-
echo "
|
|
78
|
-
echo "
|
|
140
|
+
echo "Conversation mode (interactive Telegram <-> Claude Code):"
|
|
141
|
+
echo " tq-converse start [--cwd /path/to/project]"
|
|
142
|
+
echo " Or send /converse from Telegram"
|
package/scripts/tq-message
CHANGED
|
@@ -8,6 +8,7 @@ QUEUE_FILE=""
|
|
|
8
8
|
MESSAGE_TEXT=""
|
|
9
9
|
SESSION=""
|
|
10
10
|
EXPLICIT_STATE_FILE=""
|
|
11
|
+
REPLY_TO_MSG_ID=""
|
|
11
12
|
|
|
12
13
|
while [[ $# -gt 0 ]]; do
|
|
13
14
|
case "${1:-}" in
|
|
@@ -16,15 +17,18 @@ while [[ $# -gt 0 ]]; do
|
|
|
16
17
|
--message) MESSAGE_TEXT="${2:-}"; shift 2 ;;
|
|
17
18
|
--session) SESSION="${2:-}"; shift 2 ;;
|
|
18
19
|
--state-file) EXPLICIT_STATE_FILE="${2:-}"; shift 2 ;;
|
|
20
|
+
--reply-to) REPLY_TO_MSG_ID="${2:-}"; shift 2 ;;
|
|
19
21
|
--) shift; break ;;
|
|
20
22
|
-*) echo "Unknown flag: $1" >&2; exit 1 ;;
|
|
21
23
|
*) break ;;
|
|
22
24
|
esac
|
|
23
25
|
done
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
# Allow --message without --task/--queue (conversation mode)
|
|
28
|
+
if [[ -z "$TASK_HASH" && -z "$QUEUE_FILE" && -z "$MESSAGE_TEXT" ]]; then
|
|
26
29
|
echo "Usage: tq-message --task <hash> --queue <file.yaml> [--message <text>] [--session <name>]" >&2
|
|
27
30
|
echo " tq-message --queue <file.yaml> [--message <text>]" >&2
|
|
31
|
+
echo " tq-message --message <text> [--reply-to <msg_id>]" >&2
|
|
28
32
|
exit 1
|
|
29
33
|
fi
|
|
30
34
|
|
|
@@ -36,7 +40,7 @@ if [[ -n "$QUEUE_FILE" ]]; then
|
|
|
36
40
|
fi
|
|
37
41
|
fi
|
|
38
42
|
|
|
39
|
-
CONFIG_SCRIPT=$(mktemp /tmp/tq-message-config-XXXXXX
|
|
43
|
+
CONFIG_SCRIPT=$(mktemp /tmp/tq-message-config-XXXXXX)
|
|
40
44
|
trap 'rm -f "$CONFIG_SCRIPT"' EXIT
|
|
41
45
|
|
|
42
46
|
cat > "$CONFIG_SCRIPT" <<'PYEOF'
|
|
@@ -165,21 +169,25 @@ build_message() {
|
|
|
165
169
|
if [[ -f "$state_file" ]]; then
|
|
166
170
|
status="$(grep '^status=' "$state_file" | cut -d= -f2)"
|
|
167
171
|
started="$(grep '^started=' "$state_file" | cut -d= -f2)"
|
|
172
|
+
completed_epoch="$(grep '^completed=' "$state_file" | cut -d= -f2)"
|
|
168
173
|
else
|
|
169
174
|
status="done"
|
|
170
175
|
started=""
|
|
176
|
+
completed_epoch=""
|
|
171
177
|
fi
|
|
172
178
|
|
|
173
|
-
# Calculate duration
|
|
179
|
+
# Calculate duration using completed epoch (avoids timezone skew from ISO string parsing)
|
|
174
180
|
duration=""
|
|
175
181
|
if [[ -n "$started" ]]; then
|
|
176
|
-
start_epoch="$(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
elapsed=$((
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
start_epoch="$(date -j -f "%Y-%m-%dT%H:%M:%S" "${started}" "+%s" 2>/dev/null || echo "")"
|
|
183
|
+
end_epoch="${completed_epoch:-$(date +%s)}"
|
|
184
|
+
if [[ -n "$start_epoch" && -n "$end_epoch" ]]; then
|
|
185
|
+
elapsed=$(( end_epoch - start_epoch ))
|
|
186
|
+
if (( elapsed > 0 )); then
|
|
187
|
+
duration="${elapsed}s"
|
|
188
|
+
if (( elapsed >= 60 )); then
|
|
189
|
+
duration="$(( elapsed / 60 ))m $(( elapsed % 60 ))s"
|
|
190
|
+
fi
|
|
183
191
|
fi
|
|
184
192
|
fi
|
|
185
193
|
fi
|
|
@@ -271,16 +279,50 @@ send_telegram() {
|
|
|
271
279
|
local token="$1"
|
|
272
280
|
local chat_id="$2"
|
|
273
281
|
local text="$3"
|
|
282
|
+
local reply_to="${4:-}"
|
|
283
|
+
|
|
284
|
+
local curl_args=()
|
|
285
|
+
curl_args+=(-s -X POST "https://api.telegram.org/bot${token}/sendMessage")
|
|
286
|
+
curl_args+=(-d "chat_id=${chat_id}")
|
|
287
|
+
curl_args+=(--data-urlencode "text=${text}")
|
|
288
|
+
curl_args+=(-d "parse_mode=Markdown")
|
|
289
|
+
|
|
290
|
+
# Add reply threading if a reply_to message ID is provided
|
|
291
|
+
if [[ -n "$reply_to" ]]; then
|
|
292
|
+
curl_args+=(-d "reply_to_message_id=${reply_to}")
|
|
293
|
+
curl_args+=(-d "allow_sending_without_reply=true")
|
|
294
|
+
fi
|
|
274
295
|
|
|
275
296
|
local response
|
|
276
|
-
response="$(curl
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
297
|
+
response="$(curl "${curl_args[@]}" 2>&1)"
|
|
298
|
+
|
|
299
|
+
# Extract the sent message ID for reply threading tracking
|
|
300
|
+
local sent_msg_id
|
|
301
|
+
sent_msg_id="$(echo "$response" | python3 -c "
|
|
302
|
+
import sys, json
|
|
303
|
+
try:
|
|
304
|
+
d = json.load(sys.stdin)
|
|
305
|
+
if d.get('ok'):
|
|
306
|
+
print(d.get('result', {}).get('message_id', ''))
|
|
307
|
+
else:
|
|
308
|
+
sys.exit(1)
|
|
309
|
+
except:
|
|
310
|
+
sys.exit(1)
|
|
311
|
+
" 2>/dev/null || true)"
|
|
312
|
+
|
|
313
|
+
if [[ -n "$sent_msg_id" ]]; then
|
|
314
|
+
echo "[tq-message] sent via Telegram (msg_id=$sent_msg_id)"
|
|
315
|
+
# Track outgoing message ID in registry for reply threading
|
|
316
|
+
if [[ -n "$sent_msg_id" ]] && command -v tq-converse &>/dev/null; then
|
|
317
|
+
# Try to detect which slug this message belongs to
|
|
318
|
+
local slug_file="$HOME/.tq/conversations/latest-reply-slug"
|
|
319
|
+
if [[ -f "$slug_file" ]]; then
|
|
320
|
+
local slug
|
|
321
|
+
slug="$(cat "$slug_file")"
|
|
322
|
+
tq-converse track-msg "$slug" "$sent_msg_id" 2>/dev/null || true
|
|
323
|
+
rm -f "$slug_file"
|
|
324
|
+
fi
|
|
325
|
+
fi
|
|
284
326
|
else
|
|
285
327
|
echo "[tq-message] Telegram error: $response" >&2
|
|
286
328
|
exit 1
|
|
@@ -290,29 +332,56 @@ send_telegram() {
|
|
|
290
332
|
deliver() {
|
|
291
333
|
local service="$1"
|
|
292
334
|
local msg="$2"
|
|
335
|
+
local reply_to="${3:-}"
|
|
293
336
|
case "$service" in
|
|
294
|
-
telegram) send_telegram "$TG_TOKEN" "$TG_CHAT" "$msg" ;;
|
|
337
|
+
telegram) send_telegram "$TG_TOKEN" "$TG_CHAT" "$msg" "$reply_to" ;;
|
|
295
338
|
*) echo "[tq-message] unknown service: $service" >&2; exit 1 ;;
|
|
296
339
|
esac
|
|
297
340
|
}
|
|
298
341
|
|
|
299
|
-
# Summary mode with a live session: ask Claude to
|
|
342
|
+
# Summary mode with a live session: ask Claude to write summary via slash command
|
|
300
343
|
if [[ "$CONTENT" == "summary" && -n "$SESSION" && -z "$MESSAGE_TEXT" ]]; then
|
|
301
344
|
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
302
|
-
|
|
345
|
+
HANDSHAKE_FILE="/tmp/tq-summary-${TASK_HASH}.txt"
|
|
346
|
+
rm -f "$HANDSHAKE_FILE"
|
|
347
|
+
|
|
348
|
+
# Tell Claude to generate summary (slash command calls tq-message --message which writes handshake file + delivers)
|
|
303
349
|
tmux send-keys -t "$SESSION" "/tq-message ${TASK_HASH} ${QUEUE_FILE}" Enter
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
350
|
+
|
|
351
|
+
# Poll for the handshake file (max 90 seconds, check every 3s)
|
|
352
|
+
WAITED=0
|
|
353
|
+
while [[ ! -f "$HANDSHAKE_FILE" && "$WAITED" -lt 90 ]]; do
|
|
354
|
+
sleep 3
|
|
355
|
+
WAITED=$(( WAITED + 3 ))
|
|
356
|
+
done
|
|
357
|
+
|
|
358
|
+
if [[ -f "$HANDSHAKE_FILE" ]]; then
|
|
359
|
+
# Slash command already delivered the message — just clean up
|
|
360
|
+
rm -f "$HANDSHAKE_FILE"
|
|
361
|
+
exit 0
|
|
362
|
+
else
|
|
363
|
+
# Timed out — fall back to details
|
|
364
|
+
MESSAGE_TEXT="$(build_message "details" "$TASK_HASH" "$STATE_FILE" "$PROMPT_FILE" "")"
|
|
365
|
+
fi
|
|
309
366
|
else
|
|
310
367
|
# Session gone — fall back to details
|
|
311
368
|
MESSAGE_TEXT="$(build_message "details" "$TASK_HASH" "$STATE_FILE" "$PROMPT_FILE" "")"
|
|
312
369
|
fi
|
|
313
370
|
fi
|
|
314
371
|
|
|
372
|
+
# Write handshake file for summary polling (if applicable)
|
|
373
|
+
if [[ -n "$MESSAGE_TEXT" && -n "$TASK_HASH" ]]; then
|
|
374
|
+
echo "$MESSAGE_TEXT" > "/tmp/tq-summary-${TASK_HASH}.txt"
|
|
375
|
+
fi
|
|
376
|
+
|
|
315
377
|
# Deliver message if we have one
|
|
316
378
|
if [[ -n "$MESSAGE_TEXT" ]]; then
|
|
317
|
-
|
|
379
|
+
# If no explicit reply-to, check for latest-msg-id (conversation mode)
|
|
380
|
+
if [[ -z "$REPLY_TO_MSG_ID" ]]; then
|
|
381
|
+
LATEST_MSG_FILE="$HOME/.tq/conversations/latest-msg-id"
|
|
382
|
+
if [[ -f "$LATEST_MSG_FILE" ]]; then
|
|
383
|
+
REPLY_TO_MSG_ID="$(cat "$LATEST_MSG_FILE")"
|
|
384
|
+
fi
|
|
385
|
+
fi
|
|
386
|
+
deliver "$SERVICE" "$MESSAGE_TEXT" "$REPLY_TO_MSG_ID"
|
|
318
387
|
fi
|
package/scripts/tq-setup
CHANGED
|
@@ -32,7 +32,7 @@ read -r _IGNORED
|
|
|
32
32
|
|
|
33
33
|
UPDATES="$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?offset=0&limit=10&timeout=0" 2>&1)"
|
|
34
34
|
|
|
35
|
-
DISCOVER_SCRIPT=$(mktemp /tmp/tq-setup-discover-XXXXXX
|
|
35
|
+
DISCOVER_SCRIPT=$(mktemp /tmp/tq-setup-discover-XXXXXX)
|
|
36
36
|
trap 'rm -f "$DISCOVER_SCRIPT"' EXIT
|
|
37
37
|
|
|
38
38
|
cat > "$DISCOVER_SCRIPT" <<'PYEOF'
|