shipwright-cli 1.10.0 → 2.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.
Files changed (108) hide show
  1. package/README.md +114 -36
  2. package/completions/_shipwright +212 -32
  3. package/completions/shipwright.bash +97 -25
  4. package/docs/strategy/01-market-research.md +619 -0
  5. package/docs/strategy/02-mission-and-brand.md +587 -0
  6. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  7. package/docs/strategy/QUICK-START.txt +289 -0
  8. package/docs/strategy/README.md +172 -0
  9. package/package.json +4 -2
  10. package/scripts/sw +208 -1
  11. package/scripts/sw-activity.sh +500 -0
  12. package/scripts/sw-adaptive.sh +925 -0
  13. package/scripts/sw-adversarial.sh +1 -1
  14. package/scripts/sw-architecture-enforcer.sh +1 -1
  15. package/scripts/sw-auth.sh +613 -0
  16. package/scripts/sw-autonomous.sh +664 -0
  17. package/scripts/sw-changelog.sh +704 -0
  18. package/scripts/sw-checkpoint.sh +1 -1
  19. package/scripts/sw-ci.sh +602 -0
  20. package/scripts/sw-cleanup.sh +1 -1
  21. package/scripts/sw-code-review.sh +637 -0
  22. package/scripts/sw-connect.sh +1 -1
  23. package/scripts/sw-context.sh +605 -0
  24. package/scripts/sw-cost.sh +1 -1
  25. package/scripts/sw-daemon.sh +432 -130
  26. package/scripts/sw-dashboard.sh +1 -1
  27. package/scripts/sw-db.sh +540 -0
  28. package/scripts/sw-decompose.sh +539 -0
  29. package/scripts/sw-deps.sh +551 -0
  30. package/scripts/sw-developer-simulation.sh +1 -1
  31. package/scripts/sw-discovery.sh +412 -0
  32. package/scripts/sw-docs-agent.sh +539 -0
  33. package/scripts/sw-docs.sh +1 -1
  34. package/scripts/sw-doctor.sh +59 -1
  35. package/scripts/sw-dora.sh +615 -0
  36. package/scripts/sw-durable.sh +710 -0
  37. package/scripts/sw-e2e-orchestrator.sh +535 -0
  38. package/scripts/sw-eventbus.sh +393 -0
  39. package/scripts/sw-feedback.sh +471 -0
  40. package/scripts/sw-fix.sh +1 -1
  41. package/scripts/sw-fleet-discover.sh +567 -0
  42. package/scripts/sw-fleet-viz.sh +404 -0
  43. package/scripts/sw-fleet.sh +8 -1
  44. package/scripts/sw-github-app.sh +596 -0
  45. package/scripts/sw-github-checks.sh +1 -1
  46. package/scripts/sw-github-deploy.sh +1 -1
  47. package/scripts/sw-github-graphql.sh +1 -1
  48. package/scripts/sw-guild.sh +569 -0
  49. package/scripts/sw-heartbeat.sh +1 -1
  50. package/scripts/sw-hygiene.sh +559 -0
  51. package/scripts/sw-incident.sh +617 -0
  52. package/scripts/sw-init.sh +88 -1
  53. package/scripts/sw-instrument.sh +699 -0
  54. package/scripts/sw-intelligence.sh +1 -1
  55. package/scripts/sw-jira.sh +1 -1
  56. package/scripts/sw-launchd.sh +363 -28
  57. package/scripts/sw-linear.sh +1 -1
  58. package/scripts/sw-logs.sh +1 -1
  59. package/scripts/sw-loop.sh +64 -3
  60. package/scripts/sw-memory.sh +1 -1
  61. package/scripts/sw-mission-control.sh +487 -0
  62. package/scripts/sw-model-router.sh +545 -0
  63. package/scripts/sw-otel.sh +596 -0
  64. package/scripts/sw-oversight.sh +689 -0
  65. package/scripts/sw-pipeline-composer.sh +1 -1
  66. package/scripts/sw-pipeline-vitals.sh +1 -1
  67. package/scripts/sw-pipeline.sh +687 -24
  68. package/scripts/sw-pm.sh +693 -0
  69. package/scripts/sw-pr-lifecycle.sh +522 -0
  70. package/scripts/sw-predictive.sh +1 -1
  71. package/scripts/sw-prep.sh +1 -1
  72. package/scripts/sw-ps.sh +1 -1
  73. package/scripts/sw-public-dashboard.sh +798 -0
  74. package/scripts/sw-quality.sh +595 -0
  75. package/scripts/sw-reaper.sh +1 -1
  76. package/scripts/sw-recruit.sh +573 -0
  77. package/scripts/sw-regression.sh +642 -0
  78. package/scripts/sw-release-manager.sh +736 -0
  79. package/scripts/sw-release.sh +706 -0
  80. package/scripts/sw-remote.sh +1 -1
  81. package/scripts/sw-replay.sh +520 -0
  82. package/scripts/sw-retro.sh +691 -0
  83. package/scripts/sw-scale.sh +444 -0
  84. package/scripts/sw-security-audit.sh +505 -0
  85. package/scripts/sw-self-optimize.sh +1 -1
  86. package/scripts/sw-session.sh +1 -1
  87. package/scripts/sw-setup.sh +1 -1
  88. package/scripts/sw-standup.sh +712 -0
  89. package/scripts/sw-status.sh +1 -1
  90. package/scripts/sw-strategic.sh +658 -0
  91. package/scripts/sw-stream.sh +450 -0
  92. package/scripts/sw-swarm.sh +583 -0
  93. package/scripts/sw-team-stages.sh +511 -0
  94. package/scripts/sw-templates.sh +1 -1
  95. package/scripts/sw-testgen.sh +515 -0
  96. package/scripts/sw-tmux-pipeline.sh +554 -0
  97. package/scripts/sw-tmux.sh +1 -1
  98. package/scripts/sw-trace.sh +485 -0
  99. package/scripts/sw-tracker-github.sh +188 -0
  100. package/scripts/sw-tracker-jira.sh +172 -0
  101. package/scripts/sw-tracker-linear.sh +251 -0
  102. package/scripts/sw-tracker.sh +117 -2
  103. package/scripts/sw-triage.sh +603 -0
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +677 -0
  106. package/scripts/sw-webhook.sh +627 -0
  107. package/scripts/sw-widgets.sh +530 -0
  108. package/scripts/sw-worktree.sh +1 -1
@@ -0,0 +1,677 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright ux — Premium UX Enhancement Layer ║
4
+ # ║ Themes · Animations · Keyboard Shortcuts · Accessibility · Formatting ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.0.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+
12
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
13
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
14
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
15
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
16
+ GREEN='\033[38;2;74;222;128m' # success
17
+ YELLOW='\033[38;2;250;204;21m' # warning
18
+ RED='\033[38;2;248;113;113m' # error
19
+ DIM='\033[2m'
20
+ BOLD='\033[1m'
21
+ RESET='\033[0m'
22
+
23
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
24
+ _COMPAT="$SCRIPT_DIR/lib/compat.sh"
25
+ # shellcheck source=lib/compat.sh
26
+ [[ -f "$_COMPAT" ]] && source "$_COMPAT"
27
+
28
+ # ─── Output Helpers ────────────────────────────────────────────────────────
29
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
+
34
+ # ─── Structured Event Log ──────────────────────────────────────────────────
35
+ EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
36
+
37
+ emit_event() {
38
+ local event_type="$1"
39
+ shift
40
+ local json_fields=""
41
+ for kv in "$@"; do
42
+ local key="${kv%%=*}"
43
+ local val="${kv#*=}"
44
+ if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
45
+ json_fields="${json_fields},\"${key}\":${val}"
46
+ else
47
+ val="${val//\"/\\\"}"
48
+ json_fields="${json_fields},\"${key}\":\"${val}\""
49
+ fi
50
+ done
51
+ mkdir -p "${HOME}/.shipwright"
52
+ echo "{\"ts\":\"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\",\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE" || true
53
+ }
54
+
55
+ # ─── UX Configuration ──────────────────────────────────────────────────────
56
+ UX_CONFIG_GLOBAL="${HOME}/.shipwright/ux-config.json"
57
+ UX_CONFIG_REPO="./.claude/ux-config.json"
58
+
59
+ # Detect accessibility modes
60
+ should_disable_colors() {
61
+ [[ -n "${NO_COLOR:-}" ]] || [[ -n "${CLICOLOR_FORCE:-}" && "$CLICOLOR_FORCE" == "0" ]]
62
+ }
63
+
64
+ has_reduced_motion() {
65
+ [[ -n "${PREFERS_REDUCED_MOTION:-}" ]]
66
+ }
67
+
68
+ # ─── Initialize UX Config ──────────────────────────────────────────────────
69
+ init_ux_config() {
70
+ local config_file="$1"
71
+ mkdir -p "$(dirname "$config_file")"
72
+
73
+ if [[ ! -f "$config_file" ]]; then
74
+ cat > "$config_file" <<'EOF'
75
+ {
76
+ "theme": "dark",
77
+ "spinner": "dots",
78
+ "animations_enabled": true,
79
+ "sound_enabled": false,
80
+ "reduced_motion": false,
81
+ "high_contrast": false,
82
+ "theme_custom": {}
83
+ }
84
+ EOF
85
+ fi
86
+ }
87
+
88
+ # ─── Theme System ──────────────────────────────────────────────────────────
89
+
90
+ # Theme data using functions (bash 3.2 compatible - no associative arrays)
91
+ theme_dark() {
92
+ echo "primary=#00d4ff"
93
+ echo "secondary=#7c3aed"
94
+ echo "tertiary=#0066ff"
95
+ echo "success=#4ade80"
96
+ echo "warning=#f59e0b"
97
+ echo "error=#f87171"
98
+ echo "bg=#0f172a"
99
+ echo "fg=#f1f5f9"
100
+ echo "dim=#64748b"
101
+ }
102
+
103
+ theme_light() {
104
+ echo "primary=#0066ff"
105
+ echo "secondary=#7c3aed"
106
+ echo "tertiary=#06b6d4"
107
+ echo "success=#059669"
108
+ echo "warning=#d97706"
109
+ echo "error=#dc2626"
110
+ echo "bg=#ffffff"
111
+ echo "fg=#1e293b"
112
+ echo "dim=#94a3b8"
113
+ }
114
+
115
+ theme_minimal() {
116
+ echo "primary=#000000"
117
+ echo "secondary=#808080"
118
+ echo "tertiary=#808080"
119
+ echo "success=#000000"
120
+ echo "warning=#000000"
121
+ echo "error=#000000"
122
+ echo "bg=#ffffff"
123
+ echo "fg=#000000"
124
+ echo "dim=#cccccc"
125
+ }
126
+
127
+ theme_cyberpunk() {
128
+ echo "primary=#ff006e"
129
+ echo "secondary=#00f5ff"
130
+ echo "tertiary=#ffbe0b"
131
+ echo "success=#00ff41"
132
+ echo "warning=#ffbe0b"
133
+ echo "error=#ff006e"
134
+ echo "bg=#0a0e27"
135
+ echo "fg=#00f5ff"
136
+ echo "dim=#555580"
137
+ }
138
+
139
+ theme_ocean() {
140
+ echo "primary=#0ea5e9"
141
+ echo "secondary=#06b6d4"
142
+ echo "tertiary=#0891b2"
143
+ echo "success=#10b981"
144
+ echo "warning=#f59e0b"
145
+ echo "error=#ef4444"
146
+ echo "bg=#001f3f"
147
+ echo "fg=#e0f2fe"
148
+ echo "dim=#38bdf8"
149
+ }
150
+
151
+ get_theme() {
152
+ local theme_name="${1:-dark}"
153
+ case "$theme_name" in
154
+ light) theme_light ;;
155
+ minimal) theme_minimal ;;
156
+ cyberpunk) theme_cyberpunk ;;
157
+ ocean) theme_ocean ;;
158
+ *) theme_dark ;;
159
+ esac
160
+ }
161
+
162
+ hex_to_rgb() {
163
+ local hex="${1#\#}"
164
+ printf '%d;%d;%d' 0x"${hex:0:2}" 0x"${hex:2:2}" 0x"${hex:4:2}"
165
+ }
166
+
167
+ # Get color code from theme
168
+ get_color() {
169
+ local color_name="$1"
170
+ local theme="${2:-dark}"
171
+
172
+ if should_disable_colors; then
173
+ return 0
174
+ fi
175
+
176
+ local hex
177
+ hex=$(get_theme "$theme" | grep "^${color_name}=" | cut -d= -f2)
178
+
179
+ if [[ -n "$hex" ]]; then
180
+ local rgb
181
+ rgb=$(hex_to_rgb "$hex")
182
+ echo -ne "\033[38;2;${rgb}m"
183
+ fi
184
+ }
185
+
186
+ list_themes() {
187
+ info "Available themes:"
188
+ for theme in dark light minimal cyberpunk ocean; do
189
+ echo " ${CYAN}${theme}${RESET}"
190
+ done
191
+ }
192
+
193
+ set_theme() {
194
+ local theme="$1"
195
+
196
+ init_ux_config "$UX_CONFIG_GLOBAL"
197
+
198
+ if jq --arg theme "$theme" '.theme = $theme' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
199
+ mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
200
+ success "Theme set to: ${CYAN}${theme}${RESET}"
201
+ emit_event "ux_theme_changed" "theme=$theme"
202
+ else
203
+ error "Failed to set theme"
204
+ return 1
205
+ fi
206
+ }
207
+
208
+ preview_theme() {
209
+ local theme="${1:-dark}"
210
+
211
+ echo ""
212
+ info "Preview of ${CYAN}${theme}${RESET} theme:"
213
+ echo ""
214
+
215
+ while IFS='=' read -r key value; do
216
+ [[ -z "$key" ]] && continue
217
+ local rgb
218
+ rgb=$(hex_to_rgb "$value")
219
+ printf " %-12s ${value} \033[38;2;${rgb}m████████████${RESET}\n" "$key"
220
+ done < <(get_theme "$theme")
221
+ echo ""
222
+ }
223
+
224
+ # ─── Spinner Animations ────────────────────────────────────────────────────
225
+
226
+ spinner_dots() {
227
+ echo "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
228
+ }
229
+
230
+ spinner_braille() {
231
+ echo "⠋ ⠙ ⠚ ⠒ ⠂ ⠂ ⠒ ⠲ ⠴ ⠦"
232
+ }
233
+
234
+ spinner_moon() {
235
+ echo "🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘"
236
+ }
237
+
238
+ spinner_arrows() {
239
+ echo "← ↖ ↑ ↗ → ↘ ↓ ↙"
240
+ }
241
+
242
+ spinner_bounce() {
243
+ echo "⠁ ⠂ ⠄ ⠂"
244
+ }
245
+
246
+ get_spinner() {
247
+ local style="${1:-dots}"
248
+ case "$style" in
249
+ braille) spinner_braille ;;
250
+ moon) spinner_moon ;;
251
+ arrows) spinner_arrows ;;
252
+ bounce) spinner_bounce ;;
253
+ *) spinner_dots ;;
254
+ esac
255
+ }
256
+
257
+ animate_spinner() {
258
+ local message="$1"
259
+ local duration="${2:-10}"
260
+ local style="${3:-dots}"
261
+
262
+ if has_reduced_motion; then
263
+ echo "⏳ $message"
264
+ sleep "$duration"
265
+ return 0
266
+ fi
267
+
268
+ local spinner_str
269
+ spinner_str="$(get_spinner "$style")"
270
+ local i=0
271
+ local end_time=$(($(date +%s) + duration))
272
+
273
+ while [[ $(date +%s) -lt $end_time ]]; do
274
+ # Parse space-separated spinner frames
275
+ local frame
276
+ frame=$(echo "$spinner_str" | awk '{for(n=1;n<=NF;n++) if((n-1)==('$((i % 10))')) print $n}')
277
+ printf "\r${frame} $message"
278
+ i=$((i + 1))
279
+ sleep 0.1
280
+ done
281
+ printf "\r%-80s\r" ""
282
+ }
283
+
284
+ list_spinners() {
285
+ info "Available spinners:"
286
+ for style in dots braille moon arrows bounce; do
287
+ printf " ${CYAN}%-12s${RESET} "
288
+ local spinner_str
289
+ spinner_str="$(get_spinner "$style")"
290
+ echo "$spinner_str" | awk '{for(i=1;i<=3 && i<=NF;i++) printf "%s ", $i; print "..."}'
291
+ done
292
+ }
293
+
294
+ # ─── Progress Bar ──────────────────────────────────────────────────────────
295
+ show_progress() {
296
+ local current="$1"
297
+ local total="$2"
298
+ local label="${3:-Progress}"
299
+
300
+ local percent=$((current * 100 / total))
301
+ local filled=$((percent / 5))
302
+ local empty=$((20 - filled))
303
+
304
+ printf "\r${CYAN}${label}${RESET} ["
305
+ printf "%${filled}s" | tr ' ' '='
306
+ printf "%${empty}s" | tr ' ' '-'
307
+ printf "] %3d%% " "$percent"
308
+ }
309
+
310
+ # ─── Box Drawing Helpers ───────────────────────────────────────────────────
311
+ box_title() {
312
+ local title="$1"
313
+ local width="${2:-70}"
314
+
315
+ echo "╔$(printf '═%.0s' $(seq 1 $((width-2))))╗"
316
+ printf "║ %-$((width-3))s║\n" "$title"
317
+ echo "╚$(printf '═%.0s' $(seq 1 $((width-2))))╝"
318
+ }
319
+
320
+ box_section() {
321
+ local title="$1"
322
+ local width="${2:-70}"
323
+
324
+ echo "┌─ ${CYAN}${title}${RESET}"
325
+ echo "│"
326
+ }
327
+
328
+ box_end() {
329
+ echo "└─"
330
+ }
331
+
332
+ # ─── Table Formatting ──────────────────────────────────────────────────────
333
+ table_header() {
334
+ local cols=("$@")
335
+ local widths=()
336
+
337
+ # Calculate column widths
338
+ for col in "${cols[@]}"; do
339
+ widths+=($((${#col} + 2)))
340
+ done
341
+
342
+ # Print header
343
+ for i in "${!cols[@]}"; do
344
+ printf "%-${widths[$i]}s" "${cols[$i]}"
345
+ done
346
+ echo ""
347
+
348
+ # Print separator
349
+ for w in "${widths[@]}"; do
350
+ printf '%*s' "$w" | tr ' ' '─'
351
+ done
352
+ echo ""
353
+ }
354
+
355
+ table_row() {
356
+ local cols=("$@")
357
+ local widths=()
358
+
359
+ # This is a simple implementation; in production you'd track column widths
360
+ for col in "${cols[@]}"; do
361
+ printf "%-20s" "$col"
362
+ done
363
+ echo ""
364
+ }
365
+
366
+ # ─── Keyboard Shortcuts System ─────────────────────────────────────────────
367
+
368
+ show_shortcuts() {
369
+ box_title "Keyboard Shortcuts"
370
+ echo ""
371
+
372
+ # Define shortcuts as separate lines (bash 3.2 compatible)
373
+ printf " ${CYAN}%-20s${RESET} %s\n" "q" "Exit the current menu"
374
+ printf " ${CYAN}%-20s${RESET} %s\n" "h" "Show help overlay"
375
+ printf " ${CYAN}%-20s${RESET} %s\n" "t" "Cycle through themes"
376
+ printf " ${CYAN}%-20s${RESET} %s\n" "s" "Toggle notification sound"
377
+ printf " ${CYAN}%-20s${RESET} %s\n" "m" "Toggle animation/reduced motion"
378
+ printf " ${CYAN}%-20s${RESET} %s\n" "c" "Toggle high-contrast mode"
379
+ printf " ${CYAN}%-20s${RESET} %s\n" "1-5" "Jump to pipeline stage 1-5"
380
+ printf " ${CYAN}%-20s${RESET} %s\n" "space" "Pause or resume pipeline"
381
+ printf " ${CYAN}%-20s${RESET} %s\n" "r" "Reload configuration"
382
+
383
+ box_end
384
+ }
385
+
386
+ # ─── Accessibility Support ────────────────────────────────────────────────
387
+ set_high_contrast() {
388
+ init_ux_config "$UX_CONFIG_GLOBAL"
389
+
390
+ if jq '.high_contrast = true' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
391
+ mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
392
+ success "High contrast mode enabled"
393
+ emit_event "ux_accessibility" "feature=high_contrast"
394
+ fi
395
+ }
396
+
397
+ set_reduced_motion() {
398
+ init_ux_config "$UX_CONFIG_GLOBAL"
399
+ export PREFERS_REDUCED_MOTION=1
400
+
401
+ if jq '.reduced_motion = true' "$UX_CONFIG_GLOBAL" > "${UX_CONFIG_GLOBAL}.tmp" 2>/dev/null; then
402
+ mv "${UX_CONFIG_GLOBAL}.tmp" "$UX_CONFIG_GLOBAL"
403
+ success "Reduced motion mode enabled"
404
+ emit_event "ux_accessibility" "feature=reduced_motion"
405
+ fi
406
+ }
407
+
408
+ enable_screen_reader_mode() {
409
+ # Disable colors and animations for screen reader compatibility
410
+ export NO_COLOR=1
411
+ export PREFERS_REDUCED_MOTION=1
412
+ success "Screen reader mode enabled (colors and animations disabled)"
413
+ emit_event "ux_accessibility" "feature=screen_reader"
414
+ }
415
+
416
+ # ─── Formatting Helpers ───────────────────────────────────────────────────
417
+ format_diff_line() {
418
+ local line="$1"
419
+
420
+ case "$line" in
421
+ +*) echo -e "${GREEN}${line}${RESET}" ;;
422
+ -*) echo -e "${RED}${line}${RESET}" ;;
423
+ @@*) echo -e "${BLUE}${line}${RESET}" ;;
424
+ *) echo "$line" ;;
425
+ esac
426
+ }
427
+
428
+ format_tree() {
429
+ local depth="$1"
430
+ local name="$2"
431
+ local is_last="${3:-true}"
432
+
433
+ local indent=""
434
+ for _ in $(seq 1 "$depth"); do
435
+ indent+=" "
436
+ done
437
+
438
+ local prefix="├─"
439
+ [[ "$is_last" == "true" ]] && prefix="└─"
440
+
441
+ echo "${indent}${prefix} ${name}"
442
+ }
443
+
444
+ # ─── Notification Sounds ───────────────────────────────────────────────────
445
+ notify_complete() {
446
+ local message="${1:-Complete}"
447
+
448
+ init_ux_config "$UX_CONFIG_GLOBAL"
449
+ local sound_enabled
450
+ sound_enabled=$(jq -r '.sound_enabled // false' "$UX_CONFIG_GLOBAL" 2>/dev/null || echo "false")
451
+
452
+ if [[ "$sound_enabled" == "true" ]]; then
453
+ # Use system beep (works on macOS and Linux)
454
+ printf '\a'
455
+ fi
456
+
457
+ success "$message"
458
+ emit_event "ux_notification" "type=complete" "message=$message"
459
+ }
460
+
461
+ notify_error() {
462
+ local message="${1:-Error occurred}"
463
+
464
+ init_ux_config "$UX_CONFIG_GLOBAL"
465
+ local sound_enabled
466
+ sound_enabled=$(jq -r '.sound_enabled // false' "$UX_CONFIG_GLOBAL" 2>/dev/null || echo "false")
467
+
468
+ if [[ "$sound_enabled" == "true" ]]; then
469
+ printf '\a\a' # Double beep for errors
470
+ fi
471
+
472
+ error "$message"
473
+ emit_event "ux_notification" "type=error" "message=$message"
474
+ }
475
+
476
+ # ─── Configuration Management ──────────────────────────────────────────────
477
+ show_config() {
478
+ init_ux_config "$UX_CONFIG_GLOBAL"
479
+
480
+ info "UX Configuration:"
481
+ if [[ -f "$UX_CONFIG_GLOBAL" ]]; then
482
+ jq '.' "$UX_CONFIG_GLOBAL" 2>/dev/null || cat "$UX_CONFIG_GLOBAL"
483
+ else
484
+ echo "No configuration found"
485
+ fi
486
+ }
487
+
488
+ reset_config() {
489
+ rm -f "$UX_CONFIG_GLOBAL"
490
+ init_ux_config "$UX_CONFIG_GLOBAL"
491
+ success "UX configuration reset to defaults"
492
+ emit_event "ux_reset" "scope=global"
493
+ }
494
+
495
+ # ─── Demo Mode ──────────────────────────────────────────────────────────────
496
+ run_demo() {
497
+ clear
498
+
499
+ box_title "Shipwright UX Enhancement System Demo"
500
+ echo ""
501
+
502
+ # Demo themes
503
+ info "Theme Previews:"
504
+ for theme in dark light cyberpunk ocean; do
505
+ preview_theme "$theme"
506
+ sleep 0.5
507
+ done
508
+
509
+ # Demo spinners
510
+ info "Spinner Styles:"
511
+ for style in dots braille moon arrows; do
512
+ printf " ${CYAN}%-12s${RESET} " "$style"
513
+ animate_spinner "Loading" 2 "$style"
514
+ echo " ${GREEN}✓${RESET}"
515
+ done
516
+
517
+ echo ""
518
+ info "Progress Bar Demo:"
519
+ for i in {0..100..10}; do
520
+ show_progress "$i" 100 "Building"
521
+ sleep 0.3
522
+ done
523
+ printf "\n"
524
+
525
+ # Demo box drawing
526
+ echo ""
527
+ info "Box Drawing Helpers:"
528
+ box_title "Example Section"
529
+ box_section "Content Area"
530
+ echo " This is sample content with structured formatting"
531
+ box_end
532
+
533
+ echo ""
534
+ show_shortcuts
535
+
536
+ echo ""
537
+ success "Demo complete!"
538
+ }
539
+
540
+ # ─── Help Text ──────────────────────────────────────────────────────────────
541
+ show_help() {
542
+ cat <<'EOF'
543
+ shipwright ux — Premium UX Enhancement Layer
544
+
545
+ USAGE
546
+ shipwright ux <subcommand> [options]
547
+
548
+ SUBCOMMANDS
549
+ theme <name> Set theme (dark, light, minimal, cyberpunk, ocean)
550
+ theme list Show available themes
551
+ theme preview [name] Preview a theme
552
+
553
+ spinner [style] Show spinner style (dots, braille, moon, arrows, bounce)
554
+ spinner list Show all spinner styles
555
+
556
+ config show Display current UX configuration
557
+ config reset Reset to default configuration
558
+
559
+ shortcuts Show keyboard shortcut reference
560
+
561
+ accessibility Configure accessibility options
562
+ --high-contrast Enable high contrast mode
563
+ --reduced-motion Enable reduced motion (no animations)
564
+ --screen-reader Full screen reader mode
565
+
566
+ demo Run interactive UX feature demo
567
+ help Show this help message
568
+
569
+ OPTIONS
570
+ --no-color Disable all color output
571
+ --prefers-reduced-motion Disable animations
572
+
573
+ EXAMPLES
574
+ shipwright ux theme dark
575
+ shipwright ux theme preview cyberpunk
576
+ shipwright ux spinner list
577
+ shipwright ux accessibility --high-contrast
578
+ shipwright ux demo
579
+
580
+ CONFIGURATION FILES
581
+ Global: ~/.shipwright/ux-config.json
582
+ Project: ./.claude/ux-config.json (overrides global)
583
+
584
+ EOF
585
+ }
586
+
587
+ # ─── Main Command Router ──────────────────────────────────────────────────
588
+ main() {
589
+ local cmd="${1:-help}"
590
+ shift 2>/dev/null || true
591
+
592
+ case "$cmd" in
593
+ theme)
594
+ local theme_cmd="${1:-list}"
595
+ shift 2>/dev/null || true
596
+ case "$theme_cmd" in
597
+ list)
598
+ list_themes
599
+ ;;
600
+ preview)
601
+ preview_theme "${1:-dark}"
602
+ ;;
603
+ *)
604
+ set_theme "$theme_cmd"
605
+ ;;
606
+ esac
607
+ ;;
608
+ spinner)
609
+ local spinner_cmd="${1:-dots}"
610
+ case "$spinner_cmd" in
611
+ list)
612
+ list_spinners
613
+ ;;
614
+ *)
615
+ animate_spinner "Demo animation" 3 "$spinner_cmd"
616
+ echo ""
617
+ success "Spinner demo complete"
618
+ ;;
619
+ esac
620
+ ;;
621
+ config)
622
+ local config_cmd="${1:-show}"
623
+ case "$config_cmd" in
624
+ show)
625
+ show_config
626
+ ;;
627
+ reset)
628
+ reset_config
629
+ ;;
630
+ *)
631
+ error "Unknown config command: $config_cmd"
632
+ exit 1
633
+ ;;
634
+ esac
635
+ ;;
636
+ shortcuts)
637
+ show_shortcuts
638
+ ;;
639
+ accessibility)
640
+ while [[ $# -gt 0 ]]; do
641
+ case "$1" in
642
+ --high-contrast)
643
+ set_high_contrast
644
+ ;;
645
+ --reduced-motion)
646
+ set_reduced_motion
647
+ ;;
648
+ --screen-reader)
649
+ enable_screen_reader_mode
650
+ ;;
651
+ *)
652
+ error "Unknown accessibility option: $1"
653
+ exit 1
654
+ ;;
655
+ esac
656
+ shift
657
+ done
658
+ ;;
659
+ demo)
660
+ run_demo
661
+ ;;
662
+ help|--help|-h)
663
+ show_help
664
+ ;;
665
+ *)
666
+ error "Unknown command: $cmd"
667
+ echo ""
668
+ show_help
669
+ exit 1
670
+ ;;
671
+ esac
672
+ }
673
+
674
+ # Source guard: allow direct execution and sourcing
675
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
676
+ main "$@"
677
+ fi