agileflow 3.1.0 → 3.2.0

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +57 -85
  3. package/lib/dashboard-automations.js +130 -0
  4. package/lib/dashboard-git.js +254 -0
  5. package/lib/dashboard-inbox.js +64 -0
  6. package/lib/dashboard-protocol.js +1 -0
  7. package/lib/dashboard-server.js +114 -924
  8. package/lib/dashboard-session.js +136 -0
  9. package/lib/dashboard-status.js +72 -0
  10. package/lib/dashboard-terminal.js +354 -0
  11. package/lib/dashboard-websocket.js +88 -0
  12. package/lib/drivers/codex-driver.ts +4 -4
  13. package/lib/logger.js +106 -0
  14. package/package.json +4 -2
  15. package/scripts/agileflow-configure.js +2 -2
  16. package/scripts/agileflow-welcome.js +409 -434
  17. package/scripts/claude-tmux.sh +80 -2
  18. package/scripts/context-loader.js +4 -9
  19. package/scripts/lib/command-prereqs.js +280 -0
  20. package/scripts/lib/configure-detect.js +92 -2
  21. package/scripts/lib/configure-features.js +295 -1
  22. package/scripts/lib/context-formatter.js +468 -233
  23. package/scripts/lib/context-loader.js +27 -15
  24. package/scripts/lib/damage-control-utils.js +8 -1
  25. package/scripts/lib/feature-catalog.js +321 -0
  26. package/scripts/lib/portable-tasks-cli.js +274 -0
  27. package/scripts/lib/portable-tasks.js +479 -0
  28. package/scripts/lib/signal-detectors.js +1 -1
  29. package/scripts/lib/team-events.js +86 -1
  30. package/scripts/obtain-context.js +28 -4
  31. package/scripts/smart-detect.js +17 -0
  32. package/scripts/strip-ai-attribution.js +63 -0
  33. package/scripts/team-manager.js +7 -2
  34. package/scripts/welcome-deferred.js +437 -0
  35. package/src/core/agents/perf-analyzer-assets.md +174 -0
  36. package/src/core/agents/perf-analyzer-bundle.md +165 -0
  37. package/src/core/agents/perf-analyzer-caching.md +160 -0
  38. package/src/core/agents/perf-analyzer-compute.md +165 -0
  39. package/src/core/agents/perf-analyzer-memory.md +182 -0
  40. package/src/core/agents/perf-analyzer-network.md +157 -0
  41. package/src/core/agents/perf-analyzer-queries.md +155 -0
  42. package/src/core/agents/perf-analyzer-rendering.md +156 -0
  43. package/src/core/agents/perf-consensus.md +280 -0
  44. package/src/core/agents/security-analyzer-api.md +199 -0
  45. package/src/core/agents/security-analyzer-auth.md +160 -0
  46. package/src/core/agents/security-analyzer-authz.md +168 -0
  47. package/src/core/agents/security-analyzer-deps.md +147 -0
  48. package/src/core/agents/security-analyzer-infra.md +176 -0
  49. package/src/core/agents/security-analyzer-injection.md +148 -0
  50. package/src/core/agents/security-analyzer-input.md +191 -0
  51. package/src/core/agents/security-analyzer-secrets.md +175 -0
  52. package/src/core/agents/security-consensus.md +276 -0
  53. package/src/core/agents/test-analyzer-assertions.md +181 -0
  54. package/src/core/agents/test-analyzer-coverage.md +183 -0
  55. package/src/core/agents/test-analyzer-fragility.md +185 -0
  56. package/src/core/agents/test-analyzer-integration.md +155 -0
  57. package/src/core/agents/test-analyzer-maintenance.md +173 -0
  58. package/src/core/agents/test-analyzer-mocking.md +178 -0
  59. package/src/core/agents/test-analyzer-patterns.md +189 -0
  60. package/src/core/agents/test-analyzer-structure.md +177 -0
  61. package/src/core/agents/test-consensus.md +294 -0
  62. package/src/core/commands/{legal/audit.md → audit/legal.md} +13 -13
  63. package/src/core/commands/{logic/audit.md → audit/logic.md} +12 -12
  64. package/src/core/commands/audit/performance.md +443 -0
  65. package/src/core/commands/audit/security.md +443 -0
  66. package/src/core/commands/audit/test.md +442 -0
  67. package/src/core/commands/babysit.md +505 -463
  68. package/src/core/commands/configure.md +8 -8
  69. package/src/core/commands/research/ask.md +42 -9
  70. package/src/core/commands/research/import.md +14 -8
  71. package/src/core/commands/research/list.md +17 -16
  72. package/src/core/commands/research/synthesize.md +8 -8
  73. package/src/core/commands/research/view.md +28 -4
  74. package/src/core/commands/whats-new.md +2 -2
  75. package/src/core/experts/devops/expertise.yaml +13 -2
  76. package/src/core/experts/documentation/expertise.yaml +26 -4
  77. package/src/core/profiles/COMPARISON.md +170 -0
  78. package/src/core/profiles/README.md +178 -0
  79. package/src/core/profiles/claude-code.yaml +111 -0
  80. package/src/core/profiles/codex.yaml +103 -0
  81. package/src/core/profiles/cursor.yaml +134 -0
  82. package/src/core/profiles/examples.js +250 -0
  83. package/src/core/profiles/loader.js +235 -0
  84. package/src/core/profiles/windsurf.yaml +159 -0
  85. package/src/core/teams/logic-audit.json +6 -0
  86. package/src/core/teams/perf-audit.json +71 -0
  87. package/src/core/teams/security-audit.json +71 -0
  88. package/src/core/teams/test-audit.json +71 -0
  89. package/src/core/templates/command-prerequisites.yaml +169 -0
  90. package/src/core/templates/damage-control-patterns.yaml +9 -0
  91. package/tools/cli/installers/ide/_base-ide.js +33 -3
  92. package/tools/cli/installers/ide/claude-code.js +2 -69
  93. package/tools/cli/installers/ide/codex.js +9 -9
  94. package/tools/cli/installers/ide/cursor.js +165 -4
  95. package/tools/cli/installers/ide/windsurf.js +237 -6
  96. package/tools/cli/lib/content-transformer.js +234 -9
  97. package/tools/cli/lib/docs-setup.js +1 -1
  98. package/tools/cli/lib/ide-generator.js +357 -0
  99. package/tools/cli/lib/ide-registry.js +2 -2
  100. package/scripts/tmux-task-name.sh +0 -105
  101. package/scripts/tmux-task-watcher.sh +0 -344
@@ -1,105 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tmux-task-name.sh - Rename current tmux window based on task/work description
3
- #
4
- # Called by Claude Code when starting work on a task (via TaskCreate/TaskUpdate).
5
- # Reads the task subject from ~/.claude/tasks/ or accepts it as an argument.
6
- #
7
- # Usage:
8
- # tmux-task-name.sh "Fix auth middleware" # Rename to task subject
9
- # tmux-task-name.sh --scan # Auto-detect from task files
10
- # tmux-task-name.sh --scan --session <UUID> # Scan only one session's tasks
11
- # tmux-task-name.sh --reset # Reset to default "claude-N"
12
- #
13
- # The script is best-effort: silently exits if not inside tmux.
14
-
15
- set -euo pipefail
16
-
17
- # Exit silently if not in tmux
18
- [ -n "${TMUX:-}" ] || exit 0
19
-
20
- MAX_LEN=30
21
-
22
- truncate_name() {
23
- local name="$1"
24
- if [ ${#name} -gt $MAX_LEN ]; then
25
- echo "${name:0:$((MAX_LEN - 1))}…"
26
- else
27
- echo "$name"
28
- fi
29
- }
30
-
31
- # Mode: reset to default sequential name
32
- if [ "${1:-}" = "--reset" ]; then
33
- N=$(( $(tmux list-windows -F '#{window_name}' 2>/dev/null | grep -c '^claude') ))
34
- [ "$N" -eq 0 ] && N=1
35
- tmux rename-window "claude-$N" 2>/dev/null || true
36
- exit 0
37
- fi
38
-
39
- # Mode: scan ~/.claude/tasks/ for most recently modified in-progress task
40
- if [ "${1:-}" = "--scan" ]; then
41
- TASKS_BASE="${HOME}/.claude/tasks"
42
- [ -d "$TASKS_BASE" ] || exit 0
43
-
44
- # Determine session scope: --session param, pane option, or global scan
45
- SESSION_ID=""
46
- if [ "${2:-}" = "--session" ] && [ -n "${3:-}" ]; then
47
- # Validate session ID is alphanumeric + hyphens only (prevent path traversal)
48
- if [[ "$3" =~ ^[a-zA-Z0-9_-]+$ ]]; then
49
- SESSION_ID="$3"
50
- fi
51
- elif [ -n "${TMUX:-}" ]; then
52
- SESSION_ID=$(tmux show-options -pqv @claude_session_id 2>/dev/null || true)
53
- fi
54
-
55
- BEST_SUBJECT=""
56
- BEST_MTIME=0
57
-
58
- if [ -n "$SESSION_ID" ]; then
59
- # Scoped scan: only look at this session's tasks
60
- SCAN_DIR="$TASKS_BASE/$SESSION_ID"
61
- if [ -d "$SCAN_DIR" ]; then
62
- for f in "$SCAN_DIR"/*.json; do
63
- [ -f "$f" ] || continue
64
- status=$(python3 -c "import json,sys; d=json.load(open('$f')); print(d.get('status',''))" 2>/dev/null || echo "")
65
- if [ "$status" = "in_progress" ]; then
66
- mtime=$(stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null || echo "0")
67
- if [ "$mtime" -gt "$BEST_MTIME" ]; then
68
- BEST_MTIME=$mtime
69
- BEST_SUBJECT=$(python3 -c "import json; d=json.load(open('$f')); print(d.get('subject',''))" 2>/dev/null || echo "")
70
- fi
71
- fi
72
- done
73
- fi
74
- else
75
- # Global scan: all sessions (fallback for no session context)
76
- for dir in "$TASKS_BASE"/*/; do
77
- [ -d "$dir" ] || continue
78
- for f in "$dir"*.json; do
79
- [ -f "$f" ] || continue
80
- status=$(python3 -c "import json,sys; d=json.load(open('$f')); print(d.get('status',''))" 2>/dev/null || echo "")
81
- if [ "$status" = "in_progress" ]; then
82
- mtime=$(stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null || echo "0")
83
- if [ "$mtime" -gt "$BEST_MTIME" ]; then
84
- BEST_MTIME=$mtime
85
- BEST_SUBJECT=$(python3 -c "import json; d=json.load(open('$f')); print(d.get('subject',''))" 2>/dev/null || echo "")
86
- fi
87
- fi
88
- done
89
- done
90
- fi
91
-
92
- if [ -n "$BEST_SUBJECT" ]; then
93
- tmux rename-window "$(truncate_name "$BEST_SUBJECT")" 2>/dev/null || true
94
- fi
95
- exit 0
96
- fi
97
-
98
- # Mode: direct - rename to provided argument
99
- if [ -n "${1:-}" ]; then
100
- tmux rename-window "$(truncate_name "$1")" 2>/dev/null || true
101
- exit 0
102
- fi
103
-
104
- echo "Usage: tmux-task-name.sh <task-subject> | --scan | --reset" >&2
105
- exit 1
@@ -1,344 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tmux-task-watcher.sh - Auto-rename tmux window based on Claude Code tasks
3
- #
4
- # Launched automatically by SessionStart hook. Self-backgrounds immediately.
5
- # Polls ~/.claude/tasks/<session-id>/ for in-progress tasks every few seconds.
6
- # Renames the tmux window to match the active task subject.
7
- #
8
- # Usage:
9
- # tmux-task-watcher.sh # Start watcher (backgrounds itself)
10
- # tmux-task-watcher.sh stop # Stop watcher for current pane
11
- #
12
- # Requirements:
13
- # - Must be inside tmux ($TMUX set)
14
- # - Node.js available (for JSON parsing)
15
- # - Claude Code session active ($CLAUDECODE=1)
16
-
17
- set -euo pipefail
18
-
19
- # Only run inside tmux
20
- [ -n "${TMUX:-}" ] || exit 0
21
-
22
- MODE="${1:-start}"
23
- MAX_LEN=30
24
-
25
- # Get current pane ID for per-pane tracking
26
- PANE_ID=$(tmux display-message -p '#{pane_id}' 2>/dev/null || true)
27
- [ -n "$PANE_ID" ] || exit 0
28
-
29
- # PID file keyed by pane to allow multiple watchers (one per window)
30
- SAFE_PANE_ID="${PANE_ID//[^a-zA-Z0-9]/_}"
31
- PID_FILE="/tmp/tmux-task-watcher-${SAFE_PANE_ID}.pid"
32
-
33
- # Claim file: marks which pane owns which session to prevent cross-contamination
34
- CLAIM_DIR="/tmp/tmux-session-claims"
35
-
36
- cleanup_claims() {
37
- [ -d "$CLAIM_DIR" ] || return 0
38
- for cf in "$CLAIM_DIR"/claim-*; do
39
- [ -f "$cf" ] || continue
40
- local claimer
41
- claimer=$(cat "$cf" 2>/dev/null || true)
42
- if [ "$claimer" = "$PANE_ID" ]; then
43
- rm -f "$cf"
44
- fi
45
- done
46
- }
47
-
48
- # --- Stop mode ---
49
- if [ "$MODE" = "stop" ]; then
50
- if [ -f "$PID_FILE" ]; then
51
- kill "$(cat "$PID_FILE")" 2>/dev/null || true
52
- rm "$PID_FILE" 2>/dev/null || true
53
- fi
54
- cleanup_claims
55
- exit 0
56
- fi
57
-
58
- # --- Start mode ---
59
- if [ "$MODE" = "start" ]; then
60
- # Kill any existing watcher for this pane first
61
- if [ -f "$PID_FILE" ]; then
62
- kill "$(cat "$PID_FILE")" 2>/dev/null || true
63
- rm "$PID_FILE" 2>/dev/null || true
64
- fi
65
-
66
- # Self-background: parent exits immediately so hook completes fast
67
- _WATCHER_BG=1 nohup "$0" "_run" >/dev/null 2>&1 &
68
- echo $! > "$PID_FILE"
69
- exit 0
70
- fi
71
-
72
- # --- _run mode: skip to background worker below ---
73
-
74
- # ============================================================
75
- # Background watcher logic (runs in forked process)
76
- # ============================================================
77
-
78
- # Disable strict mode for background worker - we handle errors ourselves
79
- set +eu
80
-
81
- # Derive Claude Code project dir from PWD
82
- CLAUDE_PROJECT_DIR="${HOME}/.claude/projects/$(pwd | sed 's|/|-|g')"
83
-
84
- # --- Session claim helpers ---
85
- mkdir -p "$CLAIM_DIR" 2>/dev/null || true
86
-
87
- claim_session() {
88
- local sid="$1"
89
- echo "$PANE_ID" > "$CLAIM_DIR/claim-${sid}" 2>/dev/null || true
90
- }
91
-
92
- is_claimed_by_other() {
93
- local sid="$1"
94
- local cf="$CLAIM_DIR/claim-${sid}"
95
- [ -f "$cf" ] || return 1 # not claimed
96
- local claimer
97
- claimer=$(cat "$cf" 2>/dev/null || true)
98
- # Empty or unreadable claim file - treat as unclaimed
99
- [ -n "$claimer" ] || return 1
100
- [ "$claimer" != "$PANE_ID" ] || return 1 # claimed by us = not "other"
101
- # Check if claimer pane is still alive
102
- if tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -qF "$claimer"; then
103
- return 0 # claimed by another live pane
104
- fi
105
- # Claimer is dead - remove stale claim
106
- rm -f "$cf"
107
- return 1
108
- }
109
-
110
- # Find Claude PID running inside this tmux pane
111
- find_claude_pid() {
112
- local pane_pid
113
- pane_pid=$(tmux display-message -p -t "$PANE_ID" '#{pane_pid}' 2>/dev/null || true)
114
- [ -n "$pane_pid" ] || return 1
115
-
116
- # Walk descendants of the pane shell looking for claude/claude-code process
117
- # Process tree: pane_shell -> (possibly bash/claude-smart.sh) -> claude
118
- local candidates
119
- candidates=$(ps -eo pid,ppid,comm 2>/dev/null | awk -v root="$pane_pid" '
120
- BEGIN { pids[root]=1 }
121
- { child=$1; parent=$2; comm=$3; children[parent]=children[parent] " " child; comms[child]=comm }
122
- END {
123
- # BFS through process tree
124
- queue[1]=root; qi=1; qn=1
125
- while (qi <= qn) {
126
- p = queue[qi++]
127
- n = split(children[p], kids, " ")
128
- for (i=1; i<=n; i++) {
129
- if (kids[i] != "") {
130
- qn++; queue[qn] = kids[i]
131
- pids[kids[i]] = 1
132
- }
133
- }
134
- }
135
- for (p in pids) {
136
- c = comms[p]
137
- if (c == "claude" || c == "claude-code" || c ~ /^node.*claude/) {
138
- print p
139
- }
140
- }
141
- }
142
- ' || true)
143
-
144
- # Return the first match
145
- echo "$candidates" | head -n1 | tr -d '[:space:]'
146
- }
147
-
148
- # Get process start time (epoch seconds) from /proc
149
- get_pid_start_time() {
150
- local pid="$1"
151
- # /proc/<pid>/stat field 22 is starttime in clock ticks since boot
152
- # Simpler: use stat on /proc/<pid> - ctime ≈ process creation
153
- if [ -d "/proc/$pid" ]; then
154
- stat -c '%Z' "/proc/$pid" 2>/dev/null && return
155
- fi
156
- # Fallback: ps-based start time
157
- ps -o lstart= -p "$pid" 2>/dev/null | xargs -I{} date -d '{}' +%s 2>/dev/null || echo "0"
158
- }
159
-
160
- # --- 3-tier session finder ---
161
- # Tier 1: Check tmux pane option (set by previous run or claude-smart.sh)
162
- # Tier 2: Correlate Claude PID start time with JSONL birth time
163
- # Tier 3: Newest unclaimed JSONL as fallback
164
- find_session_id() {
165
- [ -d "$CLAUDE_PROJECT_DIR" ] || return 1
166
-
167
- # -- Tier 1: Cached pane option --
168
- local cached_id
169
- cached_id=$(tmux show-options -pqv @claude_session_id 2>/dev/null || true)
170
- if [ -n "$cached_id" ]; then
171
- local cached_dir="${HOME}/.claude/tasks/${cached_id}"
172
- if [ -d "$cached_dir" ] || [ -f "$CLAUDE_PROJECT_DIR/${cached_id}.jsonl" ]; then
173
- # Verify not claimed by another live pane
174
- if ! is_claimed_by_other "$cached_id"; then
175
- echo "$cached_id"
176
- return 0
177
- fi
178
- fi
179
- fi
180
- # Also check @claude_uuid (set by claude-smart.sh)
181
- cached_id=$(tmux show-options -pqv @claude_uuid 2>/dev/null || true)
182
- if [ -n "$cached_id" ] && [ -f "$CLAUDE_PROJECT_DIR/${cached_id}.jsonl" ]; then
183
- if ! is_claimed_by_other "$cached_id"; then
184
- echo "$cached_id"
185
- return 0
186
- fi
187
- fi
188
-
189
- # -- Tier 2: PID-to-JSONL time correlation --
190
- local claude_pid
191
- claude_pid=$(find_claude_pid || true)
192
- if [ -n "$claude_pid" ]; then
193
- local pid_start
194
- pid_start=$(get_pid_start_time "$claude_pid" || echo "0")
195
- if [ "$pid_start" -gt 0 ] 2>/dev/null; then
196
- local best_file="" best_delta=999999
197
- for f in "$CLAUDE_PROJECT_DIR"/*.jsonl; do
198
- [ -f "$f" ] || continue
199
- local birth
200
- birth=$(stat -c '%W' "$f" 2>/dev/null || echo "0")
201
- [ "$birth" -gt 0 ] 2>/dev/null || continue
202
- # JSONL should be born AFTER the PID started (within 120s window)
203
- local delta=$(( birth - pid_start ))
204
- if [ "$delta" -ge 0 ] && [ "$delta" -le 120 ] && [ "$delta" -lt "$best_delta" ]; then
205
- best_delta=$delta
206
- best_file=$f
207
- fi
208
- done
209
- if [ -n "$best_file" ]; then
210
- basename "$best_file" .jsonl
211
- return 0
212
- fi
213
- fi
214
- fi
215
-
216
- # -- Tier 3: Newest unclaimed JSONL --
217
- local best_file="" best_birth=0
218
- for f in "$CLAUDE_PROJECT_DIR"/*.jsonl; do
219
- [ -f "$f" ] || continue
220
- local sid
221
- sid=$(basename "$f" .jsonl)
222
- # Skip if claimed by another live pane
223
- if is_claimed_by_other "$sid"; then
224
- continue
225
- fi
226
- local birth
227
- birth=$(stat -c '%W' "$f" 2>/dev/null || echo "0")
228
- if [ "$birth" -gt "$best_birth" ] 2>/dev/null; then
229
- best_birth=$birth
230
- best_file=$f
231
- fi
232
- done
233
- [ -n "$best_file" ] || return 1
234
- basename "$best_file" .jsonl
235
- }
236
-
237
- # Wait briefly for session file to be created/updated
238
- sleep 2
239
-
240
- SESSION_ID=$(find_session_id || true)
241
- if [ -z "$SESSION_ID" ]; then
242
- # Retry after a longer wait
243
- sleep 5
244
- SESSION_ID=$(find_session_id || true)
245
- fi
246
-
247
- # Store session ID and claim it for this pane
248
- if [ -n "$SESSION_ID" ]; then
249
- tmux set-option -p @claude_session_id "$SESSION_ID" 2>/dev/null || true
250
- claim_session "$SESSION_ID"
251
- fi
252
-
253
- # Only set TASKS_DIR if we have a valid session (avoid scanning parent dir)
254
- TASKS_DIR=""
255
- if [ -n "$SESSION_ID" ]; then
256
- TASKS_DIR="${HOME}/.claude/tasks/${SESSION_ID}"
257
- fi
258
- LAST_WINDOW_NAME=""
259
- POLL_INTERVAL=5
260
- MAX_RUNTIME=$((12 * 3600)) # 12 hours safety limit
261
- START_TIME=$(date +%s)
262
-
263
- # Parse task files and find the most recent in-progress task subject
264
- get_active_task() {
265
- [ -d "$TASKS_DIR" ] || return
266
- node -e "
267
- const fs = require('fs'), path = require('path');
268
- const dir = process.argv[1];
269
- let best = { mtime: 0, subject: '' };
270
- try {
271
- for (const f of fs.readdirSync(dir)) {
272
- if (!f.endsWith('.json') || f === '.lock') continue;
273
- const fp = path.join(dir, f);
274
- const data = JSON.parse(fs.readFileSync(fp, 'utf8'));
275
- if (data.status === 'in_progress') {
276
- const mt = fs.statSync(fp).mtimeMs;
277
- if (mt > best.mtime) {
278
- best = { mtime: mt, subject: data.subject || data.activeForm || '' };
279
- }
280
- }
281
- }
282
- } catch(e) {}
283
- if (best.subject) process.stdout.write(best.subject);
284
- " "$TASKS_DIR" 2>/dev/null || true
285
- }
286
-
287
- truncate_name() {
288
- local name="$1"
289
- if [ ${#name} -gt $MAX_LEN ]; then
290
- printf '%s' "${name:0:$((MAX_LEN - 1))}"
291
- else
292
- printf '%s' "$name"
293
- fi
294
- }
295
-
296
- # Main polling loop
297
- while true; do
298
- # Safety timeout
299
- NOW=$(date +%s)
300
- if [ $((NOW - START_TIME)) -gt $MAX_RUNTIME ]; then
301
- break
302
- fi
303
-
304
- # Check if our pane is still alive
305
- if ! tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -qF "$PANE_ID"; then
306
- break
307
- fi
308
-
309
- # Re-validate that our session hasn't been claimed by another pane
310
- if [ -n "$SESSION_ID" ] && is_claimed_by_other "$SESSION_ID"; then
311
- SESSION_ID=""
312
- TASKS_DIR=""
313
- fi
314
-
315
- # If we don't have a session ID yet, retry finding it
316
- if [ -z "$SESSION_ID" ]; then
317
- SESSION_ID=$(find_session_id || true)
318
- if [ -n "$SESSION_ID" ]; then
319
- tmux set-option -p @claude_session_id "$SESSION_ID" 2>/dev/null || true
320
- claim_session "$SESSION_ID"
321
- TASKS_DIR="${HOME}/.claude/tasks/${SESSION_ID}"
322
- fi
323
- fi
324
-
325
- # Get active task subject (only if we have a valid session)
326
- ACTIVE_TASK=""
327
- if [ -n "$TASKS_DIR" ]; then
328
- ACTIVE_TASK=$(get_active_task 2>/dev/null || true)
329
- fi
330
-
331
- if [ -n "$ACTIVE_TASK" ]; then
332
- NEW_NAME=$(truncate_name "$ACTIVE_TASK")
333
- if [ "$NEW_NAME" != "$LAST_WINDOW_NAME" ]; then
334
- tmux rename-window -t "$PANE_ID" "$NEW_NAME" 2>/dev/null || true
335
- LAST_WINDOW_NAME="$NEW_NAME"
336
- fi
337
- fi
338
-
339
- sleep "$POLL_INTERVAL"
340
- done
341
-
342
- # Cleanup
343
- cleanup_claims
344
- rm -f "$PID_FILE"