@humanu/orchestra 0.5.73 → 0.5.75

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanu/orchestra",
3
- "version": "0.5.73",
3
+ "version": "0.5.75",
4
4
  "description": "AI-powered Git worktree and tmux session manager with modern TUI",
5
5
  "keywords": [
6
6
  "git",
@@ -434,7 +434,7 @@ with open(active_path, "r", encoding="utf-8") as handle:
434
434
  line = raw_line.rstrip("\n")
435
435
  if not line:
436
436
  continue
437
- parts = line.split("\t", 1)
437
+ parts = line.split("\t")
438
438
  name = parts[0].strip()
439
439
  display_name = parts[1].strip() if len(parts) > 1 else ""
440
440
  if not name:
@@ -506,14 +506,6 @@ _tmux_truncate_tab_label() {
506
506
  fi
507
507
  }
508
508
 
509
- _tmux_pad_tab_label() {
510
- local label="$1"
511
- local width="${2:-14}"
512
-
513
- label="$(_tmux_truncate_tab_label "$label" "$width")"
514
- printf '%-*s' "$width" "$label"
515
- }
516
-
517
509
  _tmux_workspace_session_rows() {
518
510
  local current_session="$1"
519
511
  local repo_root="$2"
@@ -589,8 +581,8 @@ _tmux_workspace_session_tabs() {
589
581
  local current_session="$1"
590
582
  local fallback_display_name="$2"
591
583
  local session_dir repo_root rows tabs name display_name tab_label escaped_tab_label divider active_style inactive_style muted_style reset_style
592
- local tab_width=21
593
- local active_label_width=19
584
+ local tab_max_width=24
585
+ local active_label_max_width=22
594
586
 
595
587
  session_dir="$(tmux display-message -t "$current_session" -p '#{pane_current_path}' 2>/dev/null || echo "")"
596
588
  repo_root="$(_tmux_shared_root_for_path "$session_dir" 2>/dev/null || true)"
@@ -602,7 +594,7 @@ _tmux_workspace_session_tabs() {
602
594
  fi
603
595
 
604
596
  if [[ -z "$rows" ]]; then
605
- tab_label="$(_tmux_pad_tab_label "$fallback_display_name" "$active_label_width")"
597
+ tab_label="$(_tmux_truncate_tab_label "$fallback_display_name" "$active_label_max_width")"
606
598
  escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
607
599
  printf '#[bg=#414868] #[fg=#ff9e64,bg=#414868,bold]● #[fg=#c0caf5,bg=#414868,bold]%s #[default]' "$escaped_tab_label"
608
600
  return
@@ -621,7 +613,7 @@ _tmux_workspace_session_tabs() {
621
613
  tabs+="$divider"
622
614
  fi
623
615
  if [[ "$name" == "__ellipsis__" ]]; then
624
- tab_label="$(_tmux_pad_tab_label "⋯" "$tab_width")"
616
+ tab_label="⋯"
625
617
  escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
626
618
  tabs+="${muted_style} ${escaped_tab_label} ${reset_style}"
627
619
  continue
@@ -631,11 +623,11 @@ _tmux_workspace_session_tabs() {
631
623
  display_name="$(tmux_format_session_display "$name" without-timestamp)"
632
624
  fi
633
625
  if [[ "$name" == "$current_session" ]]; then
634
- tab_label="$(_tmux_pad_tab_label "$display_name" "$active_label_width")"
626
+ tab_label="$(_tmux_truncate_tab_label "$display_name" "$active_label_max_width")"
635
627
  escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
636
628
  tabs+="#[bg=#414868] #[fg=#ff9e64,bg=#414868,bold]● ${active_style}${escaped_tab_label} ${reset_style}"
637
629
  else
638
- tab_label="$(_tmux_pad_tab_label "$display_name" "$tab_width")"
630
+ tab_label="$(_tmux_truncate_tab_label "$display_name" "$tab_max_width")"
639
631
  escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
640
632
  tabs+="${inactive_style} ${escaped_tab_label} ${reset_style}"
641
633
  fi
@@ -651,7 +643,7 @@ _tmux_orchestra_status_left() {
651
643
  }
652
644
 
653
645
  _tmux_orchestra_status_right() {
654
- printf ''
646
+ printf '#[fg=#565f89,bg=#1a1b26]Ctrl+b,h for help#[default]'
655
647
  }
656
648
 
657
649
  _tmux_configure_orchestra_bindings() {
@@ -659,16 +651,21 @@ _tmux_configure_orchestra_bindings() {
659
651
  bridge="$(_orchestra_bridge_script)"
660
652
  [[ -f "$bridge" ]] || return
661
653
 
662
- local quoted_bridge rename_command prompt_command next_command previous_command help_command
654
+ local quoted_bridge rename_command prompt_command next_command previous_command new_session_command help_command
663
655
  printf -v quoted_bridge '%q' "$bridge"
664
656
  rename_command="$quoted_bridge manual-rename-session \\\"#{session_name}\\\" \\\"%%\\\" >/dev/null 2>&1"
665
657
  prompt_command="command-prompt -p 'Rename Orchestra session:' 'run-shell -b \"$rename_command\"'"
666
658
  next_command="$quoted_bridge cycle-workspace-session \\\"#{session_name}\\\" next \\\"#{client_tty}\\\" >/dev/null 2>&1"
667
659
  previous_command="$quoted_bridge cycle-workspace-session \\\"#{session_name}\\\" previous \\\"#{client_tty}\\\" >/dev/null 2>&1"
660
+ new_session_command="$quoted_bridge create-workspace-session \\\"#{session_name}\\\" \\\"#{client_tty}\\\" >/dev/null 2>&1"
668
661
  help_command="$quoted_bridge tmux-help-popup \\\"#{client_tty}\\\" >/dev/null 2>&1"
669
662
 
670
663
  tmux bind-key -T prefix '?' if-shell -F '#{@orchestra_display_name}' \
671
664
  "run-shell -b \"$help_command\"" 'list-keys -N' >/dev/null 2>&1 || true
665
+ tmux bind-key -T prefix h if-shell -F '#{@orchestra_display_name}' \
666
+ "run-shell -b \"$help_command\"" 'refresh-client -S' >/dev/null 2>&1 || true
667
+ tmux bind-key -T prefix n if-shell -F '#{@orchestra_display_name}' \
668
+ "run-shell -b \"$new_session_command\"" 'next-window' >/dev/null 2>&1 || true
672
669
  tmux bind-key -T prefix r if-shell -F '#{@orchestra_display_name}' \
673
670
  "$prompt_command" 'refresh-client -S' >/dev/null 2>&1 || true
674
671
  tmux bind-key -T prefix '>' if-shell -F '#{@orchestra_display_name}' \
@@ -700,7 +697,7 @@ _tmux_configure_orchestra_status() {
700
697
  tmux set-option -t "$session_name" status-left "$status_left" >/dev/null 2>&1 || true
701
698
  tmux set-option -t "$session_name" status-left-length 1000 >/dev/null 2>&1 || true
702
699
  tmux set-option -t "$session_name" status-right "$status_right" >/dev/null 2>&1 || true
703
- tmux set-option -t "$session_name" status-right-length 0 >/dev/null 2>&1 || true
700
+ tmux set-option -t "$session_name" status-right-length 40 >/dev/null 2>&1 || true
704
701
  tmux set-option -t "$session_name" window-status-format "" >/dev/null 2>&1 || true
705
702
  tmux set-option -t "$session_name" window-status-current-format "" >/dev/null 2>&1 || true
706
703
  tmux set-option -t "$session_name" window-status-separator "" >/dev/null 2>&1 || true
@@ -906,6 +903,47 @@ tmux_ensure_session() {
906
903
  _tmux_source_command_hook "$sess"
907
904
  }
908
905
 
906
+ # Create a new auto-named Orchestra session in the current session's worktree and switch to it.
907
+ # Uses the same tmux_ensure_session path as the TUI tree view create action.
908
+ # Usage: tmux_create_workspace_session <current_session> [client_tty]
909
+ tmux_create_workspace_session() {
910
+ local current_session="$1"
911
+ local target_client="${2:-}"
912
+ local session_dir branch slug new_session
913
+
914
+ if ! tmux_available; then
915
+ err "tmux not installed"
916
+ return 1
917
+ fi
918
+ if [[ -z "$current_session" ]]; then
919
+ err "tmux_create_workspace_session: current session required"
920
+ return 1
921
+ fi
922
+
923
+ session_dir="$(tmux display-message -t "$current_session" -p '#{pane_current_path}' 2>/dev/null || echo "")"
924
+ if [[ -z "$session_dir" || ! -d "$session_dir" ]]; then
925
+ err "Unable to determine current worktree path"
926
+ return 1
927
+ fi
928
+
929
+ branch="$(_tmux_branch_for_path "$session_dir" 2>/dev/null || true)"
930
+ if [[ -z "$branch" || "$branch" == "detached" ]]; then
931
+ err "Unable to determine current worktree branch"
932
+ return 1
933
+ fi
934
+
935
+ slug="$(git_branch_to_slug "$branch")"
936
+ new_session="$(tmux_ensure_session "$slug" "" "$session_dir")" || return 1
937
+
938
+ if [[ -n "$target_client" ]]; then
939
+ tmux switch-client -c "$target_client" -t "$new_session" >/dev/null 2>&1 || return 1
940
+ else
941
+ tmux switch-client -t "$new_session" >/dev/null 2>&1 || return 1
942
+ fi
943
+
944
+ printf '%s\n' "$new_session"
945
+ }
946
+
909
947
  # Check if a session exists
910
948
  # Usage: tmux_session_exists <session_name>
911
949
  tmux_session_exists() {
@@ -1083,7 +1121,7 @@ tmux_rename_session() {
1083
1121
 
1084
1122
  # --------------------------- Session Discovery ------------------------------
1085
1123
 
1086
- # Get tmux sessions for a slug prefix; prints session names sorted by last attached/activity
1124
+ # Get tmux sessions for a slug prefix; prints session names sorted by creation time, oldest first
1087
1125
  # Expected session format: [worktreename]_[worktreehash]_[datetime]_[readable_name]
1088
1126
  # Usage: tmux_list_sessions_for_slug <slug> [worktree_path]
1089
1127
  tmux_list_sessions_for_slug() {
@@ -1114,19 +1152,19 @@ tmux_list_sessions_for_slug() {
1114
1152
  fi
1115
1153
 
1116
1154
  # List sessions with any known prefix variant
1117
- tmux list-sessions -F '#{session_name}|||#{session_last_attached}|||#{session_activity}' 2>/dev/null \
1155
+ tmux list-sessions -F '#{session_name}|||#{session_created}' 2>/dev/null \
1118
1156
  | sed 's/|||/\t/g' \
1119
- | awk -v a="$p1_new" -v b="$p2_new" -v c="$p1_old" -v d="$p2_old" -v e="$p1_branch_hash" -v f="$p2_branch_hash" 'BEGIN{FS="\t"} (length(a)>0 && index($1, a)==1) || (length(b)>0 && index($1, b)==1) || (length(c)>0 && index($1, c)==1) || (length(d)>0 && index($1, d)==1) || (length(e)>0 && index($1, e)==1) || (length(f)>0 && index($1, f)==1) {print $1"\t"$2"\t"$3}' \
1120
- | sort -t $'\t' -k2,2nr -k3,3nr \
1157
+ | awk -v a="$p1_new" -v b="$p2_new" -v c="$p1_old" -v d="$p2_old" -v e="$p1_branch_hash" -v f="$p2_branch_hash" 'BEGIN{FS="\t"} (length(a)>0 && index($1, a)==1) || (length(b)>0 && index($1, b)==1) || (length(c)>0 && index($1, c)==1) || (length(d)>0 && index($1, d)==1) || (length(e)>0 && index($1, e)==1) || (length(f)>0 && index($1, f)==1) {print $1"\t"$2}' \
1158
+ | sort -t $'\t' -k2,2n -k1,1f \
1121
1159
  | awk -F '\t' '{print $1}' | awk '!seen[$0]++' || true
1122
1160
  else
1123
1161
  # New-format prefix matching without hash: orchestra__slug__... or slug__...
1124
1162
  local prefix1="${ORCH_PREFIX}${slug}${d}"
1125
1163
  local prefix2="${slug}${d}"
1126
- tmux list-sessions -F '#{session_name}|||#{session_last_attached}|||#{session_activity}' 2>/dev/null \
1164
+ tmux list-sessions -F '#{session_name}|||#{session_created}' 2>/dev/null \
1127
1165
  | sed 's/|||/\t/g' \
1128
- | awk -v p1="$prefix1" -v p2="$prefix2" 'BEGIN{FS="\t"} index($1, p1)==1 || index($1, p2)==1 {print $1"\t"$2"\t"$3}' \
1129
- | sort -t $'\t' -k2,2nr -k3,3nr \
1166
+ | awk -v p1="$prefix1" -v p2="$prefix2" 'BEGIN{FS="\t"} index($1, p1)==1 || index($1, p2)==1 {print $1"\t"$2}' \
1167
+ | sort -t $'\t' -k2,2n -k1,1f \
1130
1168
  | awk -F '\t' '{print $1}' || true
1131
1169
  fi
1132
1170
  }
@@ -1509,21 +1547,33 @@ tmux_show_orchestra_help_popup() {
1509
1547
  fi
1510
1548
 
1511
1549
  local popup_command
1512
- popup_command='printf "%s\n" \
1550
+ popup_command="$(cat <<'EOF'
1551
+ bash -lc '
1552
+ trap "exit 0" INT TERM
1553
+ printf "%s\n" \
1513
1554
  "Orchestra tmux shortcuts" \
1514
1555
  "" \
1556
+ "Ctrl+b, d Detach and return to Orchestra" \
1557
+ "Ctrl+b, r Rename the current Orchestra session" \
1558
+ "Ctrl+b, n New Orchestra session in this worktree" \
1559
+ "" \
1515
1560
  "Ctrl+b, Left Previous Orchestra session in this workspace" \
1516
1561
  "Ctrl+b, Right Next Orchestra session in this workspace" \
1517
1562
  "Ctrl+b, < Previous Orchestra session in this workspace" \
1518
1563
  "Ctrl+b, > Next Orchestra session in this workspace" \
1519
- "Ctrl+b, r Rename the current Orchestra session" \
1520
- "Ctrl+b, d Detach and return to Orchestra" \
1564
+ "" \
1521
1565
  "Ctrl+b, [ Copy/scroll mode" \
1566
+ "Ctrl+b, h Show this help" \
1522
1567
  "Ctrl+b, ? Show this help" \
1523
1568
  "" \
1524
- "Press Enter to close..."; read -r _'
1569
+ "Press any key to close..."
1570
+ IFS= read -rsn1 _ || true
1571
+ exit 0
1572
+ '
1573
+ EOF
1574
+ )"
1525
1575
 
1526
- tmux display-popup "${target_args[@]}" -E -w 78 -h 15 -T "Orchestra shortcuts" "$popup_command" >/dev/null 2>&1
1576
+ tmux display-popup "${target_args[@]}" -E -w 78 -h 18 -T "Orchestra shortcuts" "$popup_command" >/dev/null 2>&1 || true
1527
1577
  }
1528
1578
 
1529
1579
  # Find the adjacent active Orchestra session registered for the current repo.
@@ -1556,7 +1606,7 @@ tmux_workspace_cycle_target() {
1556
1606
  }
1557
1607
 
1558
1608
  active_file="$(mktemp)"
1559
- tmux list-sessions -F $'#{session_name}\t#{@orchestra_display_name}' > "$active_file" 2>/dev/null || {
1609
+ tmux list-sessions -F $'#{session_name}\t#{@orchestra_display_name}\t#{session_last_attached}\t#{session_activity}' > "$active_file" 2>/dev/null || {
1560
1610
  rm -f "$active_file"
1561
1611
  err "Unable to list tmux sessions"
1562
1612
  return 1
@@ -97,7 +97,11 @@ case "${1:-}" in
97
97
  "create-session-exact")
98
98
  bridge_create_session_exact "${2:-}" "${3:-}"
99
99
  ;;
100
-
100
+
101
+ "create-workspace-session")
102
+ bridge_create_workspace_session "${2:-}" "${3:-}"
103
+ ;;
104
+
101
105
  "kill-session")
102
106
  bridge_kill_session "${2:-}"
103
107
  ;;
@@ -243,6 +247,8 @@ Commands:
243
247
  copy-env-files <source> <target> Copy .env files between directories
244
248
  cycle-workspace-session <session> <next|previous> [client]
245
249
  Switch within registered Orchestra sessions for this repo
250
+ create-workspace-session <session> [client]
251
+ Create a new session in the current worktree and switch to it
246
252
  tmux-help-popup [client] Show Orchestra tmux shortcut popup
247
253
  repo-info Get repository information
248
254
  git-status Get git status --porcelain
@@ -62,6 +62,26 @@ bridge_create_session_exact() {
62
62
  fi
63
63
  }
64
64
 
65
+ # Create a new tmux session in the current session's worktree and switch to it.
66
+ bridge_create_workspace_session() {
67
+ if [[ -z "${1:-}" ]]; then
68
+ json_error "Current session name required"
69
+ return 1
70
+ fi
71
+ session_name="$1"
72
+ target_client="${2:-}"
73
+
74
+ if tmux_available; then
75
+ created_session="$(tmux_create_workspace_session "$session_name" "$target_client")" || {
76
+ json_error "Failed to create workspace session"
77
+ return 1
78
+ }
79
+ json_object "ok:b=true" "session:s=$created_session"
80
+ else
81
+ json_error "tmux not available"
82
+ fi
83
+ }
84
+
65
85
  # Kill tmux session
66
86
  bridge_kill_session() {
67
87
  if [[ -z "${1:-}" ]]; then