agileflow 2.99.0 → 2.99.2
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/dashboard-protocol.js +38 -0
- package/lib/dashboard-server.js +197 -7
- package/lib/feedback.js +36 -9
- package/lib/git-operations.js +4 -1
- package/lib/merge-operations.js +25 -0
- package/lib/progress.js +7 -6
- package/lib/session-operations.js +611 -0
- package/lib/session-switching.js +191 -0
- package/lib/template-loader.js +4 -2
- package/lib/worktree-operations.js +5 -25
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +13 -0
- package/scripts/agileflow-welcome.js +11 -6
- package/scripts/batch-pmap-loop.js +11 -4
- package/scripts/claude-tmux.sh +186 -103
- package/scripts/damage-control-bash.js +33 -3
- package/scripts/damage-control-edit.js +33 -3
- package/scripts/damage-control-write.js +33 -3
- package/scripts/lib/configure-features.js +10 -7
- package/scripts/lib/configure-repair.js +12 -2
- package/scripts/lib/process-cleanup.js +197 -15
- package/scripts/obtain-context.js +5 -0
- package/scripts/session-manager.js +156 -932
- package/scripts/spawn-parallel.js +15 -11
- package/src/core/agents/configuration/archival.md +2 -1
- package/src/core/agents/configuration/attribution.md +2 -1
- package/src/core/agents/configuration/ci.md +2 -1
- package/src/core/agents/configuration/damage-control.md +2 -1
- package/src/core/agents/configuration/git-config.md +2 -1
- package/src/core/agents/configuration/hooks.md +2 -1
- package/src/core/agents/configuration/precompact.md +2 -1
- package/src/core/agents/configuration/status-line.md +2 -1
- package/src/core/agents/configuration/verify.md +2 -1
- package/src/core/commands/adr/list.md +1 -1
- package/src/core/commands/adr/update.md +1 -1
- package/src/core/commands/adr/view.md +1 -1
- package/src/core/commands/adr.md +1 -1
- package/src/core/commands/agent.md +1 -1
- package/src/core/commands/api.md +1 -1
- package/src/core/commands/assign.md +1 -1
- package/src/core/commands/audit.md +1 -1
- package/src/core/commands/auto.md +1 -1
- package/src/core/commands/automate.md +1 -1
- package/src/core/commands/babysit.md +1 -1
- package/src/core/commands/baseline.md +1 -1
- package/src/core/commands/batch.md +1 -1
- package/src/core/commands/blockers.md +1 -1
- package/src/core/commands/board.md +1 -1
- package/src/core/commands/changelog.md +1 -1
- package/src/core/commands/choose.md +1 -1
- package/src/core/commands/ci.md +1 -1
- package/src/core/commands/compress.md +1 -1
- package/src/core/commands/configure.md +56 -1
- package/src/core/commands/context/export.md +1 -1
- package/src/core/commands/context/full.md +1 -1
- package/src/core/commands/context/note.md +1 -1
- package/src/core/commands/council.md +1 -1
- package/src/core/commands/debt.md +1 -1
- package/src/core/commands/deploy.md +1 -1
- package/src/core/commands/deps.md +1 -1
- package/src/core/commands/diagnose.md +1 -1
- package/src/core/commands/docs.md +1 -1
- package/src/core/commands/epic/list.md +1 -1
- package/src/core/commands/epic/view.md +1 -1
- package/src/core/commands/epic.md +1 -1
- package/src/core/commands/feedback.md +1 -1
- package/src/core/commands/handoff.md +1 -1
- package/src/core/commands/help.md +4 -190
- package/src/core/commands/ideate/history.md +1 -1
- package/src/core/commands/ideate/new.md +1 -1
- package/src/core/commands/impact.md +1 -1
- package/src/core/commands/install.md +1 -1
- package/src/core/commands/logic/audit.md +1 -1
- package/src/core/commands/maintain.md +1 -1
- package/src/core/commands/metrics.md +1 -1
- package/src/core/commands/multi-expert.md +1 -1
- package/src/core/commands/packages.md +1 -1
- package/src/core/commands/pr.md +1 -1
- package/src/core/commands/readme-sync.md +1 -1
- package/src/core/commands/research/analyze.md +1 -1
- package/src/core/commands/research/ask.md +1 -1
- package/src/core/commands/research/import.md +1 -1
- package/src/core/commands/research/list.md +1 -1
- package/src/core/commands/research/synthesize.md +1 -1
- package/src/core/commands/research/view.md +1 -1
- package/src/core/commands/retro.md +1 -1
- package/src/core/commands/review.md +1 -1
- package/src/core/commands/rlm.md +1 -1
- package/src/core/commands/roadmap/analyze.md +1 -1
- package/src/core/commands/rpi.md +1 -1
- package/src/core/commands/serve.md +127 -0
- package/src/core/commands/session/cleanup.md +1 -1
- package/src/core/commands/session/end.md +84 -23
- package/src/core/commands/session/history.md +1 -1
- package/src/core/commands/session/init.md +1 -1
- package/src/core/commands/session/new.md +198 -84
- package/src/core/commands/session/resume.md +1 -1
- package/src/core/commands/session/spawn.md +1 -1
- package/src/core/commands/session/status.md +1 -1
- package/src/core/commands/skill/create.md +1 -1
- package/src/core/commands/skill/delete.md +1 -1
- package/src/core/commands/skill/edit.md +1 -1
- package/src/core/commands/skill/list.md +1 -1
- package/src/core/commands/skill/test.md +1 -1
- package/src/core/commands/skill/upgrade.md +1 -1
- package/src/core/commands/sprint.md +1 -1
- package/src/core/commands/status.md +1 -1
- package/src/core/commands/story/list.md +1 -1
- package/src/core/commands/story/view.md +1 -1
- package/src/core/commands/story-validate.md +1 -1
- package/src/core/commands/story.md +1 -1
- package/src/core/commands/team/list.md +1 -1
- package/src/core/commands/team/start.md +1 -1
- package/src/core/commands/team/status.md +1 -1
- package/src/core/commands/team/stop.md +1 -1
- package/src/core/commands/template.md +1 -1
- package/src/core/commands/tests.md +1 -1
- package/src/core/commands/update.md +1 -1
- package/src/core/commands/validate-expertise.md +1 -1
- package/src/core/commands/velocity.md +1 -1
- package/src/core/commands/verify.md +1 -1
- package/src/core/commands/whats-new.md +1 -1
- package/src/core/commands/workflow.md +1 -1
- package/tools/cli/installers/ide/codex.js +12 -4
- package/tools/cli/lib/content-injector.js +23 -4
package/scripts/claude-tmux.sh
CHANGED
|
@@ -2,19 +2,24 @@
|
|
|
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 --attach # Reattach to
|
|
5
|
+
# ./claude-tmux.sh # Create new tmux session (supports multiple from same dir)
|
|
6
|
+
# ./claude-tmux.sh --attach # Reattach to most recent session
|
|
7
7
|
# ./claude-tmux.sh --no-tmux # Start without tmux (regular claude)
|
|
8
8
|
# ./claude-tmux.sh -n # Same as --no-tmux
|
|
9
|
-
# ./claude-tmux.sh --kill # Kill
|
|
9
|
+
# ./claude-tmux.sh --kill # Kill ALL sessions for this directory
|
|
10
|
+
# ./claude-tmux.sh --refresh # Refresh tmux config on all existing sessions
|
|
10
11
|
# ./claude-tmux.sh --help # Show help with keybinds
|
|
11
12
|
#
|
|
12
13
|
# When already in tmux: Just runs claude normally
|
|
13
14
|
# When not in tmux: Creates a tmux session and runs claude inside it
|
|
14
15
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# preserved via --resume regardless of
|
|
16
|
+
# Multiple terminals can run `af` from the same directory simultaneously.
|
|
17
|
+
# Sessions are named: claude-<dir>, claude-<dir>-2, claude-<dir>-3, etc.
|
|
18
|
+
# Your Claude conversation is preserved via --resume regardless of tmux state.
|
|
19
|
+
#
|
|
20
|
+
# SESSION CREATION (while inside tmux):
|
|
21
|
+
# - Alt+N New worktree session (isolated branch + directory)
|
|
22
|
+
# - Alt+S Same-directory session (quick, no worktree)
|
|
18
23
|
#
|
|
19
24
|
# FREEZE RECOVERY (while inside tmux):
|
|
20
25
|
# - Alt+k Send Ctrl+C twice (soft interrupt)
|
|
@@ -29,6 +34,7 @@ NO_TMUX=false
|
|
|
29
34
|
KILL_SESSION=false
|
|
30
35
|
SHOW_HELP=false
|
|
31
36
|
ATTACH_ONLY=false
|
|
37
|
+
REFRESH_CONFIG=false
|
|
32
38
|
USE_RESUME=false
|
|
33
39
|
RESUME_SESSION_ID=""
|
|
34
40
|
|
|
@@ -54,6 +60,10 @@ for arg in "$@"; do
|
|
|
54
60
|
KILL_SESSION=true
|
|
55
61
|
shift
|
|
56
62
|
;;
|
|
63
|
+
--refresh)
|
|
64
|
+
REFRESH_CONFIG=true
|
|
65
|
+
shift
|
|
66
|
+
;;
|
|
57
67
|
--help|-h)
|
|
58
68
|
SHOW_HELP=true
|
|
59
69
|
shift
|
|
@@ -71,13 +81,15 @@ USAGE:
|
|
|
71
81
|
agileflow [options] [claude-args...]
|
|
72
82
|
|
|
73
83
|
OPTIONS:
|
|
74
|
-
--attach, -a Reattach to
|
|
84
|
+
--attach, -a Reattach to most recent session for this directory
|
|
75
85
|
--no-tmux, -n Run claude without tmux
|
|
76
|
-
--kill Kill
|
|
86
|
+
--kill Kill ALL sessions for this directory
|
|
87
|
+
--refresh Refresh tmux config on all existing sessions
|
|
77
88
|
--help, -h Show this help
|
|
78
89
|
|
|
79
|
-
By default, af
|
|
80
|
-
|
|
90
|
+
By default, af creates a new tmux session. Multiple terminals can run
|
|
91
|
+
af from the same directory simultaneously (sessions: claude-dir,
|
|
92
|
+
claude-dir-2, claude-dir-3, etc.).
|
|
81
93
|
|
|
82
94
|
TMUX KEYBINDS:
|
|
83
95
|
Alt+1-9 Switch to window N
|
|
@@ -93,6 +105,10 @@ TMUX KEYBINDS:
|
|
|
93
105
|
Alt+w Close window
|
|
94
106
|
Alt+q Detach from tmux
|
|
95
107
|
|
|
108
|
+
SESSION CREATION:
|
|
109
|
+
Alt+N New worktree session (isolated branch + directory)
|
|
110
|
+
Alt+S Same-directory session (quick, no worktree)
|
|
111
|
+
|
|
96
112
|
FREEZE RECOVERY:
|
|
97
113
|
Alt+k Send Ctrl+C twice (soft interrupt)
|
|
98
114
|
Alt+K Force kill pane immediately
|
|
@@ -106,38 +122,59 @@ if [ "$NO_TMUX" = true ]; then
|
|
|
106
122
|
exec claude "$@"
|
|
107
123
|
fi
|
|
108
124
|
|
|
109
|
-
# Generate
|
|
125
|
+
# Generate directory name (used for session name patterns)
|
|
110
126
|
DIR_NAME=$(basename "$(pwd)")
|
|
111
|
-
SESSION_NAME="claude-${DIR_NAME}"
|
|
112
127
|
|
|
113
|
-
# Handle --kill flag
|
|
128
|
+
# Handle --kill flag — kill ALL sessions for this directory
|
|
114
129
|
if [ "$KILL_SESSION" = true ]; then
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
SESSION_BASE="claude-${DIR_NAME}"
|
|
131
|
+
KILLED=0
|
|
132
|
+
# Kill exact base session
|
|
133
|
+
if tmux has-session -t "$SESSION_BASE" 2>/dev/null; then
|
|
134
|
+
tmux kill-session -t "$SESSION_BASE" 2>/dev/null || true
|
|
135
|
+
KILLED=$((KILLED + 1))
|
|
136
|
+
fi
|
|
137
|
+
# Kill numbered sessions (claude-dir-2, claude-dir-3, etc.)
|
|
138
|
+
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^${SESSION_BASE}-[0-9]*$"); do
|
|
139
|
+
tmux kill-session -t "$sid" 2>/dev/null || true
|
|
140
|
+
KILLED=$((KILLED + 1))
|
|
141
|
+
done
|
|
142
|
+
if [ "$KILLED" -gt 0 ]; then
|
|
143
|
+
echo "Killed $KILLED session(s) for $DIR_NAME."
|
|
119
144
|
else
|
|
120
|
-
echo "No
|
|
145
|
+
echo "No sessions found for '$DIR_NAME'."
|
|
121
146
|
fi
|
|
122
147
|
exit 0
|
|
123
148
|
fi
|
|
124
149
|
|
|
125
|
-
# Handle --attach flag (reattach to
|
|
150
|
+
# Handle --attach flag (reattach to most recent session for this directory)
|
|
126
151
|
if [ "$ATTACH_ONLY" = true ]; then
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
152
|
+
SESSION_BASE="claude-${DIR_NAME}"
|
|
153
|
+
# Find the highest-numbered existing session
|
|
154
|
+
LATEST=""
|
|
155
|
+
if tmux has-session -t "$SESSION_BASE" 2>/dev/null; then
|
|
156
|
+
LATEST="$SESSION_BASE"
|
|
157
|
+
fi
|
|
158
|
+
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^${SESSION_BASE}-[0-9]*$" | sort -t- -k3 -n); do
|
|
159
|
+
LATEST="$sid"
|
|
160
|
+
done
|
|
161
|
+
if [ -n "$LATEST" ]; then
|
|
162
|
+
echo "Attaching to session: $LATEST"
|
|
163
|
+
exec tmux attach-session -t "$LATEST"
|
|
130
164
|
else
|
|
131
165
|
echo "No existing session. Creating new one..."
|
|
132
166
|
# Fall through to create new session
|
|
133
167
|
fi
|
|
134
168
|
fi
|
|
135
169
|
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
170
|
+
# Find next available session name (supports multiple from same directory)
|
|
171
|
+
SESSION_BASE="claude-${DIR_NAME}"
|
|
172
|
+
SESSION_NAME="$SESSION_BASE"
|
|
173
|
+
SESSION_NUM=1
|
|
174
|
+
while tmux has-session -t "$SESSION_NAME" 2>/dev/null; do
|
|
175
|
+
SESSION_NUM=$((SESSION_NUM + 1))
|
|
176
|
+
SESSION_NAME="${SESSION_BASE}-${SESSION_NUM}"
|
|
177
|
+
done
|
|
141
178
|
|
|
142
179
|
# Find the most recent conversation to resume
|
|
143
180
|
PROJ_DIR=$(pwd | sed 's|/|-|g' | sed 's|^-||')
|
|
@@ -172,11 +209,31 @@ if [ -f "$METADATA_FILE" ]; then
|
|
|
172
209
|
fi
|
|
173
210
|
|
|
174
211
|
# Check for default Claude flags from metadata (e.g., --dangerously-skip-permissions)
|
|
212
|
+
# Priority: 1) sessions.defaultStartupMode (from /configure), 2) features.claudeFlags
|
|
175
213
|
if [ -f "$METADATA_FILE" ]; then
|
|
176
214
|
META_FLAGS=$(node -e "
|
|
177
215
|
try {
|
|
178
216
|
const meta = JSON.parse(require('fs').readFileSync('$METADATA_FILE', 'utf8'));
|
|
179
|
-
|
|
217
|
+
// Check sessions config first (from /configure)
|
|
218
|
+
const mode = meta.sessions?.defaultStartupMode;
|
|
219
|
+
if (mode && mode !== 'normal') {
|
|
220
|
+
const modeConfig = meta.sessions?.startupModes?.[mode];
|
|
221
|
+
if (modeConfig?.flags) {
|
|
222
|
+
// Normalize short flags to canonical form
|
|
223
|
+
let flags = modeConfig.flags;
|
|
224
|
+
if (flags === '--dangerous') flags = '--dangerously-skip-permissions';
|
|
225
|
+
console.log(flags);
|
|
226
|
+
process.exit(0);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Fallback to claudeFlags feature
|
|
230
|
+
if (meta.features?.claudeFlags?.enabled) {
|
|
231
|
+
let flags = meta.features.claudeFlags.defaultFlags || '';
|
|
232
|
+
if (flags === '--dangerous') flags = '--dangerously-skip-permissions';
|
|
233
|
+
console.log(flags);
|
|
234
|
+
} else {
|
|
235
|
+
console.log('');
|
|
236
|
+
}
|
|
180
237
|
} catch(e) { console.log(''); }
|
|
181
238
|
" 2>/dev/null || echo "")
|
|
182
239
|
if [ -n "$META_FLAGS" ]; then
|
|
@@ -201,112 +258,138 @@ if ! command -v tmux &> /dev/null; then
|
|
|
201
258
|
exec claude "$@"
|
|
202
259
|
fi
|
|
203
260
|
|
|
204
|
-
#
|
|
205
|
-
|
|
261
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
262
|
+
# TMUX CONFIGURATION FUNCTION — applies theme, keybinds, and status bar
|
|
263
|
+
# Extracted so --refresh can re-apply to existing sessions
|
|
264
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
265
|
+
configure_tmux_session() {
|
|
266
|
+
local target_session="$1"
|
|
206
267
|
|
|
207
|
-
#
|
|
208
|
-
tmux set-option -
|
|
268
|
+
# Enable mouse support
|
|
269
|
+
tmux set-option -t "$target_session" mouse on
|
|
209
270
|
|
|
210
|
-
#
|
|
211
|
-
tmux
|
|
271
|
+
# Fix colors - proper terminal support
|
|
272
|
+
tmux set-option -t "$target_session" default-terminal "xterm-256color"
|
|
273
|
+
tmux set-option -t "$target_session" -ga terminal-overrides ",xterm-256color:Tc"
|
|
212
274
|
|
|
213
|
-
#
|
|
214
|
-
# TMUX CONFIGURATION - Modern status bar with keybinds
|
|
215
|
-
# ══════════════════════════════════════════════════════════════════════════════
|
|
275
|
+
# ─── Status Bar Styling (2-line) ────────────────────────────────────────────
|
|
216
276
|
|
|
217
|
-
#
|
|
218
|
-
tmux set-option -t "$
|
|
277
|
+
# Status bar position and refresh
|
|
278
|
+
tmux set-option -t "$target_session" status-position bottom
|
|
279
|
+
# Reduce refresh rate to prevent CPU overhead and freezes (was 5s, now 30s)
|
|
280
|
+
tmux set-option -t "$target_session" status-interval 30
|
|
219
281
|
|
|
220
|
-
#
|
|
221
|
-
tmux set-option -t "$
|
|
222
|
-
tmux set-option -t "$SESSION_NAME" -ga terminal-overrides ",xterm-256color:Tc"
|
|
282
|
+
# Enable 2-line status bar
|
|
283
|
+
tmux set-option -t "$target_session" status 2
|
|
223
284
|
|
|
224
|
-
#
|
|
285
|
+
# Base styling - Tokyo Night inspired dark theme
|
|
286
|
+
tmux set-option -t "$target_session" status-style "bg=#1a1b26,fg=#a9b1d6"
|
|
225
287
|
|
|
226
|
-
#
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
tmux set-option -t "$SESSION_NAME" status-interval 30
|
|
288
|
+
# Capture git branch once (avoids spawning process every refresh)
|
|
289
|
+
local git_branch
|
|
290
|
+
git_branch=$(git branch --show-current 2>/dev/null || echo '-')
|
|
230
291
|
|
|
231
|
-
#
|
|
232
|
-
tmux set-option -t "$
|
|
292
|
+
# Line 0 (top): Session name (stripped of claude- prefix) + Keybinds + Git branch
|
|
293
|
+
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+N new session Alt+k interrupt Alt+q detach "
|
|
233
294
|
|
|
234
|
-
#
|
|
235
|
-
tmux set-option -t "$
|
|
295
|
+
# Line 1 (bottom): Window tabs with smart truncation and brand color
|
|
296
|
+
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} }}"
|
|
236
297
|
|
|
237
|
-
#
|
|
238
|
-
|
|
298
|
+
# Pane border styling - blue inactive, orange active
|
|
299
|
+
tmux set-option -t "$target_session" pane-border-style "fg=#3d59a1"
|
|
300
|
+
tmux set-option -t "$target_session" pane-active-border-style "fg=#e8683a"
|
|
239
301
|
|
|
240
|
-
#
|
|
241
|
-
tmux set-option -t "$
|
|
302
|
+
# Message styling - orange highlight
|
|
303
|
+
tmux set-option -t "$target_session" message-style "bg=#e8683a,fg=#1a1b26,bold"
|
|
242
304
|
|
|
243
|
-
#
|
|
244
|
-
# - Active window: full name (max 15 chars), brand orange highlight
|
|
245
|
-
# - Inactive windows: truncate to 8 chars with ... suffix, warm gray
|
|
246
|
-
tmux set-option -t "$SESSION_NAME" 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} }}"
|
|
305
|
+
# ─── Keybindings ────────────────────────────────────────────────────────────
|
|
247
306
|
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
tmux
|
|
307
|
+
# Alt+number to switch windows (1-9)
|
|
308
|
+
for i in 1 2 3 4 5 6 7 8 9; do
|
|
309
|
+
tmux bind-key -n "M-$i" select-window -t ":$i"
|
|
310
|
+
done
|
|
251
311
|
|
|
252
|
-
#
|
|
253
|
-
tmux
|
|
312
|
+
# Alt+c to create new window
|
|
313
|
+
tmux bind-key -n M-c new-window -c "#{pane_current_path}"
|
|
254
314
|
|
|
255
|
-
#
|
|
315
|
+
# Alt+q to detach
|
|
316
|
+
tmux bind-key -n M-q detach-client
|
|
256
317
|
|
|
257
|
-
#
|
|
318
|
+
# Alt+d to split horizontally (side by side)
|
|
319
|
+
tmux bind-key -n M-d split-window -h -c "#{pane_current_path}"
|
|
258
320
|
|
|
259
|
-
# Alt+
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
321
|
+
# Alt+s to split vertically (top/bottom)
|
|
322
|
+
tmux bind-key -n M-s split-window -v -c "#{pane_current_path}"
|
|
323
|
+
|
|
324
|
+
# Alt+arrow to navigate panes
|
|
325
|
+
tmux bind-key -n M-Left select-pane -L
|
|
326
|
+
tmux bind-key -n M-Right select-pane -R
|
|
327
|
+
tmux bind-key -n M-Up select-pane -U
|
|
328
|
+
tmux bind-key -n M-Down select-pane -D
|
|
263
329
|
|
|
264
|
-
# Alt+
|
|
265
|
-
tmux bind-key -n M-
|
|
330
|
+
# Alt+x to close current pane (with confirmation)
|
|
331
|
+
tmux bind-key -n M-x confirm-before -p "Close pane? (y/n)" kill-pane
|
|
266
332
|
|
|
267
|
-
# Alt+
|
|
268
|
-
tmux bind-key -n M-
|
|
333
|
+
# Alt+w to close current window (with confirmation)
|
|
334
|
+
tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" kill-window
|
|
269
335
|
|
|
270
|
-
# Alt+
|
|
271
|
-
tmux bind-key -n M-
|
|
336
|
+
# Alt+n/p for next/previous window
|
|
337
|
+
tmux bind-key -n M-n next-window
|
|
338
|
+
tmux bind-key -n M-p previous-window
|
|
272
339
|
|
|
273
|
-
# Alt+
|
|
274
|
-
tmux bind-key -n M-
|
|
340
|
+
# Alt+r to rename window
|
|
341
|
+
tmux bind-key -n M-r command-prompt -I "#W" "rename-window '%%'"
|
|
275
342
|
|
|
276
|
-
# Alt+
|
|
277
|
-
tmux bind-key -n M-
|
|
278
|
-
tmux bind-key -n M-Right select-pane -R
|
|
279
|
-
tmux bind-key -n M-Up select-pane -U
|
|
280
|
-
tmux bind-key -n M-Down select-pane -D
|
|
343
|
+
# Alt+z to zoom/unzoom pane (fullscreen toggle)
|
|
344
|
+
tmux bind-key -n M-z resize-pane -Z
|
|
281
345
|
|
|
282
|
-
# Alt+
|
|
283
|
-
tmux bind-key -n M-
|
|
346
|
+
# Alt+[ to enter copy mode (for scrolling)
|
|
347
|
+
tmux bind-key -n M-[ copy-mode
|
|
284
348
|
|
|
285
|
-
#
|
|
286
|
-
|
|
349
|
+
# ─── Session Creation Keybindings ──────────────────────────────────────────
|
|
350
|
+
# Alt+N (shift+n) to create a new worktree session window
|
|
351
|
+
tmux bind-key -n M-N run-shell "node .agileflow/scripts/spawn-parallel.js add-window --name auto-\$(date +%s) 2>/dev/null && tmux display-message 'New worktree session created' || tmux display-message 'Session creation failed'"
|
|
287
352
|
|
|
288
|
-
# Alt+
|
|
289
|
-
tmux bind-key -n M-
|
|
290
|
-
tmux bind-key -n M-p previous-window
|
|
353
|
+
# Alt+S (shift+s) to create a same-directory Claude window (no worktree)
|
|
354
|
+
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 'Same-dir session created'"
|
|
291
355
|
|
|
292
|
-
#
|
|
293
|
-
|
|
356
|
+
# ─── Freeze Recovery Keybindings ───────────────────────────────────────────
|
|
357
|
+
# Alt+k to send Ctrl+C twice (soft interrupt for frozen processes)
|
|
358
|
+
tmux bind-key -n M-k run-shell "tmux send-keys C-c; sleep 0.5; tmux send-keys C-c"
|
|
294
359
|
|
|
295
|
-
# Alt+
|
|
296
|
-
tmux bind-key -n M-
|
|
360
|
+
# Alt+K (shift+k) to force-kill pane immediately (nuclear option for hard freezes)
|
|
361
|
+
tmux bind-key -n M-K kill-pane
|
|
362
|
+
|
|
363
|
+
# Alt+R (shift+r) to respawn the pane (restart with a fresh shell)
|
|
364
|
+
tmux bind-key -n M-R respawn-pane -k
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# Handle --refresh flag — re-apply config to all existing claude-* sessions
|
|
368
|
+
if [ "$REFRESH_CONFIG" = true ]; then
|
|
369
|
+
REFRESHED=0
|
|
370
|
+
for sid in $(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^claude-"); do
|
|
371
|
+
configure_tmux_session "$sid"
|
|
372
|
+
REFRESHED=$((REFRESHED + 1))
|
|
373
|
+
done
|
|
374
|
+
if [ "$REFRESHED" -gt 0 ]; then
|
|
375
|
+
echo "Refreshed config on $REFRESHED session(s)."
|
|
376
|
+
else
|
|
377
|
+
echo "No claude-* sessions found to refresh."
|
|
378
|
+
fi
|
|
379
|
+
exit 0
|
|
380
|
+
fi
|
|
297
381
|
|
|
298
|
-
#
|
|
299
|
-
|
|
382
|
+
# Create new tmux session with Claude
|
|
383
|
+
echo "Starting Claude in tmux session: $SESSION_NAME"
|
|
300
384
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
tmux bind-key -n M-k run-shell "tmux send-keys C-c; sleep 0.5; tmux send-keys C-c"
|
|
385
|
+
# Set base-index globally BEFORE creating session so first window gets index 1
|
|
386
|
+
tmux set-option -g base-index 1
|
|
304
387
|
|
|
305
|
-
#
|
|
306
|
-
tmux
|
|
388
|
+
# Create session in detached mode first (will use base-index 1)
|
|
389
|
+
tmux new-session -d -s "$SESSION_NAME" -n "main"
|
|
307
390
|
|
|
308
|
-
#
|
|
309
|
-
|
|
391
|
+
# Apply tmux configuration
|
|
392
|
+
configure_tmux_session "$SESSION_NAME"
|
|
310
393
|
|
|
311
394
|
# Send the claude command to the first window
|
|
312
395
|
CLAUDE_CMD="claude"
|
|
@@ -15,7 +15,37 @@
|
|
|
15
15
|
* Usage: Configured as PreToolUse hook in .claude/settings.json
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
function loadDamageControlUtils() {
|
|
22
|
+
const candidates = [
|
|
23
|
+
path.join(__dirname, 'lib', 'damage-control-utils.js'),
|
|
24
|
+
path.join(process.cwd(), '.agileflow', 'scripts', 'lib', 'damage-control-utils.js'),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
try {
|
|
29
|
+
if (fs.existsSync(candidate)) {
|
|
30
|
+
return require(candidate);
|
|
31
|
+
}
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Try next candidate
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const utils = loadDamageControlUtils();
|
|
41
|
+
if (!utils || typeof utils.createBashHook !== 'function') {
|
|
42
|
+
// Fail-open: never block Bash tool because hook bootstrap failed.
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
utils.createBashHook()();
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// Fail-open on runtime errors to avoid breaking CLI workflows.
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
@@ -12,7 +12,37 @@
|
|
|
12
12
|
* Usage: Configured as PreToolUse hook in .claude/settings.json
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
function loadDamageControlUtils() {
|
|
19
|
+
const candidates = [
|
|
20
|
+
path.join(__dirname, 'lib', 'damage-control-utils.js'),
|
|
21
|
+
path.join(process.cwd(), '.agileflow', 'scripts', 'lib', 'damage-control-utils.js'),
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(candidate)) {
|
|
27
|
+
return require(candidate);
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
// Try next candidate
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const utils = loadDamageControlUtils();
|
|
38
|
+
if (!utils || typeof utils.createPathHook !== 'function') {
|
|
39
|
+
// Fail-open: never block Edit tool because hook bootstrap failed.
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
utils.createPathHook('edit')();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Fail-open on runtime errors to avoid breaking CLI workflows.
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
@@ -12,7 +12,37 @@
|
|
|
12
12
|
* Usage: Configured as PreToolUse hook in .claude/settings.json
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
function loadDamageControlUtils() {
|
|
19
|
+
const candidates = [
|
|
20
|
+
path.join(__dirname, 'lib', 'damage-control-utils.js'),
|
|
21
|
+
path.join(process.cwd(), '.agileflow', 'scripts', 'lib', 'damage-control-utils.js'),
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(candidate)) {
|
|
27
|
+
return require(candidate);
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
// Try next candidate
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const utils = loadDamageControlUtils();
|
|
38
|
+
if (!utils || typeof utils.createPathHook !== 'function') {
|
|
39
|
+
// Fail-open: never block Write tool because hook bootstrap failed.
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
utils.createPathHook('write')();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Fail-open on runtime errors to avoid breaking CLI workflows.
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
@@ -287,7 +287,7 @@ function enableFeature(feature, options = {}, version) {
|
|
|
287
287
|
features: {
|
|
288
288
|
processCleanup: {
|
|
289
289
|
enabled: true,
|
|
290
|
-
autoKill:
|
|
290
|
+
autoKill: false,
|
|
291
291
|
version,
|
|
292
292
|
at: new Date().toISOString(),
|
|
293
293
|
},
|
|
@@ -296,9 +296,10 @@ function enableFeature(feature, options = {}, version) {
|
|
|
296
296
|
version
|
|
297
297
|
);
|
|
298
298
|
success('Process cleanup enabled');
|
|
299
|
-
|
|
299
|
+
info('Duplicate Claude processes will be detected and reported on session start');
|
|
300
|
+
info('Auto-kill is disabled by default for safety');
|
|
300
301
|
info(' Only affects processes in the SAME working directory (worktrees are safe)');
|
|
301
|
-
info('
|
|
302
|
+
info(' Set AGILEFLOW_PROCESS_CLEANUP_AUTOKILL=1 to opt in to auto-kill at runtime');
|
|
302
303
|
return true;
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -1142,7 +1143,7 @@ function enableShellAliases() {
|
|
|
1142
1143
|
const ALIAS_BLOCK_LINES = [
|
|
1143
1144
|
'# AgileFlow tmux wrapper',
|
|
1144
1145
|
'# AgileFlow tmux shortcuts (claude stays normal)',
|
|
1145
|
-
|
|
1146
|
+
"# Use 'af' or 'agileflow' for tmux, 'claude' stays normal",
|
|
1146
1147
|
'alias af="bash .agileflow/scripts/af"',
|
|
1147
1148
|
'alias agileflow="bash .agileflow/scripts/af"',
|
|
1148
1149
|
];
|
|
@@ -1209,7 +1210,7 @@ function disableShellAliases() {
|
|
|
1209
1210
|
const ALIAS_BLOCK_LINES = [
|
|
1210
1211
|
'# AgileFlow tmux wrapper',
|
|
1211
1212
|
'# AgileFlow tmux shortcuts (claude stays normal)',
|
|
1212
|
-
|
|
1213
|
+
"# Use 'af' or 'agileflow' for tmux, 'claude' stays normal",
|
|
1213
1214
|
'alias af="bash .agileflow/scripts/af"',
|
|
1214
1215
|
'alias agileflow="bash .agileflow/scripts/af"',
|
|
1215
1216
|
];
|
|
@@ -1223,8 +1224,10 @@ function disableShellAliases() {
|
|
|
1223
1224
|
const content = fs.readFileSync(rc.path, 'utf8');
|
|
1224
1225
|
|
|
1225
1226
|
// Check for any AgileFlow alias (covers old and new markers)
|
|
1226
|
-
if (
|
|
1227
|
-
|
|
1227
|
+
if (
|
|
1228
|
+
!content.includes('alias af="bash .agileflow/scripts/af"') &&
|
|
1229
|
+
!content.includes(SHELL_ALIAS_MARKER)
|
|
1230
|
+
) {
|
|
1228
1231
|
continue;
|
|
1229
1232
|
}
|
|
1230
1233
|
|
|
@@ -8,6 +8,7 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const crypto = require('crypto');
|
|
10
10
|
const { execFileSync } = require('child_process');
|
|
11
|
+
const { feedback } = require('../../lib/feedback');
|
|
11
12
|
const {
|
|
12
13
|
c,
|
|
13
14
|
log,
|
|
@@ -168,7 +169,9 @@ function showVersionInfo(version) {
|
|
|
168
169
|
latestVersion = execFileSync('npm', ['view', 'agileflow', 'version'], {
|
|
169
170
|
encoding: 'utf8',
|
|
170
171
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
171
|
-
})
|
|
172
|
+
})
|
|
173
|
+
.toString()
|
|
174
|
+
.trim();
|
|
172
175
|
log(`Latest: v${latestVersion}`);
|
|
173
176
|
|
|
174
177
|
if (installedVersion !== 'unknown' && latestVersion && installedVersion !== latestVersion) {
|
|
@@ -262,7 +265,12 @@ function repairScripts(targetFeature = null) {
|
|
|
262
265
|
// Ensure scripts directory exists
|
|
263
266
|
ensureDir(scriptsDir);
|
|
264
267
|
|
|
265
|
-
|
|
268
|
+
const bar =
|
|
269
|
+
scriptsToCheck.length > 5
|
|
270
|
+
? feedback.progressBar('Checking scripts', scriptsToCheck.length)
|
|
271
|
+
: null;
|
|
272
|
+
|
|
273
|
+
for (const [script, scriptInfo] of scriptsToCheck) {
|
|
266
274
|
const destPath = path.join(scriptsDir, script);
|
|
267
275
|
const srcPath = path.join(sourceDir, script);
|
|
268
276
|
|
|
@@ -287,7 +295,9 @@ function repairScripts(targetFeature = null) {
|
|
|
287
295
|
} else {
|
|
288
296
|
skipped++;
|
|
289
297
|
}
|
|
298
|
+
if (bar) bar.increment(script);
|
|
290
299
|
}
|
|
300
|
+
if (bar) bar.complete('Script check complete');
|
|
291
301
|
|
|
292
302
|
// Summary
|
|
293
303
|
log('');
|