-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdev.zsh
More file actions
417 lines (365 loc) · 15.6 KB
/
Copy pathdev.zsh
File metadata and controls
417 lines (365 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
#!/usr/bin/env zsh
# Dev Session Manager
# Quick development session bootstrapping with tmux
#
# Copyright (c) 2026 Jeryl Estopace
# GitHub: https://github.com/jeryldev
# LinkedIn: https://www.linkedin.com/in/jeryldev/
# Repository: https://github.com/jeryldev/dev-session-manager
# Version
DEV_VERSION="2.3.0"
# Configuration
DEV_SESSION_PREFIX="dev-"
DEV_DEFAULT_DIR="${DEV_HOME_DIR:-$HOME/code}"
DEV_AI_CMD="${DEV_AI_CMD:-claude}"
# Colors (use existing shell colors or define defaults)
: ${RED:='\033[0;31m'}
: ${GREEN:='\033[0;32m'}
: ${YELLOW:='\033[0;33m'}
: ${BLUE:='\033[0;34m'}
: ${NC:='\033[0m'}
# Check if a command is available
_dev_has_command() {
command -v "$1" &> /dev/null
}
_dev_check_optional() {
local cmd="$1" label="$2" install="$3"
if _dev_has_command "$cmd"; then
echo -e " ${GREEN}✓${NC} $cmd ($label)"
else
echo -e " ${RED}✗${NC} $cmd ($install)"
fi
}
# Show prerequisite status with checkmarks
_dev_show_prerequisites() {
echo ""
echo -e "${YELLOW}Prerequisites:${NC}"
# Check zsh
if [[ -n "$ZSH_VERSION" ]]; then
echo -e " ${GREEN}✓${NC} zsh ($ZSH_VERSION)"
else
echo -e " ${RED}✗${NC} zsh (not detected)"
fi
# Check tmux
if _dev_has_command tmux; then
local tmux_ver=$(tmux -V 2>/dev/null | cut -d' ' -f2)
echo -e " ${GREEN}✓${NC} tmux ($tmux_ver)"
else
echo -e " ${RED}✗${NC} tmux (not installed)"
echo -e " ${YELLOW}Install: brew install tmux${NC}"
fi
echo ""
echo -e "${YELLOW}Optional tools:${NC}"
_dev_check_optional claude "AI popup: Prefix a" "brew install claude-code"
_dev_check_optional kb "Kanban popup: Prefix k" "brew install jeryldev/tap/kb"
_dev_check_optional lazygit "Git popup: Prefix g" "brew install lazygit"
echo ""
}
# Normalize session names
_dev_normalize_session_name() {
local name="$1"
if [[ ! "$name" =~ ^${DEV_SESSION_PREFIX} ]]; then
echo "${DEV_SESSION_PREFIX}${name}"
else
echo "$name"
fi
}
# Get display name (without prefix)
_dev_display_name() {
local session_name="$1"
echo "${session_name#${DEV_SESSION_PREFIX}}"
}
# Validate session name
_dev_validate_name() {
local name="$1"
if [[ -z "$name" ]]; then
echo -e "${RED}Error: Session name cannot be empty${NC}"
return 1
fi
if [[ "$name" =~ [^a-zA-Z0-9_-] ]]; then
echo -e "${RED}Error: Session name can only contain letters, numbers, hyphens, and underscores${NC}"
return 1
fi
return 0
}
# Check if tmux is available
_dev_check_tmux() {
if ! _dev_has_command tmux; then
echo -e "${RED}Error: tmux is not installed${NC}"
echo -e "${YELLOW}Install with: brew install tmux${NC}"
return 1
fi
return 0
}
_dev_session_not_found() {
local display_name="$1"
echo -e "${RED}✗ Session '${display_name}' not found${NC}"
echo -e "${YELLOW}Tip: Run 'dev ls' to see active sessions${NC}"
}
_dev_attach_session() {
local session_name="$1" message="$2"
_dev_setup_popup_keybindings
echo -e "${BLUE}${message}${NC}"
tmux attach -t "$session_name"
}
# Center text in a box
_dev_center_text() {
local text="$1"
local width="$2"
local text_len=${#text}
local padding=$(( (width - text_len) / 2 ))
local left_pad=$(printf '%*s' "$padding" '')
local right_pad=$(printf '%*s' "$((width - text_len - padding))" '')
echo "${left_pad}${text}${right_pad}"
}
# Dev session manager
# Usage: dev <command> [args]
dev() {
local cmd="$1"
local box_width=56
case "$cmd" in
help|h|-h|--help)
local title="Dev session manager"
local centered_title=$(_dev_center_text "$title" "$box_width")
echo -e "${GREEN}╔$(printf '═%.0s' {1..56})╗${NC}"
echo -e "${GREEN}║${NC}${centered_title}${GREEN}║${NC}"
echo -e "${GREEN}╚$(printf '═%.0s' {1..56})╝${NC}"
_dev_show_prerequisites
echo -e "${YELLOW}Commands:${NC}"
echo -e " ${BLUE}dev <name>${NC} Create or attach to a dev session"
echo -e " ${BLUE}dev attach <name>${NC} Attach to an existing dev session"
echo -e " ${BLUE}dev ls${NC} List all dev sessions"
echo -e " ${BLUE}dev kill <name>${NC} Kill a dev session"
echo -e " ${BLUE}dev reload${NC} Reload popup keybindings"
echo -e " ${BLUE}dev help${NC} Show this help"
echo -e " ${BLUE}dev tmux${NC} Show tmux commands reference"
echo -e " ${BLUE}dev version${NC} Show version"
echo ""
echo -e "${YELLOW}Examples:${NC}"
echo -e " ${BLUE}dev myproject${NC} Create 'dev-myproject' session"
echo -e " ${BLUE}dev 1${NC} Create 'dev-1' session"
echo -e " ${BLUE}dev attach 1${NC} Attach to 'dev-1'"
echo -e " ${BLUE}dev kill 1${NC} Kill 'dev-1'"
echo ""
echo -e "${YELLOW}Session layout (7 windows, all start at ${DEV_DEFAULT_DIR}):${NC}"
echo -e " 1. frontend 2. backend 3. database 4. testing"
echo -e " 5. ${GREEN}editor${NC} 6. scratch 7. extra"
echo ""
echo -e "${BLUE}Starts at window 5 (editor)${NC}"
echo ""
echo -e "${YELLOW}Popup keybindings (inside tmux):${NC}"
echo -e " ${BLUE}Prefix a${NC} AI assistant (claude)"
echo -e " ${BLUE}Prefix k${NC} Kanban board (kb)"
echo -e " ${BLUE}Prefix g${NC} Git UI (lazygit)"
echo -e " ${BLUE}Prefix j${NC} Terminal (shell)"
echo ""
;;
version|v|-v|--version)
echo -e "Dev session manager ${GREEN}v${DEV_VERSION}${NC}"
echo -e "https://github.com/jeryldev/dev-session-manager"
;;
ls|list)
if ! _dev_check_tmux; then
return 1
fi
echo -e "${GREEN}Active dev sessions:${NC}"
local sessions=$(tmux list-sessions 2>/dev/null | grep "^${DEV_SESSION_PREFIX}")
if [ -z "$sessions" ]; then
echo -e " ${YELLOW}No dev sessions found${NC}"
else
while IFS= read -r line; do
echo " ${line#${DEV_SESSION_PREFIX}}"
done <<< "$sessions"
echo ""
echo -e "${BLUE}Tip: Use 'dev attach <name>' to attach or 'dev kill <name>' to kill${NC}"
fi
;;
attach|a)
if ! _dev_check_tmux; then
return 1
fi
if [ -z "$2" ]; then
echo -e "${RED}Usage: dev attach <name>${NC}"
echo -e "${YELLOW}Example: dev attach myproject${NC}"
return 1
fi
if ! _dev_validate_name "$2"; then
return 1
fi
local session_name=$(_dev_normalize_session_name "$2")
local display_name=$(_dev_display_name "$session_name")
if tmux has-session -t "$session_name" 2>/dev/null; then
_dev_attach_session "$session_name" "Attaching to: ${display_name}"
else
_dev_session_not_found "$display_name"
fi
;;
kill|k)
if ! _dev_check_tmux; then
return 1
fi
if [ -z "$2" ]; then
echo -e "${RED}Usage: dev kill <name>${NC}"
echo -e "${YELLOW}Example: dev kill myproject${NC}"
return 1
fi
if ! _dev_validate_name "$2"; then
return 1
fi
local session_name=$(_dev_normalize_session_name "$2")
local display_name=$(_dev_display_name "$session_name")
if tmux has-session -t "$session_name" 2>/dev/null; then
tmux kill-session -t "$session_name"
echo -e "${GREEN}✓ Killed session: ${display_name}${NC}"
else
_dev_session_not_found "$display_name"
fi
;;
reload)
if ! tmux list-sessions &>/dev/null; then
echo -e "${YELLOW}No active tmux server. Start a session first.${NC}"
return 1
fi
echo -e "${BLUE}Reloading dev configuration...${NC}"
if _dev_setup_popup_keybindings; then
echo -e "${GREEN}✓ Popup keybindings updated${NC}"
else
echo -e "${YELLOW}⚠ Some keybindings were skipped${NC}"
fi
;;
tmux|t)
local title="Tmux commands reference"
local centered_title=$(_dev_center_text "$title" "$box_width")
echo -e "${GREEN}╔$(printf '═%.0s' {1..56})╗${NC}"
echo -e "${GREEN}║${NC}${centered_title}${GREEN}║${NC}"
echo -e "${GREEN}╚$(printf '═%.0s' {1..56})╝${NC}"
echo ""
echo -e "${YELLOW}Note: These examples use Ctrl+b as the prefix (default).${NC}"
echo -e "${YELLOW}Your prefix may differ. Check with: tmux show-option -g prefix${NC}"
echo ""
echo -e "${YELLOW}Detach and exit:${NC}"
echo -e " ${BLUE}Prefix d${NC} ${GREEN}Detach${NC} from session (keeps running)"
echo -e " ${BLUE}exit${NC} ${RED}Exit${NC} shell (closes pane/window)"
echo -e " ${BLUE}dev kill <name>${NC} ${RED}Kill${NC} entire session"
echo ""
echo -e "${YELLOW}Window navigation:${NC}"
echo -e " ${BLUE}Prefix n${NC} Next window"
echo -e " ${BLUE}Prefix p${NC} Previous window"
echo -e " ${BLUE}Prefix 0-9${NC} Jump to window number"
echo -e " ${BLUE}Prefix w${NC} Show window list"
echo ""
echo -e "${YELLOW}Window management:${NC}"
echo -e " ${BLUE}Prefix c${NC} Create new window"
echo -e " ${BLUE}Prefix ,${NC} Rename current window"
echo -e " ${BLUE}Prefix &${NC} Kill current window"
echo ""
echo -e "${YELLOW}Pane splits:${NC}"
echo -e " ${BLUE}Prefix %${NC} Split vertically (side by side)"
echo -e " ${BLUE}Prefix \"${NC} Split horizontally (top/bottom)"
echo -e " ${BLUE}Prefix z${NC} Toggle pane zoom (fullscreen)"
echo -e " ${BLUE}Prefix x${NC} Close current pane"
echo ""
echo -e "${YELLOW}Pane navigation:${NC}"
echo -e " ${BLUE}Prefix arrow${NC} Navigate with arrow keys"
echo -e " ${BLUE}Prefix o${NC} Cycle through panes"
echo ""
echo -e "${YELLOW}Copy mode (scrollback):${NC}"
echo -e " ${BLUE}Prefix [${NC} Enter copy mode"
echo -e " ${BLUE}q${NC} Exit copy mode"
echo ""
echo -e "${YELLOW}Session management:${NC}"
echo -e " ${BLUE}Prefix \$${NC} Rename session"
echo -e " ${BLUE}Prefix s${NC} Show all sessions"
echo -e " ${BLUE}Prefix (${NC} Switch to previous session"
echo -e " ${BLUE}Prefix )${NC} Switch to next session"
echo ""
echo -e "${YELLOW}Dev popups:${NC}"
echo -e " ${BLUE}Prefix a${NC} AI assistant popup"
echo -e " ${BLUE}Prefix k${NC} Kanban board popup"
echo -e " ${BLUE}Prefix g${NC} Git UI popup"
echo -e " ${BLUE}Prefix j${NC} Terminal popup"
echo ""
echo -e "${GREEN}Quick reference:${NC}"
echo -e " ${BLUE}Detach${NC} = Prefix d (session stays alive, can reattach)"
echo -e " ${BLUE}Exit${NC} = type 'exit' (closes current pane/window)"
echo -e " ${BLUE}Kill${NC} = dev kill <name> (destroys entire session)"
echo ""
;;
"")
echo -e "${RED}Usage: dev <command> [args]${NC}"
echo -e "${YELLOW}Run 'dev help' for more information${NC}"
return 1
;;
*)
# Check tmux before creating session
if ! _dev_check_tmux; then
return 1
fi
# Create or attach to session
if ! _dev_validate_name "$1"; then
return 1
fi
local session_name=$(_dev_normalize_session_name "$1")
local display_name=$(_dev_display_name "$session_name")
# Check if session already exists
if tmux has-session -t "$session_name" 2>/dev/null; then
if [[ ! -t 0 ]]; then
echo -e "${RED}Error: Session '${display_name}' already exists (non-interactive, cannot prompt)${NC}"
return 1
fi
echo -e "${YELLOW}⚠ Session '${display_name}' already exists!${NC}"
echo -ne "${GREEN}Attach to it? (y/n) ${NC}"
read -r choice
case "$choice" in
y|Y)
_dev_attach_session "$session_name" "Attaching to: ${display_name}"
;;
*)
echo -e "${RED}Operation cancelled${NC}"
echo -e "${BLUE}Tip: Use 'dev kill $1' to kill the session${NC}"
;;
esac
return 0
fi
# Create new session
echo -e "${GREEN}Creating session: ${display_name}${NC}"
tmux new-session -d -s "$session_name" -n "frontend" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:2" -n "backend" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:3" -n "database" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:4" -n "testing" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:5" -n "editor" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:6" -n "scratch" -c "$DEV_DEFAULT_DIR"
tmux new-window -t "$session_name:7" -n "extra" -c "$DEV_DEFAULT_DIR"
# Select the editor window (window 5)
tmux select-window -t "$session_name:5"
_dev_attach_session "$session_name" "Created 7 windows, starting at editor"
;;
esac
}
_dev_validate_ai_cmd() {
if [[ "$DEV_AI_CMD" == *" "* ]]; then
echo -e "${RED}Error: DEV_AI_CMD cannot contain spaces ('${DEV_AI_CMD}')${NC}"
return 1
fi
return 0
}
_dev_bind_popup() {
local key="$1" prefix="$2" cmd="$3" suffix="${4:+-$4}"
tmux bind-key "$key" run-shell \
'SESSION="'"${prefix}"'-#{session_name}-#{window_index}-#{window_name}'"${suffix}"'"; tmux has-session -t "$SESSION" 2>/dev/null || tmux new-session -d -s "$SESSION" -c "#{pane_current_path}" "'"${cmd}"'"; tmux display-popup -w 90% -h 90% -b single -E "tmux attach-session -t $SESSION"'
}
_dev_setup_popup_keybindings() {
tmux list-sessions &>/dev/null || return 1
_dev_bind_popup j term "${SHELL:-zsh}"
if _dev_validate_ai_cmd; then
_dev_bind_popup a ai "[ -f ~/.ssh/id_ed25519 ] && ssh-add ~/.ssh/id_ed25519 2>/dev/null; ${DEV_AI_CMD} --enable-auto-mode" "${DEV_AI_CMD}"
fi
_dev_has_command kb && _dev_bind_popup k kb kb
_dev_has_command lazygit && _dev_bind_popup g lg lazygit
}
# Run directly if executed (not sourced), set up keybindings if sourced
if [[ "${zsh_eval_context[-1]}" != "file" ]]; then
dev "$@"
else
_dev_setup_popup_keybindings
fi