agileflow 2.99.8 → 3.0.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.
- package/CHANGELOG.md +5 -0
- 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 +184 -133
- 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/auto-self-improve.js +3 -3
- package/scripts/claude-smart.sh +67 -0
- package/scripts/claude-tmux.sh +248 -161
- package/scripts/damage-control-multi-agent.js +227 -0
- package/scripts/lib/bus-utils.js +471 -0
- package/scripts/lib/configure-detect.js +5 -6
- package/scripts/lib/configure-features.js +44 -0
- 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/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 +105 -2
- 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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# claude-smart.sh - Smart resume wrapper for Claude in tmux
|
|
3
|
+
#
|
|
4
|
+
# Uses per-pane tmux option @claude_uuid to track conversation identity.
|
|
5
|
+
# New windows start fresh; re-running in the same pane resumes the conversation.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# claude-smart.sh # Resume if UUID stored, else fresh
|
|
9
|
+
# claude-smart.sh --fresh # Force fresh start (ignore stored UUID)
|
|
10
|
+
# claude-smart.sh [flags...] # Pass-through flags to claude
|
|
11
|
+
#
|
|
12
|
+
# NO set -e: UUID capture must run even after Ctrl+C / non-zero exit
|
|
13
|
+
|
|
14
|
+
FRESH=false
|
|
15
|
+
ARGS=()
|
|
16
|
+
|
|
17
|
+
for arg in "$@"; do
|
|
18
|
+
case $arg in
|
|
19
|
+
--fresh)
|
|
20
|
+
FRESH=true
|
|
21
|
+
;;
|
|
22
|
+
*)
|
|
23
|
+
ARGS+=("$arg")
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# ── Resolve conversation UUID ──────────────────────────────────────────────
|
|
29
|
+
STORED_UUID=""
|
|
30
|
+
if [ "$FRESH" = false ] && [ -n "$TMUX" ]; then
|
|
31
|
+
STORED_UUID=$(tmux show-options -pqv @claude_uuid 2>/dev/null || true)
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Validate: the .jsonl file must still exist
|
|
35
|
+
if [ -n "$STORED_UUID" ]; then
|
|
36
|
+
PROJ_DIR=$(pwd | sed 's|/|-|g' | sed 's|^-||')
|
|
37
|
+
SESSIONS_DIR="$HOME/.claude/projects/-$PROJ_DIR"
|
|
38
|
+
if [ ! -f "$SESSIONS_DIR/$STORED_UUID.jsonl" ]; then
|
|
39
|
+
STORED_UUID="" # File gone, start fresh
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ── Build and run Claude command ───────────────────────────────────────────
|
|
44
|
+
CMD=(claude)
|
|
45
|
+
if [ -n "$STORED_UUID" ]; then
|
|
46
|
+
CMD+=(--resume "$STORED_UUID")
|
|
47
|
+
fi
|
|
48
|
+
CMD+=("${ARGS[@]}")
|
|
49
|
+
|
|
50
|
+
"${CMD[@]}"
|
|
51
|
+
EXIT_CODE=$?
|
|
52
|
+
|
|
53
|
+
# ── Capture UUID after exit ────────────────────────────────────────────────
|
|
54
|
+
# Store the most recent non-agent .jsonl as the pane's conversation UUID
|
|
55
|
+
if [ -n "$TMUX" ]; then
|
|
56
|
+
PROJ_DIR=${PROJ_DIR:-$(pwd | sed 's|/|-|g' | sed 's|^-||')}
|
|
57
|
+
SESSIONS_DIR=${SESSIONS_DIR:-"$HOME/.claude/projects/-$PROJ_DIR"}
|
|
58
|
+
if [ -d "$SESSIONS_DIR" ]; then
|
|
59
|
+
NEWEST=$(ls -t "$SESSIONS_DIR"/*.jsonl 2>/dev/null | grep -v "agent-" | head -1)
|
|
60
|
+
if [ -n "$NEWEST" ]; then
|
|
61
|
+
NEW_UUID=$(basename "$NEWEST" .jsonl)
|
|
62
|
+
tmux set-option -p @claude_uuid "$NEW_UUID" 2>/dev/null || true
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
exit $EXIT_CODE
|
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 keybind cheat sheet in a popup
|
|
256
|
+
tmux bind-key -n M-h display-popup -E -w 52 -h 24 "\
|
|
257
|
+
printf '\\n';\
|
|
258
|
+
printf ' \\033[1;38;5;208mSESSIONS\\033[0m\\n';\
|
|
259
|
+
printf ' Alt+s New Claude window\\n';\
|
|
260
|
+
printf ' Alt+l Switch session\\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+1-9 Switch to window\\n';\
|
|
265
|
+
printf ' Alt+c New empty window\\n';\
|
|
266
|
+
printf ' Alt+n/p Next / previous\\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+←→↑↓ Navigate\\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×2)\\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,59 @@ 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
|
+
# No detached session found — create a new one
|
|
195
406
|
SESSION_NAME="$SESSION_BASE"
|
|
196
407
|
SESSION_NUM=1
|
|
197
408
|
while tmux has-session -t "$SESSION_NAME" 2>/dev/null; do
|
|
@@ -265,12 +476,6 @@ if [ -f "$METADATA_FILE" ]; then
|
|
|
265
476
|
fi
|
|
266
477
|
fi
|
|
267
478
|
|
|
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
479
|
# Check if tmux is available
|
|
275
480
|
if ! command -v tmux &> /dev/null; then
|
|
276
481
|
echo "tmux not found. Running claude without tmux."
|
|
@@ -281,118 +486,6 @@ if ! command -v tmux &> /dev/null; then
|
|
|
281
486
|
exec claude "$@"
|
|
282
487
|
fi
|
|
283
488
|
|
|
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
489
|
# Create new tmux session with Claude
|
|
397
490
|
echo "Starting Claude in tmux session: $SESSION_NAME"
|
|
398
491
|
|
|
@@ -408,33 +501,27 @@ tmux move-window -t "$SESSION_NAME":1 >/dev/null 2>&1 || true
|
|
|
408
501
|
# Apply tmux configuration
|
|
409
502
|
configure_tmux_session "$SESSION_NAME"
|
|
410
503
|
|
|
411
|
-
#
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
# Check for inherited session flags (set by parent Claude session)
|
|
415
|
-
INHERITED_FLAGS=""
|
|
504
|
+
# Export scripts directory to tmux session environment (used by keybinds)
|
|
505
|
+
tmux set-environment -t "$SESSION_NAME" AGILEFLOW_SCRIPTS "$SCRIPT_DIR"
|
|
416
506
|
if [ -n "$CLAUDE_SESSION_FLAGS" ]; then
|
|
417
|
-
|
|
507
|
+
tmux set-environment -t "$SESSION_NAME" CLAUDE_SESSION_FLAGS "$CLAUDE_SESSION_FLAGS"
|
|
418
508
|
fi
|
|
419
509
|
|
|
510
|
+
# Pre-seed @claude_uuid on initial pane if we found a recent conversation
|
|
420
511
|
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"
|
|
512
|
+
tmux set-option -p -t "$SESSION_NAME" @claude_uuid "$RESUME_SESSION_ID" 2>/dev/null || true
|
|
426
513
|
fi
|
|
427
514
|
|
|
428
|
-
#
|
|
429
|
-
|
|
430
|
-
|
|
515
|
+
# Launch Claude via smart wrapper (handles resume from @claude_uuid)
|
|
516
|
+
SMART_CMD="\"$SCRIPT_DIR/claude-smart.sh\""
|
|
517
|
+
if [ -n "$CLAUDE_SESSION_FLAGS" ]; then
|
|
518
|
+
SMART_CMD="$SMART_CMD $CLAUDE_SESSION_FLAGS"
|
|
431
519
|
fi
|
|
432
|
-
|
|
433
520
|
if [ $# -gt 0 ]; then
|
|
434
|
-
|
|
435
|
-
CLAUDE_CMD="$CLAUDE_CMD $*"
|
|
521
|
+
SMART_CMD="$SMART_CMD $*"
|
|
436
522
|
fi
|
|
437
|
-
|
|
523
|
+
|
|
524
|
+
tmux send-keys -t "$SESSION_NAME" "$SMART_CMD" Enter
|
|
438
525
|
|
|
439
526
|
# Attach to the session
|
|
440
527
|
exec tmux attach-session -t "$SESSION_NAME"
|