@walwal-harness/cli 6.1.3 → 6.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +43 -74
  3. package/assets/launchd/com.walwal.harness-wake.plist.template +32 -0
  4. package/assets/templates/CONVENTIONS.md +12 -0
  5. package/assets/templates/HARNESS.md +216 -403
  6. package/assets/templates/config.json +27 -29
  7. package/assets/templates/memory.md +16 -2
  8. package/assets/templates/progress.json.template +6 -5
  9. package/bin/init.js +179 -90
  10. package/conventions/README.md +92 -0
  11. package/conventions/conductor.md +24 -0
  12. package/conventions/coo-developer.md +24 -0
  13. package/conventions/cqo.md +24 -0
  14. package/conventions/cto.md +24 -0
  15. package/conventions/dispatcher.md +24 -0
  16. package/conventions/documentationer.md +24 -0
  17. package/conventions/evaluator-architecture.md +24 -0
  18. package/conventions/evaluator-code-quality.md +24 -0
  19. package/conventions/evaluator-functional.md +24 -0
  20. package/conventions/evaluator-security.md +24 -0
  21. package/conventions/evaluator-visual.md +24 -0
  22. package/conventions/generator-backend.md +24 -0
  23. package/conventions/generator-designer.md +24 -0
  24. package/conventions/generator-devops.md +24 -0
  25. package/conventions/generator-frontend.md +24 -0
  26. package/conventions/meeting-manager.md +24 -0
  27. package/conventions/planner.md +24 -0
  28. package/conventions/service-ops.md +24 -0
  29. package/conventions/shared.md +40 -0
  30. package/gotchas/conductor.md +16 -0
  31. package/gotchas/dispatcher.md +16 -0
  32. package/gotchas/meeting-manager.md +8 -0
  33. package/gotchas/service-ops.md +8 -0
  34. package/package.json +5 -4
  35. package/scripts/conductor-tick.sh +61 -53
  36. package/scripts/harness-archive.sh +7 -5
  37. package/scripts/harness-dashboard-up.sh +11 -3
  38. package/scripts/harness-hourly-review.sh +303 -0
  39. package/scripts/harness-meeting-doc.sh +55 -16
  40. package/scripts/harness-next.sh +1 -1
  41. package/scripts/harness-queue-manager.sh +12 -5
  42. package/scripts/harness-service-ops-monitor.sh +228 -0
  43. package/scripts/harness-session-start.sh +60 -31
  44. package/scripts/harness-statusline.sh +8 -12
  45. package/scripts/harness-stop.sh +85 -0
  46. package/scripts/harness-user-prompt-submit.sh +11 -29
  47. package/scripts/harness-wake-install.sh +179 -0
  48. package/scripts/harness-wake.sh +258 -0
  49. package/scripts/harness-worker-dispatch.sh +157 -0
  50. package/scripts/lib/harness-progress-migrate.sh +6 -1
  51. package/scripts/lib/harness-render-progress.sh +3 -3
  52. package/skills/brainstorming/SKILL.md +2 -2
  53. package/skills/conductor/SKILL.md +67 -55
  54. package/skills/cqo/SKILL.md +1 -0
  55. package/skills/cto/SKILL.md +1 -0
  56. package/skills/dispatcher/SKILL.md +33 -14
  57. package/skills/evaluator-code-quality/SKILL.md +4 -4
  58. package/skills/evaluator-functional/SKILL.md +6 -6
  59. package/skills/evaluator-visual/SKILL.md +4 -4
  60. package/skills/generator-backend/SKILL.md +4 -4
  61. package/skills/generator-frontend/SKILL.md +5 -5
  62. package/skills/meeting-manager/SKILL.md +82 -12
  63. package/skills/planner/SKILL.md +3 -3
  64. package/skills/service-ops/SKILL.md +25 -0
  65. package/commands/harness-solo.md +0 -103
  66. package/commands/harness-stop.md +0 -53
  67. package/commands/harness-team.md +0 -530
  68. package/scripts/harness-dashboard.sh +0 -509
  69. package/scripts/harness-goal-init.sh +0 -72
  70. package/scripts/harness-goal-show.sh +0 -37
  71. package/scripts/harness-gotcha-memory.sh +0 -348
  72. package/scripts/harness-monitor.sh +0 -398
  73. package/scripts/harness-prompt-history.sh +0 -164
  74. package/scripts/harness-tmux.sh +0 -372
@@ -1,372 +0,0 @@
1
- #!/bin/bash
2
- # harness-tmux.sh — Unified Harness Studio layout
3
- #
4
- # Team Mode (--team):
5
- # ┌──────────────┬──────────────┬──────────────┐
6
- # │ │ │ TEAM 1 │
7
- # │ Dashboard │ │ │
8
- # │ (queue + │ Gotcha & ├──────────────┤
9
- # │ status + │ Memory │ TEAM 2 │
10
- # │ features) │ │ │
11
- # ├──────────────┤ ├──────────────┤
12
- # │ Archive │ │ TEAM 3 │
13
- # │ Prompt │ │ │
14
- # └──────────────┴──────────────┴──────────────┘
15
- # (Archive Prompt는 별도 패널 — Dashboard 모니터링 영역 보호)
16
- #
17
- # Rendering strategy:
18
- # 1. iTerm2 detected → native split panes (no tmux needed)
19
- # 2. tmux available → tmux session
20
- # 3. Fallback → open new terminal windows
21
- #
22
- # Usage:
23
- # bash scripts/harness-tmux.sh [project-root] --team
24
- # bash scripts/harness-tmux.sh [project-root] --solo
25
- # bash scripts/harness-tmux.sh [project-root] --team --force-tmux # skip iTerm2, use tmux
26
- # bash scripts/harness-tmux.sh --kill
27
-
28
- set -euo pipefail
29
-
30
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
31
- SESSION_NAME="harness-studio"
32
-
33
- PROJECT_ROOT=""
34
- MODE=""
35
- DETACH=false
36
- FORCE_TMUX=false
37
-
38
- for arg in "$@"; do
39
- case "$arg" in
40
- --team) MODE="team" ;;
41
- --solo) MODE="solo" ;;
42
- --detach) DETACH=true ;;
43
- --force-tmux) FORCE_TMUX=true ;;
44
- --kill)
45
- tmux kill-session -t "$SESSION_NAME" 2>/dev/null && echo "tmux session killed." || true
46
- # Also try to close iTerm2 studio tab if exists
47
- if [ "${TERM_PROGRAM:-}" = "iTerm.app" ]; then
48
- osascript -e '
49
- tell application "iTerm2"
50
- repeat with w in windows
51
- repeat with t in tabs of w
52
- if name of current session of t contains "harness-studio" then
53
- close t
54
- end if
55
- end repeat
56
- end repeat
57
- end tell
58
- ' 2>/dev/null || true
59
- fi
60
- echo "Killed."
61
- exit 0
62
- ;;
63
- *)
64
- if [ -d "$arg" ]; then PROJECT_ROOT="$arg"; fi
65
- ;;
66
- esac
67
- done
68
-
69
- if [ -z "$PROJECT_ROOT" ]; then
70
- dir="$(pwd)"
71
- while [ "$dir" != "/" ]; do
72
- if [ -d "$dir/.harness" ]; then PROJECT_ROOT="$dir"; break; fi
73
- dir="$(dirname "$dir")"
74
- done
75
- fi
76
-
77
- if [ -z "$PROJECT_ROOT" ] || [ ! -d "$PROJECT_ROOT/.harness" ]; then
78
- echo "Error: .harness/ not found."
79
- exit 1
80
- fi
81
-
82
- # Auto-detect mode from progress.json if not specified.
83
- # Self-heal: feature-queue.json 에 in_progress 가 있으면 mode 와 무관하게 team 으로 간주.
84
- # (SessionStart 훅 외 경로에서 dashboard/tmux 가 stale solo 를 표시하던 버그)
85
- if [ -z "$MODE" ]; then
86
- if command -v jq &>/dev/null && [ -f "$PROJECT_ROOT/.harness/progress.json" ]; then
87
- MODE=$(jq -r '.mode // "solo"' "$PROJECT_ROOT/.harness/progress.json" 2>/dev/null)
88
- QUEUE_FILE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
89
- if [ "$MODE" != "team" ] && [ -f "$QUEUE_FILE" ]; then
90
- ACTIVE_COUNT=$(jq -r '(.queue.in_progress | length) // 0' "$QUEUE_FILE" 2>/dev/null || echo 0)
91
- if [ "${ACTIVE_COUNT:-0}" -gt 0 ]; then
92
- HEAL_TEAMS=$ACTIVE_COUNT
93
- [ "$HEAL_TEAMS" -gt 3 ] && HEAL_TEAMS=3
94
- bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
95
- ".mode = \"team\" | .team_state.active_teams = ${HEAL_TEAMS} | .team_state.paused_at = null" \
96
- 2>/dev/null
97
- MODE="team"
98
- echo "# tmux: mode auto-healed to 'team' (queue had ${ACTIVE_COUNT} in_progress)"
99
- if [ -f "$PROJECT_ROOT/.harness/progress.log" ]; then
100
- echo "$(date +'%Y-%m-%d %H:%M') | system | heal | mode | tmux reset solo→team (queue had ${ACTIVE_COUNT} in_progress)" >> "$PROJECT_ROOT/.harness/progress.log"
101
- fi
102
- fi
103
- fi
104
- fi
105
- MODE="${MODE:-solo}"
106
- fi
107
-
108
- echo "Project: $PROJECT_ROOT"
109
- echo "Mode: $MODE"
110
-
111
- # ══════════════════════════════════════════
112
- # Strategy 1: iTerm2 native split panes
113
- # ══════════════════════════════════════════
114
- launch_iterm2_team() {
115
- osascript <<APPLESCRIPT
116
- tell application "iTerm2"
117
- activate
118
-
119
- -- Create new window with first session (Prompt History)
120
- set studioWindow to (create window with default profile)
121
-
122
- tell studioWindow
123
- tell current session of current tab
124
- set name to "harness-studio"
125
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-dashboard.sh' '${PROJECT_ROOT}'"
126
-
127
- -- Split down (bottom of Dashboard column) → Archive Prompt
128
- set archivePane to (split horizontally with default profile)
129
- tell archivePane
130
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-prompt-history.sh' '${PROJECT_ROOT}'"
131
- end tell
132
-
133
- -- Split right → Gotchas (top of Rules column, full-height initially)
134
- set gotchaPane to (split vertically with default profile)
135
- tell gotchaPane
136
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode gotcha"
137
-
138
- -- IMPORTANT: Team 컬럼을 먼저 분리 (전체 높이 유지). 그 후에 gotchaPane 을 horizontal 분할.
139
- set t1Pane to (split vertically with default profile)
140
- tell t1Pane
141
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-monitor.sh' '${PROJECT_ROOT}' --team 1"
142
-
143
- set t2Pane to (split horizontally with default profile)
144
- tell t2Pane
145
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-monitor.sh' '${PROJECT_ROOT}' --team 2"
146
-
147
- set t3Pane to (split horizontally with default profile)
148
- tell t3Pane
149
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-monitor.sh' '${PROJECT_ROOT}' --team 3"
150
- end tell
151
- end tell
152
- end tell
153
-
154
- -- Team 컬럼 분리 후, gotchaPane (Rules 컬럼) 을 horizontal 두 번 split → Conventions / Memory
155
- set convPane to (split horizontally with default profile)
156
- tell convPane
157
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode conventions"
158
-
159
- set memPane to (split horizontally with default profile)
160
- tell memPane
161
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode memory"
162
- end tell
163
- end tell
164
- end tell
165
- end tell
166
- end tell
167
- end tell
168
- APPLESCRIPT
169
- }
170
-
171
- launch_iterm2_solo() {
172
- # Solo 레이아웃 (Team 모드와 동일 구조, 우측만 단일 Monitor 패널):
173
- # Left col : Dashboard (top) + Prompt History (bottom)
174
- # Middle : Gotcha · Memory · Conventions
175
- # Right : Monitor (lifecycle stream, 단일 에이전트)
176
- osascript <<APPLESCRIPT
177
- tell application "iTerm2"
178
- activate
179
-
180
- set studioWindow to (create window with default profile)
181
-
182
- tell studioWindow
183
- tell current session of current tab
184
- set name to "harness-studio"
185
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-dashboard.sh' '${PROJECT_ROOT}'"
186
-
187
- -- Split down (bottom of Dashboard column) → Prompt History
188
- set archivePane to (split horizontally with default profile)
189
- tell archivePane
190
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-prompt-history.sh' '${PROJECT_ROOT}'"
191
- end tell
192
-
193
- -- Split right → Gotchas (top of Rules column, full-height initially)
194
- set gotchaPane to (split vertically with default profile)
195
- tell gotchaPane
196
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode gotcha"
197
-
198
- -- Monitor 컬럼을 먼저 분리 (전체 높이 유지)
199
- set monPane to (split vertically with default profile)
200
- tell monPane
201
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-monitor.sh' '${PROJECT_ROOT}'"
202
- end tell
203
-
204
- -- Monitor 분리 후, gotchaPane (Rules 컬럼) 을 horizontal 두 번 split → Conventions / Memory
205
- set convPane to (split horizontally with default profile)
206
- tell convPane
207
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode conventions"
208
-
209
- set memPane to (split horizontally with default profile)
210
- tell memPane
211
- write text "cd '${PROJECT_ROOT}' && bash '${SCRIPT_DIR}/harness-gotcha-memory.sh' '${PROJECT_ROOT}' --mode memory"
212
- end tell
213
- end tell
214
- end tell
215
- end tell
216
- end tell
217
- end tell
218
- APPLESCRIPT
219
- }
220
-
221
- # ══════════════════════════════════════════
222
- # Strategy 2: tmux
223
- # ══════════════════════════════════════════
224
- launch_tmux_team() {
225
- tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
226
-
227
- PANE_DASH=$(tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 220 -y 55 -P -F '#{pane_id}')
228
- PANE_GOTCHA=$(tmux split-window -h -p 70 -t "$PANE_DASH" -c "$PROJECT_ROOT" -P -F '#{pane_id}')
229
- PANE_T1=$(tmux split-window -h -p 60 -t "$PANE_GOTCHA" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
230
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\" --team 1'")
231
- PANE_T2=$(tmux split-window -v -p 66 -t "$PANE_T1" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
232
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\" --team 2'")
233
- PANE_T3=$(tmux split-window -v -p 50 -t "$PANE_T2" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
234
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\" --team 3'")
235
- # Dashboard 열을 상(Dashboard)/하(Archive Prompt)로 분할 — archive가 길어져도 모니터링 유지
236
- PANE_ARCHIVE=$(tmux split-window -v -p 35 -t "$PANE_DASH" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
237
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-prompt-history.sh\" \"${PROJECT_ROOT}\"'")
238
-
239
- # Gotcha 패널을 3 개 sub-pane 으로 수직 분할: Gotchas / Conventions / Memory
240
- # 각 pane 은 tmux copy-mode (prefix + [) 로 독립 스크롤 가능
241
- PANE_CONV=$(tmux split-window -v -p 60 -t "$PANE_GOTCHA" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
242
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode conventions'")
243
- PANE_MEM=$(tmux split-window -v -p 50 -t "$PANE_CONV" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
244
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode memory'")
245
-
246
- tmux send-keys -t "$PANE_DASH" "bash \"${SCRIPT_DIR}/harness-dashboard.sh\" \"${PROJECT_ROOT}\"" Enter
247
- tmux send-keys -t "$PANE_GOTCHA" "bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode gotcha" Enter
248
-
249
- tmux select-pane -t "$PANE_DASH" -T "Dashboard"
250
- tmux select-pane -t "$PANE_ARCHIVE" -T "Archive Prompt"
251
- tmux select-pane -t "$PANE_GOTCHA" -T "Gotchas (scrollable: prefix + [ )"
252
- tmux select-pane -t "$PANE_CONV" -T "Conventions (scrollable: prefix + [ )"
253
- tmux select-pane -t "$PANE_MEM" -T "Shared Memory (scrollable: prefix + [ )"
254
- tmux select-pane -t "$PANE_T1" -T "TEAM 1"
255
- tmux select-pane -t "$PANE_T2" -T "TEAM 2"
256
- tmux select-pane -t "$PANE_T3" -T "TEAM 3"
257
- tmux select-pane -t "$PANE_DASH"
258
-
259
- tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
260
- tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true
261
- }
262
-
263
- launch_tmux_solo() {
264
- # Solo 레이아웃 (Team 모드와 동일 구조, 우측만 단일 Monitor 패널).
265
- tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
266
-
267
- PANE_DASH=$(tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 200 -y 50 -P -F '#{pane_id}')
268
- PANE_GOTCHA=$(tmux split-window -h -p 70 -t "$PANE_DASH" -c "$PROJECT_ROOT" -P -F '#{pane_id}')
269
- PANE_MONITOR=$(tmux split-window -h -p 50 -t "$PANE_GOTCHA" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
270
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'")
271
- PANE_HISTORY=$(tmux split-window -v -p 35 -t "$PANE_DASH" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
272
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-prompt-history.sh\" \"${PROJECT_ROOT}\"'")
273
-
274
- # Gotcha 패널을 3 개 sub-pane 으로 수직 분할: Gotchas / Conventions / Memory
275
- PANE_CONV=$(tmux split-window -v -p 60 -t "$PANE_GOTCHA" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
276
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode conventions'")
277
- PANE_MEM=$(tmux split-window -v -p 50 -t "$PANE_CONV" -c "$PROJECT_ROOT" -P -F '#{pane_id}' \
278
- "bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode memory'")
279
-
280
- tmux send-keys -t "$PANE_DASH" "bash \"${SCRIPT_DIR}/harness-dashboard.sh\" \"${PROJECT_ROOT}\"" Enter
281
- tmux send-keys -t "$PANE_GOTCHA" "bash \"${SCRIPT_DIR}/harness-gotcha-memory.sh\" \"${PROJECT_ROOT}\" --mode gotcha" Enter
282
-
283
- tmux select-pane -t "$PANE_DASH" -T "Dashboard"
284
- tmux select-pane -t "$PANE_HISTORY" -T "Prompt History"
285
- tmux select-pane -t "$PANE_GOTCHA" -T "Gotchas (scrollable: prefix + [ )"
286
- tmux select-pane -t "$PANE_CONV" -T "Conventions (scrollable: prefix + [ )"
287
- tmux select-pane -t "$PANE_MEM" -T "Shared Memory (scrollable: prefix + [ )"
288
- tmux select-pane -t "$PANE_MONITOR" -T "Monitor"
289
- tmux select-pane -t "$PANE_DASH"
290
-
291
- tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
292
- tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true
293
- }
294
-
295
- # ══════════════════════════════════════════
296
- # Dispatch: pick best strategy
297
- # ══════════════════════════════════════════
298
-
299
- # Detect iTerm2 — works even from Claude Code's non-TTY Bash
300
- IS_ITERM=false
301
- if [ "${TERM_PROGRAM:-}" = "iTerm.app" ]; then
302
- IS_ITERM=true
303
- elif [ "$(uname)" = "Darwin" ]; then
304
- # Check if iTerm2 is running (for non-TTY environments like Claude Code)
305
- if pgrep -q "iTerm2" 2>/dev/null; then
306
- IS_ITERM=true
307
- fi
308
- fi
309
-
310
- HAS_TMUX=false
311
- if command -v tmux &>/dev/null; then
312
- HAS_TMUX=true
313
- fi
314
-
315
- # --force-tmux overrides iTerm2 detection
316
- if [ "$FORCE_TMUX" = true ]; then
317
- if [ "$HAS_TMUX" != true ]; then
318
- echo "ERROR: --force-tmux specified but tmux not installed. Run: brew install tmux"
319
- exit 1
320
- fi
321
- IS_ITERM=false
322
- fi
323
-
324
- if [ "$IS_ITERM" = true ]; then
325
- echo "Strategy: iTerm2 native split panes"
326
- if [ "$MODE" = "team" ]; then
327
- launch_iterm2_team
328
- else
329
- launch_iterm2_solo
330
- fi
331
- echo "Layout ready"
332
-
333
- elif [ "$HAS_TMUX" = true ]; then
334
- echo "Strategy: tmux"
335
- if [ "$MODE" = "team" ]; then
336
- launch_tmux_team
337
- else
338
- launch_tmux_solo
339
- fi
340
-
341
- # Attach to tmux
342
- if [ "$DETACH" = true ]; then
343
- echo "Session created. Attach: tmux attach -t $SESSION_NAME"
344
- echo "Layout ready"
345
- elif ! tty -s 2>/dev/null; then
346
- # Non-TTY (e.g., Claude Code Bash tool) — open in new terminal
347
- if [ "$(uname)" = "Darwin" ]; then
348
- osascript -e "
349
- tell application \"Terminal\"
350
- do script \"tmux attach -t $SESSION_NAME\"
351
- activate
352
- end tell
353
- " 2>/dev/null && echo "OPENED_TERMINAL=true" || echo "Layout ready"
354
- else
355
- echo "Session created. Attach: tmux attach -t $SESSION_NAME"
356
- echo "Layout ready"
357
- fi
358
- else
359
- if [ -n "${TMUX:-}" ]; then
360
- tmux switch-client -t "$SESSION_NAME"
361
- else
362
- echo "Launching Harness Studio ($MODE mode)..."
363
- tmux attach -t "$SESSION_NAME"
364
- fi
365
- fi
366
-
367
- else
368
- echo "ERROR: Neither iTerm2 nor tmux found."
369
- echo "Install tmux: brew install tmux"
370
- echo "Or use iTerm2: https://iterm2.com"
371
- exit 1
372
- fi