agileflow 2.99.8 → 3.0.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/CHANGELOG.md +10 -0
- package/README.md +3 -3
- package/lib/cache-provider.js +155 -0
- package/lib/codebase-indexer.js +1 -1
- package/lib/content-sanitizer.js +1 -0
- package/lib/dashboard-protocol.js +25 -0
- package/lib/dashboard-server.js +282 -150
- package/lib/errors.js +18 -0
- package/lib/file-cache.js +1 -1
- package/lib/flag-detection.js +11 -20
- package/lib/git-operations.js +15 -33
- package/lib/merge-operations.js +40 -34
- package/lib/process-executor.js +199 -0
- package/lib/registry-cache.js +13 -47
- package/lib/skill-loader.js +206 -0
- package/lib/smart-json-file.js +2 -4
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +13 -12
- package/scripts/agileflow-statusline.sh +30 -0
- package/scripts/agileflow-welcome.js +181 -212
- package/scripts/archive-completed-stories.sh +3 -0
- package/scripts/auto-self-improve.js +3 -3
- package/scripts/ci-summary.js +294 -0
- package/scripts/claude-smart.sh +85 -0
- package/scripts/claude-tmux.sh +272 -161
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +87 -10
- package/scripts/lib/configure-features.js +110 -4
- package/scripts/lib/configure-repair.js +5 -6
- package/scripts/lib/configure-utils.js +2 -3
- package/scripts/lib/context-formatter.js +87 -8
- package/scripts/lib/damage-control-utils.js +37 -3
- package/scripts/lib/file-lock.js +392 -0
- package/scripts/lib/ideation-index.js +2 -5
- package/scripts/lib/lifecycle-detector.js +123 -0
- package/scripts/lib/process-cleanup.js +55 -81
- package/scripts/lib/scale-detector.js +357 -0
- package/scripts/lib/signal-detectors.js +779 -0
- package/scripts/lib/story-state-machine.js +1 -1
- package/scripts/lib/sync-ideation-status.js +2 -3
- package/scripts/lib/task-registry.js +7 -1
- package/scripts/lib/team-events.js +357 -0
- package/scripts/messaging-bridge.js +79 -36
- package/scripts/migrate-ideation-index.js +37 -14
- package/scripts/obtain-context.js +37 -19
- package/scripts/precompact-context.sh +3 -0
- package/scripts/ralph-loop.js +3 -4
- package/scripts/smart-detect.js +390 -0
- package/scripts/team-manager.js +174 -30
- package/src/core/commands/audit.md +13 -11
- package/src/core/commands/babysit.md +162 -115
- package/src/core/commands/changelog.md +21 -4
- package/src/core/commands/configure.md +141 -21
- package/src/core/commands/debt.md +12 -2
- package/src/core/commands/feedback.md +7 -6
- package/src/core/commands/ideate/history.md +1 -1
- package/src/core/commands/ideate/new.md +5 -5
- package/src/core/commands/logic/audit.md +2 -2
- package/src/core/commands/pr.md +7 -6
- package/src/core/commands/research/analyze.md +28 -20
- package/src/core/commands/research/ask.md +43 -0
- package/src/core/commands/research/import.md +29 -21
- package/src/core/commands/research/list.md +8 -7
- package/src/core/commands/research/synthesize.md +356 -20
- package/src/core/commands/research/view.md +8 -5
- package/src/core/commands/review.md +24 -6
- package/src/core/commands/skill/create.md +34 -0
- package/tools/cli/lib/docs-setup.js +4 -0
package/scripts/claude-tmux.sh
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# claude-tmux.sh - Wrapper script that auto-starts Claude Code in a tmux session
|
|
3
3
|
#
|
|
4
4
|
# Usage:
|
|
5
|
-
# ./claude-tmux.sh #
|
|
6
|
-
# ./claude-tmux.sh --
|
|
5
|
+
# ./claude-tmux.sh # Reattach to detached session, or create new
|
|
6
|
+
# ./claude-tmux.sh --new # Force create a new session
|
|
7
|
+
# ./claude-tmux.sh --attach # Reattach to most recent session (any state)
|
|
7
8
|
# ./claude-tmux.sh --no-tmux # Start without tmux (regular claude)
|
|
8
9
|
# ./claude-tmux.sh -n # Same as --no-tmux
|
|
9
10
|
# ./claude-tmux.sh --kill # Kill ALL sessions for this directory
|
|
@@ -11,11 +12,12 @@
|
|
|
11
12
|
# ./claude-tmux.sh --help # Show help with keybinds
|
|
12
13
|
#
|
|
13
14
|
# When already in tmux: Just runs claude normally
|
|
14
|
-
# When not in tmux:
|
|
15
|
+
# When not in tmux: Reattaches to a detached session if one exists, otherwise
|
|
16
|
+
# creates a new tmux session. Use --new to always create a fresh session.
|
|
15
17
|
#
|
|
16
18
|
# Multiple terminals can run `af` from the same directory simultaneously.
|
|
17
19
|
# Sessions are named: claude-<dir>, claude-<dir>-2, claude-<dir>-3, etc.
|
|
18
|
-
# Your Claude conversation is preserved via
|
|
20
|
+
# Your Claude conversation is preserved via smart resume (per-pane UUID tracking).
|
|
19
21
|
#
|
|
20
22
|
# SESSION CREATION (while inside tmux):
|
|
21
23
|
# - Alt+N New worktree session (isolated branch + directory)
|
|
@@ -29,11 +31,15 @@
|
|
|
29
31
|
|
|
30
32
|
set -e
|
|
31
33
|
|
|
34
|
+
# Resolve script directory (used for claude-smart.sh and other helpers)
|
|
35
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
36
|
+
|
|
32
37
|
# Parse arguments
|
|
33
38
|
NO_TMUX=false
|
|
34
39
|
KILL_SESSION=false
|
|
35
40
|
SHOW_HELP=false
|
|
36
41
|
ATTACH_ONLY=false
|
|
42
|
+
FORCE_NEW=false
|
|
37
43
|
REFRESH_CONFIG=false
|
|
38
44
|
USE_RESUME=false
|
|
39
45
|
RESUME_SESSION_ID=""
|
|
@@ -48,6 +54,10 @@ for arg in "$@"; do
|
|
|
48
54
|
ATTACH_ONLY=true
|
|
49
55
|
shift
|
|
50
56
|
;;
|
|
57
|
+
--new)
|
|
58
|
+
FORCE_NEW=true
|
|
59
|
+
shift
|
|
60
|
+
;;
|
|
51
61
|
--fresh|-f)
|
|
52
62
|
# Kept for backwards compat, but this is now the default behavior
|
|
53
63
|
shift
|
|
@@ -81,38 +91,38 @@ USAGE:
|
|
|
81
91
|
agileflow [options] [claude-args...]
|
|
82
92
|
|
|
83
93
|
OPTIONS:
|
|
84
|
-
--attach, -a Reattach to most recent session
|
|
94
|
+
--attach, -a Reattach to most recent session (attached or detached)
|
|
95
|
+
--new Force create a new session (skip auto-reattach)
|
|
85
96
|
--no-tmux, -n Run claude without tmux
|
|
86
97
|
--kill Kill ALL sessions for this directory
|
|
87
98
|
--refresh Refresh tmux config on all existing sessions
|
|
88
99
|
--help, -h Show this help
|
|
89
100
|
|
|
90
|
-
By default, af
|
|
91
|
-
af
|
|
92
|
-
|
|
101
|
+
By default, af reattaches to a detached session if one exists (so Alt+Q
|
|
102
|
+
then af gets you right back). Use --new to force a new session.
|
|
103
|
+
|
|
104
|
+
SESSIONS:
|
|
105
|
+
Alt+s New Claude window
|
|
106
|
+
Alt+l Switch between sessions (picker)
|
|
107
|
+
Alt+q Detach (run af to reattach)
|
|
93
108
|
|
|
94
|
-
|
|
109
|
+
WINDOWS:
|
|
95
110
|
Alt+1-9 Switch to window N
|
|
96
|
-
Alt+c Create
|
|
111
|
+
Alt+c Create empty window
|
|
97
112
|
Alt+n/p Next/previous window
|
|
98
|
-
Alt+d Split horizontally
|
|
99
|
-
Alt+s Split vertically
|
|
100
|
-
Alt+arrows Navigate panes
|
|
101
|
-
Alt+z Zoom/unzoom pane
|
|
102
|
-
Alt+[ Enter copy mode (scroll)
|
|
103
113
|
Alt+r Rename window
|
|
104
|
-
Alt+x Close pane
|
|
105
114
|
Alt+w Close window
|
|
106
|
-
Alt+q Detach from tmux
|
|
107
115
|
|
|
108
|
-
|
|
109
|
-
Alt+
|
|
110
|
-
Alt+
|
|
116
|
+
PANES:
|
|
117
|
+
Alt+d Split side by side
|
|
118
|
+
Alt+v Split top/bottom
|
|
119
|
+
Alt+arrows Navigate panes
|
|
120
|
+
Alt+z Zoom/unzoom pane
|
|
121
|
+
Alt+x Close pane
|
|
111
122
|
|
|
112
|
-
|
|
113
|
-
Alt+
|
|
114
|
-
Alt+
|
|
115
|
-
Alt+R Respawn pane with fresh shell
|
|
123
|
+
OTHER:
|
|
124
|
+
Alt+[ Scroll mode
|
|
125
|
+
Alt+k Send Ctrl+C twice (unfreeze)
|
|
116
126
|
EOF
|
|
117
127
|
exit 0
|
|
118
128
|
fi
|
|
@@ -145,6 +155,156 @@ if command -v tmux &> /dev/null; then
|
|
|
145
155
|
unset _TMUX_BASE _TMUX_SOCK_DIR
|
|
146
156
|
fi
|
|
147
157
|
|
|
158
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
159
|
+
# TMUX CONFIGURATION FUNCTION — applies theme, keybinds, and status bar
|
|
160
|
+
# Defined early so --refresh can use it before any session logic
|
|
161
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
162
|
+
configure_tmux_session() {
|
|
163
|
+
local target_session="$1"
|
|
164
|
+
|
|
165
|
+
# Enable mouse support
|
|
166
|
+
tmux set-option -t "$target_session" mouse on
|
|
167
|
+
|
|
168
|
+
# Fix colors - proper terminal support
|
|
169
|
+
tmux set-option -t "$target_session" default-terminal "xterm-256color"
|
|
170
|
+
tmux set-option -t "$target_session" -ga terminal-overrides ",xterm-256color:Tc"
|
|
171
|
+
|
|
172
|
+
# ─── Status Bar Styling (2-line) ────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
# Status bar position and refresh
|
|
175
|
+
tmux set-option -t "$target_session" status-position bottom
|
|
176
|
+
# Reduce refresh rate to prevent CPU overhead and freezes (was 5s, now 30s)
|
|
177
|
+
tmux set-option -t "$target_session" status-interval 30
|
|
178
|
+
|
|
179
|
+
# Enable 2-line status bar
|
|
180
|
+
tmux set-option -t "$target_session" status 2
|
|
181
|
+
|
|
182
|
+
# Base styling - Tokyo Night inspired dark theme
|
|
183
|
+
tmux set-option -t "$target_session" status-style "bg=#1a1b26,fg=#a9b1d6"
|
|
184
|
+
|
|
185
|
+
# Line 0 (top): Session name + live git branch + keybind hints
|
|
186
|
+
# Uses #() for live branch updates (runs on status-interval, every 30s)
|
|
187
|
+
tmux set-option -t "$target_session" status-format[0] "#[bg=#1a1b26] #[fg=#e8683a bold]#{s/claude-//:session_name} #[fg=#3b4261]· #[fg=#7aa2f7] #(git -C #{pane_current_path} branch --show-current 2>/dev/null || echo '-')#[align=right]#[fg=#565a6e]Alt+h help "
|
|
188
|
+
|
|
189
|
+
# Line 1 (bottom): Window tabs with smart truncation and brand color
|
|
190
|
+
tmux set-option -t "$target_session" status-format[1] "#[bg=#1a1b26]#{W:#{?window_active,#[fg=#1a1b26 bg=#e8683a bold] #I #[fg=#e8683a bg=#2d2f3a]#[fg=#e0e0e0] #{=15:window_name} #[bg=#1a1b26 fg=#2d2f3a],#[fg=#8a8a8a] #I:#{=|8|...:window_name} }}"
|
|
191
|
+
|
|
192
|
+
# Pane border styling - blue inactive, orange active
|
|
193
|
+
tmux set-option -t "$target_session" pane-border-style "fg=#3d59a1"
|
|
194
|
+
tmux set-option -t "$target_session" pane-active-border-style "fg=#e8683a"
|
|
195
|
+
|
|
196
|
+
# Message styling - orange highlight
|
|
197
|
+
tmux set-option -t "$target_session" message-style "bg=#e8683a,fg=#1a1b26,bold"
|
|
198
|
+
|
|
199
|
+
# ─── Keybindings ────────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
# Alt+number to switch windows (1-9)
|
|
202
|
+
for i in 1 2 3 4 5 6 7 8 9; do
|
|
203
|
+
tmux bind-key -n "M-$i" select-window -t ":$i"
|
|
204
|
+
done
|
|
205
|
+
|
|
206
|
+
# Alt+c to create new window
|
|
207
|
+
tmux bind-key -n M-c new-window -c "#{pane_current_path}"
|
|
208
|
+
|
|
209
|
+
# Alt+q to detach
|
|
210
|
+
tmux bind-key -n M-q detach-client
|
|
211
|
+
|
|
212
|
+
# Alt+l to list and switch between sessions (interactive picker)
|
|
213
|
+
tmux bind-key -n M-l choose-tree -s -Z
|
|
214
|
+
|
|
215
|
+
# Alt+d to split horizontally (side by side)
|
|
216
|
+
tmux bind-key -n M-d split-window -h -c "#{pane_current_path}"
|
|
217
|
+
|
|
218
|
+
# Alt+v to split vertically (top/bottom)
|
|
219
|
+
tmux bind-key -n M-v split-window -v -c "#{pane_current_path}"
|
|
220
|
+
|
|
221
|
+
# Alt+arrow to navigate panes
|
|
222
|
+
tmux bind-key -n M-Left select-pane -L
|
|
223
|
+
tmux bind-key -n M-Right select-pane -R
|
|
224
|
+
tmux bind-key -n M-Up select-pane -U
|
|
225
|
+
tmux bind-key -n M-Down select-pane -D
|
|
226
|
+
|
|
227
|
+
# Alt+x to close current pane (with confirmation)
|
|
228
|
+
tmux bind-key -n M-x confirm-before -p "Close pane? (y/n)" kill-pane
|
|
229
|
+
|
|
230
|
+
# Alt+w to close current window (with confirmation)
|
|
231
|
+
tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" kill-window
|
|
232
|
+
|
|
233
|
+
# Alt+n/p for next/previous window
|
|
234
|
+
tmux bind-key -n M-n next-window
|
|
235
|
+
tmux bind-key -n M-p previous-window
|
|
236
|
+
|
|
237
|
+
# Alt+r to rename window
|
|
238
|
+
tmux bind-key -n M-r command-prompt -I "#W" "rename-window '%%'"
|
|
239
|
+
|
|
240
|
+
# Alt+z to zoom/unzoom pane (fullscreen toggle)
|
|
241
|
+
tmux bind-key -n M-z resize-pane -Z
|
|
242
|
+
|
|
243
|
+
# Alt+[ to enter copy mode (for scrolling)
|
|
244
|
+
tmux bind-key -n M-[ copy-mode
|
|
245
|
+
|
|
246
|
+
# ─── Session Creation Keybindings ──────────────────────────────────────────
|
|
247
|
+
# Alt+s to create a new Claude window (starts fresh, future re-runs in same pane resume)
|
|
248
|
+
tmux bind-key -n M-s run-shell "tmux new-window -n claude -c '#{pane_current_path}' && tmux send-keys '\"\$AGILEFLOW_SCRIPTS/claude-smart.sh\" --fresh \$CLAUDE_SESSION_FLAGS' Enter"
|
|
249
|
+
|
|
250
|
+
# ─── Freeze Recovery Keybindings ───────────────────────────────────────────
|
|
251
|
+
# Alt+k to send Ctrl+C twice (soft interrupt for frozen processes)
|
|
252
|
+
tmux bind-key -n M-k run-shell "tmux send-keys C-c; sleep 0.5; tmux send-keys C-c"
|
|
253
|
+
|
|
254
|
+
# ─── Help Panel ──────────────────────────────────────────────────────────
|
|
255
|
+
# Alt+h to show all Alt keybindings in a popup
|
|
256
|
+
tmux bind-key -n M-h display-popup -E -w 52 -h 26 "\
|
|
257
|
+
printf '\\n';\
|
|
258
|
+
printf ' \\033[1;38;5;208mSESSIONS\\033[0m\\n';\
|
|
259
|
+
printf ' Alt+s New Claude session\\n';\
|
|
260
|
+
printf ' Alt+l Switch session (picker)\\n';\
|
|
261
|
+
printf ' Alt+q Detach (af to resume)\\n';\
|
|
262
|
+
printf '\\n';\
|
|
263
|
+
printf ' \\033[1;38;5;208mWINDOWS\\033[0m\\n';\
|
|
264
|
+
printf ' Alt+c New empty window\\n';\
|
|
265
|
+
printf ' Alt+1-9 Switch to window N\\n';\
|
|
266
|
+
printf ' Alt+n/p Next / previous window\\n';\
|
|
267
|
+
printf ' Alt+r Rename window\\n';\
|
|
268
|
+
printf ' Alt+w Close window\\n';\
|
|
269
|
+
printf '\\n';\
|
|
270
|
+
printf ' \\033[1;38;5;208mPANES\\033[0m\\n';\
|
|
271
|
+
printf ' Alt+d Split side by side\\n';\
|
|
272
|
+
printf ' Alt+v Split top / bottom\\n';\
|
|
273
|
+
printf ' Alt+arrows Navigate panes\\n';\
|
|
274
|
+
printf ' Alt+z Zoom / unzoom\\n';\
|
|
275
|
+
printf ' Alt+x Close pane\\n';\
|
|
276
|
+
printf '\\n';\
|
|
277
|
+
printf ' \\033[1;38;5;208mOTHER\\033[0m\\n';\
|
|
278
|
+
printf ' Alt+[ Scroll mode\\n';\
|
|
279
|
+
printf ' Alt+k Unfreeze (Ctrl+C x2)\\n';\
|
|
280
|
+
printf ' Alt+h This help\\n';\
|
|
281
|
+
printf '\\n';\
|
|
282
|
+
read -n 1 -s -r -p ' Press any key to close'"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Handle --refresh flag — re-apply config to all existing claude-* sessions
|
|
286
|
+
if [ "$REFRESH_CONFIG" = true ]; then
|
|
287
|
+
REFRESHED=0
|
|
288
|
+
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^claude-"); do
|
|
289
|
+
configure_tmux_session "$sid"
|
|
290
|
+
# Ensure AGILEFLOW_SCRIPTS is set (needed by Alt+S keybind)
|
|
291
|
+
tmux set-environment -t "$sid" AGILEFLOW_SCRIPTS "$SCRIPT_DIR" 2>/dev/null || true
|
|
292
|
+
REFRESHED=$((REFRESHED + 1))
|
|
293
|
+
done
|
|
294
|
+
if [ "$REFRESHED" -gt 0 ]; then
|
|
295
|
+
echo "Refreshed config on $REFRESHED session(s)."
|
|
296
|
+
else
|
|
297
|
+
echo "No claude-* sessions found to refresh."
|
|
298
|
+
fi
|
|
299
|
+
exit 0
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
# Check if we're already inside tmux — use smart wrapper instead of session management
|
|
303
|
+
if [ -n "$TMUX" ]; then
|
|
304
|
+
# shellcheck disable=SC2086
|
|
305
|
+
exec "$SCRIPT_DIR/claude-smart.sh" $CLAUDE_SESSION_FLAGS "$@"
|
|
306
|
+
fi
|
|
307
|
+
|
|
148
308
|
# Generate directory name (used for session name patterns)
|
|
149
309
|
DIR_NAME=$(basename "$(pwd)")
|
|
150
310
|
|
|
@@ -190,8 +350,83 @@ if [ "$ATTACH_ONLY" = true ]; then
|
|
|
190
350
|
fi
|
|
191
351
|
fi
|
|
192
352
|
|
|
193
|
-
#
|
|
353
|
+
# ── Auto-cleanup dead sessions ────────────────────────────────────────────
|
|
354
|
+
# Silently remove sessions where all panes have exited (dead/empty shells).
|
|
355
|
+
# This prevents accumulation of orphan sessions over time.
|
|
194
356
|
SESSION_BASE="claude-${DIR_NAME}"
|
|
357
|
+
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^${SESSION_BASE}\(\$\|-[0-9]*\$\)"); do
|
|
358
|
+
# Count alive panes (pane_dead=0 means alive)
|
|
359
|
+
ALIVE=$(tmux list-panes -t "$sid" -F '#{pane_dead}' 2>/dev/null | grep -c '^0$' || true)
|
|
360
|
+
if [ "$ALIVE" = "0" ]; then
|
|
361
|
+
tmux kill-session -t "$sid" 2>/dev/null || true
|
|
362
|
+
fi
|
|
363
|
+
done
|
|
364
|
+
|
|
365
|
+
# ── Auto-reattach to detached session ──────────────────────────────────────
|
|
366
|
+
# When user does Alt+Q (detach) and then runs `af` again, reattach to the
|
|
367
|
+
# existing session instead of creating a new one. This preserves tmux windows,
|
|
368
|
+
# pane layout, and the live Claude chat session.
|
|
369
|
+
# If multiple detached sessions exist, show a picker so user can choose.
|
|
370
|
+
if [ "$FORCE_NEW" = false ]; then
|
|
371
|
+
DETACHED=()
|
|
372
|
+
while IFS= read -r sid; do
|
|
373
|
+
[ -n "$sid" ] && DETACHED+=("$sid")
|
|
374
|
+
done < <(tmux list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null | awk '$2 == "0" {print $1}' | grep "^${SESSION_BASE}\(\$\|-[0-9]*\$\)")
|
|
375
|
+
|
|
376
|
+
if [ "${#DETACHED[@]}" -eq 1 ]; then
|
|
377
|
+
# Single detached session — just reattach
|
|
378
|
+
echo "Reattaching to: ${DETACHED[0]}"
|
|
379
|
+
exec tmux attach-session -t "${DETACHED[0]}"
|
|
380
|
+
elif [ "${#DETACHED[@]}" -gt 1 ]; then
|
|
381
|
+
# Multiple detached sessions — let user pick
|
|
382
|
+
echo ""
|
|
383
|
+
echo " Multiple detached sessions found:"
|
|
384
|
+
echo ""
|
|
385
|
+
i=1
|
|
386
|
+
for sid in "${DETACHED[@]}"; do
|
|
387
|
+
WINS=$(tmux list-windows -t "$sid" -F '#{window_name}' 2>/dev/null | tr '\n' ',' | sed 's/,$//')
|
|
388
|
+
printf " %d) %s (%s)\n" "$i" "$sid" "$WINS"
|
|
389
|
+
i=$((i + 1))
|
|
390
|
+
done
|
|
391
|
+
printf " %d) Create new session\n" "$i"
|
|
392
|
+
echo ""
|
|
393
|
+
printf " Pick [1]: "
|
|
394
|
+
read -r CHOICE
|
|
395
|
+
CHOICE=${CHOICE:-1}
|
|
396
|
+
if [ "$CHOICE" -ge 1 ] 2>/dev/null && [ "$CHOICE" -lt "$i" ] 2>/dev/null; then
|
|
397
|
+
IDX=$((CHOICE - 1))
|
|
398
|
+
echo "Reattaching to: ${DETACHED[$IDX]}"
|
|
399
|
+
exec tmux attach-session -t "${DETACHED[$IDX]}"
|
|
400
|
+
fi
|
|
401
|
+
# Fall through to create new session
|
|
402
|
+
fi
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
# ── Reuse existing attached session ──────────────────────────────────────
|
|
406
|
+
# If a session exists but is attached in another terminal, attach to it
|
|
407
|
+
# as a second client rather than creating a duplicate session (e.g. -2).
|
|
408
|
+
# Use --new to force creating a separate session.
|
|
409
|
+
if [ "$FORCE_NEW" = false ]; then
|
|
410
|
+
EXISTING=()
|
|
411
|
+
while IFS= read -r sid; do
|
|
412
|
+
[ -n "$sid" ] && EXISTING+=("$sid")
|
|
413
|
+
done < <(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^${SESSION_BASE}\(\$\|-[0-9]*\$\)")
|
|
414
|
+
|
|
415
|
+
if [ "${#EXISTING[@]}" -gt 0 ]; then
|
|
416
|
+
# Prefer the base session, otherwise pick the first one
|
|
417
|
+
TARGET="${EXISTING[0]}"
|
|
418
|
+
for sid in "${EXISTING[@]}"; do
|
|
419
|
+
if [ "$sid" = "$SESSION_BASE" ]; then
|
|
420
|
+
TARGET="$sid"
|
|
421
|
+
break
|
|
422
|
+
fi
|
|
423
|
+
done
|
|
424
|
+
echo "Attaching to existing session: $TARGET"
|
|
425
|
+
exec tmux attach-session -t "$TARGET"
|
|
426
|
+
fi
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
# No existing session found — create a new one
|
|
195
430
|
SESSION_NAME="$SESSION_BASE"
|
|
196
431
|
SESSION_NUM=1
|
|
197
432
|
while tmux has-session -t "$SESSION_NAME" 2>/dev/null; do
|
|
@@ -265,12 +500,6 @@ if [ -f "$METADATA_FILE" ]; then
|
|
|
265
500
|
fi
|
|
266
501
|
fi
|
|
267
502
|
|
|
268
|
-
# Check if we're already inside tmux
|
|
269
|
-
if [ -n "$TMUX" ]; then
|
|
270
|
-
# Already in tmux, just run claude
|
|
271
|
-
exec claude "$@"
|
|
272
|
-
fi
|
|
273
|
-
|
|
274
503
|
# Check if tmux is available
|
|
275
504
|
if ! command -v tmux &> /dev/null; then
|
|
276
505
|
echo "tmux not found. Running claude without tmux."
|
|
@@ -281,118 +510,6 @@ if ! command -v tmux &> /dev/null; then
|
|
|
281
510
|
exec claude "$@"
|
|
282
511
|
fi
|
|
283
512
|
|
|
284
|
-
# ══════════════════════════════════════════════════════════════════════════════
|
|
285
|
-
# TMUX CONFIGURATION FUNCTION — applies theme, keybinds, and status bar
|
|
286
|
-
# Extracted so --refresh can re-apply to existing sessions
|
|
287
|
-
# ══════════════════════════════════════════════════════════════════════════════
|
|
288
|
-
configure_tmux_session() {
|
|
289
|
-
local target_session="$1"
|
|
290
|
-
|
|
291
|
-
# Enable mouse support
|
|
292
|
-
tmux set-option -t "$target_session" mouse on
|
|
293
|
-
|
|
294
|
-
# Fix colors - proper terminal support
|
|
295
|
-
tmux set-option -t "$target_session" default-terminal "xterm-256color"
|
|
296
|
-
tmux set-option -t "$target_session" -ga terminal-overrides ",xterm-256color:Tc"
|
|
297
|
-
|
|
298
|
-
# ─── Status Bar Styling (2-line) ────────────────────────────────────────────
|
|
299
|
-
|
|
300
|
-
# Status bar position and refresh
|
|
301
|
-
tmux set-option -t "$target_session" status-position bottom
|
|
302
|
-
# Reduce refresh rate to prevent CPU overhead and freezes (was 5s, now 30s)
|
|
303
|
-
tmux set-option -t "$target_session" status-interval 30
|
|
304
|
-
|
|
305
|
-
# Enable 2-line status bar
|
|
306
|
-
tmux set-option -t "$target_session" status 2
|
|
307
|
-
|
|
308
|
-
# Base styling - Tokyo Night inspired dark theme
|
|
309
|
-
tmux set-option -t "$target_session" status-style "bg=#1a1b26,fg=#a9b1d6"
|
|
310
|
-
|
|
311
|
-
# Capture git branch once (avoids spawning process every refresh)
|
|
312
|
-
local git_branch
|
|
313
|
-
git_branch=$(git branch --show-current 2>/dev/null || echo '-')
|
|
314
|
-
|
|
315
|
-
# Line 0 (top): Session name (stripped of claude- prefix) + Keybinds + Git branch
|
|
316
|
-
tmux set-option -t "$target_session" status-format[0] "#[bg=#1a1b26] #[fg=#e8683a bold]#{s/claude-//:session_name} #[fg=#3b4261]· #[fg=#7aa2f7] ${git_branch} #[align=right]#[fg=#7a7e8a]Alt+1-9 windows Alt+s new session Alt+q detach "
|
|
317
|
-
|
|
318
|
-
# Line 1 (bottom): Window tabs with smart truncation and brand color
|
|
319
|
-
tmux set-option -t "$target_session" status-format[1] "#[bg=#1a1b26]#{W:#{?window_active,#[fg=#1a1b26 bg=#e8683a bold] #I #[fg=#e8683a bg=#2d2f3a]#[fg=#e0e0e0] #{=15:window_name} #[bg=#1a1b26 fg=#2d2f3a],#[fg=#8a8a8a] #I:#{=|8|...:window_name} }}"
|
|
320
|
-
|
|
321
|
-
# Pane border styling - blue inactive, orange active
|
|
322
|
-
tmux set-option -t "$target_session" pane-border-style "fg=#3d59a1"
|
|
323
|
-
tmux set-option -t "$target_session" pane-active-border-style "fg=#e8683a"
|
|
324
|
-
|
|
325
|
-
# Message styling - orange highlight
|
|
326
|
-
tmux set-option -t "$target_session" message-style "bg=#e8683a,fg=#1a1b26,bold"
|
|
327
|
-
|
|
328
|
-
# ─── Keybindings ────────────────────────────────────────────────────────────
|
|
329
|
-
|
|
330
|
-
# Alt+number to switch windows (1-9)
|
|
331
|
-
for i in 1 2 3 4 5 6 7 8 9; do
|
|
332
|
-
tmux bind-key -n "M-$i" select-window -t ":$i"
|
|
333
|
-
done
|
|
334
|
-
|
|
335
|
-
# Alt+c to create new window
|
|
336
|
-
tmux bind-key -n M-c new-window -c "#{pane_current_path}"
|
|
337
|
-
|
|
338
|
-
# Alt+q to detach
|
|
339
|
-
tmux bind-key -n M-q detach-client
|
|
340
|
-
|
|
341
|
-
# Alt+d to split horizontally (side by side)
|
|
342
|
-
tmux bind-key -n M-d split-window -h -c "#{pane_current_path}"
|
|
343
|
-
|
|
344
|
-
# Alt+v to split vertically (top/bottom)
|
|
345
|
-
tmux bind-key -n M-v split-window -v -c "#{pane_current_path}"
|
|
346
|
-
|
|
347
|
-
# Alt+arrow to navigate panes
|
|
348
|
-
tmux bind-key -n M-Left select-pane -L
|
|
349
|
-
tmux bind-key -n M-Right select-pane -R
|
|
350
|
-
tmux bind-key -n M-Up select-pane -U
|
|
351
|
-
tmux bind-key -n M-Down select-pane -D
|
|
352
|
-
|
|
353
|
-
# Alt+x to close current pane (with confirmation)
|
|
354
|
-
tmux bind-key -n M-x confirm-before -p "Close pane? (y/n)" kill-pane
|
|
355
|
-
|
|
356
|
-
# Alt+w to close current window (with confirmation)
|
|
357
|
-
tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" kill-window
|
|
358
|
-
|
|
359
|
-
# Alt+n/p for next/previous window
|
|
360
|
-
tmux bind-key -n M-n next-window
|
|
361
|
-
tmux bind-key -n M-p previous-window
|
|
362
|
-
|
|
363
|
-
# Alt+r to rename window
|
|
364
|
-
tmux bind-key -n M-r command-prompt -I "#W" "rename-window '%%'"
|
|
365
|
-
|
|
366
|
-
# Alt+z to zoom/unzoom pane (fullscreen toggle)
|
|
367
|
-
tmux bind-key -n M-z resize-pane -Z
|
|
368
|
-
|
|
369
|
-
# Alt+[ to enter copy mode (for scrolling)
|
|
370
|
-
tmux bind-key -n M-[ copy-mode
|
|
371
|
-
|
|
372
|
-
# ─── Session Creation Keybindings ──────────────────────────────────────────
|
|
373
|
-
# Alt+s to create a same-directory Claude window
|
|
374
|
-
tmux bind-key -n M-s run-shell "tmux new-window -c '#{pane_current_path}' && tmux send-keys 'claude \$CLAUDE_SESSION_FLAGS' Enter && tmux display-message 'New Claude session created'"
|
|
375
|
-
|
|
376
|
-
# ─── Freeze Recovery Keybindings ───────────────────────────────────────────
|
|
377
|
-
# Alt+k to send Ctrl+C twice (soft interrupt for frozen processes)
|
|
378
|
-
tmux bind-key -n M-k run-shell "tmux send-keys C-c; sleep 0.5; tmux send-keys C-c"
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
# Handle --refresh flag — re-apply config to all existing claude-* sessions
|
|
382
|
-
if [ "$REFRESH_CONFIG" = true ]; then
|
|
383
|
-
REFRESHED=0
|
|
384
|
-
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^claude-"); do
|
|
385
|
-
configure_tmux_session "$sid"
|
|
386
|
-
REFRESHED=$((REFRESHED + 1))
|
|
387
|
-
done
|
|
388
|
-
if [ "$REFRESHED" -gt 0 ]; then
|
|
389
|
-
echo "Refreshed config on $REFRESHED session(s)."
|
|
390
|
-
else
|
|
391
|
-
echo "No claude-* sessions found to refresh."
|
|
392
|
-
fi
|
|
393
|
-
exit 0
|
|
394
|
-
fi
|
|
395
|
-
|
|
396
513
|
# Create new tmux session with Claude
|
|
397
514
|
echo "Starting Claude in tmux session: $SESSION_NAME"
|
|
398
515
|
|
|
@@ -408,33 +525,27 @@ tmux move-window -t "$SESSION_NAME":1 >/dev/null 2>&1 || true
|
|
|
408
525
|
# Apply tmux configuration
|
|
409
526
|
configure_tmux_session "$SESSION_NAME"
|
|
410
527
|
|
|
411
|
-
#
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
# Check for inherited session flags (set by parent Claude session)
|
|
415
|
-
INHERITED_FLAGS=""
|
|
528
|
+
# Export scripts directory to tmux session environment (used by keybinds)
|
|
529
|
+
tmux set-environment -t "$SESSION_NAME" AGILEFLOW_SCRIPTS "$SCRIPT_DIR"
|
|
416
530
|
if [ -n "$CLAUDE_SESSION_FLAGS" ]; then
|
|
417
|
-
|
|
531
|
+
tmux set-environment -t "$SESSION_NAME" CLAUDE_SESSION_FLAGS "$CLAUDE_SESSION_FLAGS"
|
|
418
532
|
fi
|
|
419
533
|
|
|
534
|
+
# Pre-seed @claude_uuid on initial pane if we found a recent conversation
|
|
420
535
|
if [ "$USE_RESUME" = true ] && [ -n "$RESUME_SESSION_ID" ]; then
|
|
421
|
-
|
|
422
|
-
CLAUDE_CMD="claude --resume $RESUME_SESSION_ID"
|
|
423
|
-
elif [ "$USE_RESUME" = true ]; then
|
|
424
|
-
# Fresh restart with conversation picker
|
|
425
|
-
CLAUDE_CMD="claude --resume"
|
|
536
|
+
tmux set-option -p -t "$SESSION_NAME" @claude_uuid "$RESUME_SESSION_ID" 2>/dev/null || true
|
|
426
537
|
fi
|
|
427
538
|
|
|
428
|
-
#
|
|
429
|
-
|
|
430
|
-
|
|
539
|
+
# Launch Claude via smart wrapper (handles resume from @claude_uuid)
|
|
540
|
+
SMART_CMD="\"$SCRIPT_DIR/claude-smart.sh\""
|
|
541
|
+
if [ -n "$CLAUDE_SESSION_FLAGS" ]; then
|
|
542
|
+
SMART_CMD="$SMART_CMD $CLAUDE_SESSION_FLAGS"
|
|
431
543
|
fi
|
|
432
|
-
|
|
433
544
|
if [ $# -gt 0 ]; then
|
|
434
|
-
|
|
435
|
-
CLAUDE_CMD="$CLAUDE_CMD $*"
|
|
545
|
+
SMART_CMD="$SMART_CMD $*"
|
|
436
546
|
fi
|
|
437
|
-
|
|
547
|
+
|
|
548
|
+
tmux send-keys -t "$SESSION_NAME" "$SMART_CMD" Enter
|
|
438
549
|
|
|
439
550
|
# Attach to the session
|
|
440
551
|
exec tmux attach-session -t "$SESSION_NAME"
|