@humanu/orchestra 0.5.72 → 0.5.74

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.72",
3
+ "version": "0.5.74",
4
4
  "description": "AI-powered Git worktree and tmux session manager with modern TUI",
5
5
  "keywords": [
6
6
  "git",
@@ -416,6 +416,80 @@ _tmux_registry_sync_active_workspace_sessions() {
416
416
  done < <(tmux list-sessions -F '#{session_name}' 2>/dev/null || true)
417
417
  }
418
418
 
419
+ _tmux_workspace_cycle_target_cached() {
420
+ local current_session="$1"
421
+ local direction="$2"
422
+ local db_path="$3"
423
+ local active_file="$4"
424
+
425
+ python3 - "$db_path" "$current_session" "$direction" "$active_file" <<'PY'
426
+ import sqlite3
427
+ import sys
428
+
429
+ db_path, current_session, direction, active_path = sys.argv[1:5]
430
+ active = set()
431
+ active_orchestra = set()
432
+ with open(active_path, "r", encoding="utf-8") as handle:
433
+ for raw_line in handle:
434
+ line = raw_line.rstrip("\n")
435
+ if not line:
436
+ continue
437
+ parts = line.split("\t", 1)
438
+ name = parts[0].strip()
439
+ display_name = parts[1].strip() if len(parts) > 1 else ""
440
+ if not name:
441
+ continue
442
+ active.add(name)
443
+ if display_name:
444
+ active_orchestra.add(name)
445
+
446
+ try:
447
+ conn = sqlite3.connect(db_path)
448
+ current_row = conn.execute(
449
+ "SELECT repo_root FROM sessions WHERE tmux_name = ?1",
450
+ (current_session,),
451
+ ).fetchone()
452
+ if current_row is None:
453
+ raise SystemExit(2)
454
+
455
+ registered = {
456
+ name
457
+ for (name,) in conn.execute("SELECT tmux_name FROM sessions").fetchall()
458
+ }
459
+ if active_orchestra.difference(registered):
460
+ raise SystemExit(2)
461
+
462
+ rows = conn.execute(
463
+ """
464
+ SELECT tmux_name
465
+ FROM sessions
466
+ WHERE repo_root = ?1
467
+ ORDER BY worktree_path COLLATE NOCASE, created_at, tmux_name COLLATE NOCASE
468
+ """,
469
+ (current_row[0],),
470
+ ).fetchall()
471
+ except sqlite3.Error:
472
+ raise SystemExit(2)
473
+
474
+ names = []
475
+ seen = set()
476
+ for (name,) in rows:
477
+ if name in active and name not in seen:
478
+ names.append(name)
479
+ seen.add(name)
480
+
481
+ if len(names) < 2 or current_session not in seen:
482
+ raise SystemExit(3)
483
+
484
+ index = names.index(current_session)
485
+ if direction == "next":
486
+ index = (index + 1) % len(names)
487
+ else:
488
+ index = (index - 1) % len(names)
489
+ print(names[index])
490
+ PY
491
+ }
492
+
419
493
  _tmux_status_escape_text() {
420
494
  local text="$1"
421
495
  text="${text//\#/##}"
@@ -424,9 +498,9 @@ _tmux_status_escape_text() {
424
498
 
425
499
  _tmux_truncate_tab_label() {
426
500
  local label="$1"
427
- local max_len="${2:-18}"
501
+ local max_len="${2:-14}"
428
502
  if (( ${#label} > max_len )); then
429
- printf '%s...' "${label:0:$((max_len - 3))}"
503
+ printf '%s' "${label:0:max_len}"
430
504
  else
431
505
  printf '%s' "$label"
432
506
  fi
@@ -506,7 +580,9 @@ PY
506
580
  _tmux_workspace_session_tabs() {
507
581
  local current_session="$1"
508
582
  local fallback_display_name="$2"
509
- local session_dir repo_root rows tabs name display_name label escaped_label divider
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
584
+ local tab_max_width=24
585
+ local active_label_max_width=22
510
586
 
511
587
  session_dir="$(tmux display-message -t "$current_session" -p '#{pane_current_path}' 2>/dev/null || echo "")"
512
588
  repo_root="$(_tmux_shared_root_for_path "$session_dir" 2>/dev/null || true)"
@@ -518,34 +594,42 @@ _tmux_workspace_session_tabs() {
518
594
  fi
519
595
 
520
596
  if [[ -z "$rows" ]]; then
521
- label="$(_tmux_truncate_tab_label "$fallback_display_name")"
522
- escaped_label="$(_tmux_status_escape_text "$label")"
523
- printf '#[fg=white,bg=colour22,bold] %s #[default]' "$escaped_label"
597
+ tab_label="$(_tmux_truncate_tab_label "$fallback_display_name" "$active_label_max_width")"
598
+ escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
599
+ printf '#[bg=#414868] #[fg=#ff9e64,bg=#414868,bold] #[fg=#c0caf5,bg=#414868,bold]%s #[default]' "$escaped_tab_label"
524
600
  return
525
601
  fi
526
602
 
603
+ # Tokyo Night palette: midnight footer, muted inactive tabs, orange active marker.
604
+ active_style="#[fg=#c0caf5,bg=#414868,bold]"
605
+ inactive_style="#[fg=#a9b1d6,bg=#24283b,nobold]"
606
+ muted_style="#[fg=#565f89,bg=#1a1b26,nobold]"
607
+ reset_style="#[default]"
527
608
  tabs=""
528
- divider="#[fg=white,bg=colour22] | #[default]"
609
+ divider="${muted_style}|${reset_style}"
529
610
  while IFS=$'\t' read -r name display_name; do
530
611
  [[ -n "$name" ]] || continue
531
612
  if [[ -n "$tabs" ]]; then
532
613
  tabs+="$divider"
533
614
  fi
534
615
  if [[ "$name" == "__ellipsis__" ]]; then
535
- tabs+="#[fg=white,bg=colour22] ... #[default]"
616
+ tab_label="⋯"
617
+ escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
618
+ tabs+="${muted_style} ${escaped_tab_label} ${reset_style}"
536
619
  continue
537
620
  fi
538
621
 
539
622
  if [[ -z "$display_name" ]]; then
540
623
  display_name="$(tmux_format_session_display "$name" without-timestamp)"
541
624
  fi
542
- label="$(_tmux_truncate_tab_label "$display_name")"
543
- escaped_label="$(_tmux_status_escape_text "$label")"
544
-
545
625
  if [[ "$name" == "$current_session" ]]; then
546
- tabs+="#[fg=white,bg=colour22,bold] [${escaped_label}] #[default]"
626
+ tab_label="$(_tmux_truncate_tab_label "$display_name" "$active_label_max_width")"
627
+ escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
628
+ tabs+="#[bg=#414868] #[fg=#ff9e64,bg=#414868,bold]● ${active_style}${escaped_tab_label} ${reset_style}"
547
629
  else
548
- tabs+="#[fg=white,bg=colour22] ${escaped_label} #[default]"
630
+ tab_label="$(_tmux_truncate_tab_label "$display_name" "$tab_max_width")"
631
+ escaped_tab_label="$(_tmux_status_escape_text "$tab_label")"
632
+ tabs+="${inactive_style} ${escaped_tab_label} ${reset_style}"
549
633
  fi
550
634
  done <<< "$rows"
551
635
 
@@ -559,7 +643,7 @@ _tmux_orchestra_status_left() {
559
643
  }
560
644
 
561
645
  _tmux_orchestra_status_right() {
562
- printf ''
646
+ printf '#[fg=#565f89,bg=#1a1b26]Ctrl+b,h for help#[default]'
563
647
  }
564
648
 
565
649
  _tmux_configure_orchestra_bindings() {
@@ -577,6 +661,8 @@ _tmux_configure_orchestra_bindings() {
577
661
 
578
662
  tmux bind-key -T prefix '?' if-shell -F '#{@orchestra_display_name}' \
579
663
  "run-shell -b \"$help_command\"" 'list-keys -N' >/dev/null 2>&1 || true
664
+ tmux bind-key -T prefix h if-shell -F '#{@orchestra_display_name}' \
665
+ "run-shell -b \"$help_command\"" 'refresh-client -S' >/dev/null 2>&1 || true
580
666
  tmux bind-key -T prefix r if-shell -F '#{@orchestra_display_name}' \
581
667
  "$prompt_command" 'refresh-client -S' >/dev/null 2>&1 || true
582
668
  tmux bind-key -T prefix '>' if-shell -F '#{@orchestra_display_name}' \
@@ -604,10 +690,11 @@ _tmux_configure_orchestra_status() {
604
690
  tmux set-option -t "$session_name" @orchestra_worktree_name "$worktree_name" >/dev/null 2>&1 || true
605
691
  tmux set-option -t "$session_name" status on >/dev/null 2>&1 || true
606
692
  tmux set-option -t "$session_name" status-position bottom >/dev/null 2>&1 || true
693
+ tmux set-option -t "$session_name" status-style "fg=#c0caf5,bg=#1a1b26" >/dev/null 2>&1 || true
607
694
  tmux set-option -t "$session_name" status-left "$status_left" >/dev/null 2>&1 || true
608
695
  tmux set-option -t "$session_name" status-left-length 1000 >/dev/null 2>&1 || true
609
696
  tmux set-option -t "$session_name" status-right "$status_right" >/dev/null 2>&1 || true
610
- tmux set-option -t "$session_name" status-right-length 0 >/dev/null 2>&1 || true
697
+ tmux set-option -t "$session_name" status-right-length 40 >/dev/null 2>&1 || true
611
698
  tmux set-option -t "$session_name" window-status-format "" >/dev/null 2>&1 || true
612
699
  tmux set-option -t "$session_name" window-status-current-format "" >/dev/null 2>&1 || true
613
700
  tmux set-option -t "$session_name" window-status-separator "" >/dev/null 2>&1 || true
@@ -1416,21 +1503,32 @@ tmux_show_orchestra_help_popup() {
1416
1503
  fi
1417
1504
 
1418
1505
  local popup_command
1419
- popup_command='printf "%s\n" \
1506
+ popup_command="$(cat <<'EOF'
1507
+ bash -lc '
1508
+ trap "exit 0" INT TERM
1509
+ printf "%s\n" \
1420
1510
  "Orchestra tmux shortcuts" \
1421
1511
  "" \
1512
+ "Ctrl+b, d Detach and return to Orchestra" \
1513
+ "Ctrl+b, r Rename the current Orchestra session" \
1514
+ "" \
1422
1515
  "Ctrl+b, Left Previous Orchestra session in this workspace" \
1423
1516
  "Ctrl+b, Right Next Orchestra session in this workspace" \
1424
1517
  "Ctrl+b, < Previous Orchestra session in this workspace" \
1425
1518
  "Ctrl+b, > Next Orchestra session in this workspace" \
1426
- "Ctrl+b, r Rename the current Orchestra session" \
1427
- "Ctrl+b, d Detach and return to Orchestra" \
1519
+ "" \
1428
1520
  "Ctrl+b, [ Copy/scroll mode" \
1521
+ "Ctrl+b, h Show this help" \
1429
1522
  "Ctrl+b, ? Show this help" \
1430
1523
  "" \
1431
- "Press Enter to close..."; read -r _'
1524
+ "Press any key to close..."
1525
+ IFS= read -rsn1 _ || true
1526
+ exit 0
1527
+ '
1528
+ EOF
1529
+ )"
1432
1530
 
1433
- tmux display-popup "${target_args[@]}" -E -w 78 -h 15 -T "Orchestra shortcuts" "$popup_command" >/dev/null 2>&1
1531
+ tmux display-popup "${target_args[@]}" -E -w 78 -h 18 -T "Orchestra shortcuts" "$popup_command" >/dev/null 2>&1 || true
1434
1532
  }
1435
1533
 
1436
1534
  # Find the adjacent active Orchestra session registered for the current repo.
@@ -1438,7 +1536,7 @@ tmux_show_orchestra_help_popup() {
1438
1536
  tmux_workspace_cycle_target() {
1439
1537
  local current_session="$1"
1440
1538
  local direction="$2"
1441
- local session_dir repo_root db_path active_file target
1539
+ local session_dir repo_root db_path active_file target query_status
1442
1540
 
1443
1541
  if ! tmux_available; then
1444
1542
  err "tmux not installed"
@@ -1456,40 +1554,64 @@ tmux_workspace_cycle_target() {
1456
1554
  ;;
1457
1555
  esac
1458
1556
 
1557
+ db_path="$(_tmux_session_registry_path)"
1558
+ have_cmd python3 || {
1559
+ err "python3 is required to read the Orchestra session registry"
1560
+ return 1
1561
+ }
1562
+
1563
+ active_file="$(mktemp)"
1564
+ tmux list-sessions -F $'#{session_name}\t#{@orchestra_display_name}' > "$active_file" 2>/dev/null || {
1565
+ rm -f "$active_file"
1566
+ err "Unable to list tmux sessions"
1567
+ return 1
1568
+ }
1569
+
1570
+ if [[ -f "$db_path" ]]; then
1571
+ if target="$(_tmux_workspace_cycle_target_cached "$current_session" "$direction" "$db_path" "$active_file")"; then
1572
+ query_status=0
1573
+ else
1574
+ query_status=$?
1575
+ fi
1576
+ if [[ $query_status -eq 0 && -n "$target" ]]; then
1577
+ rm -f "$active_file"
1578
+ printf '%s\n' "$target"
1579
+ return 0
1580
+ fi
1581
+ if [[ $query_status -eq 3 ]]; then
1582
+ rm -f "$active_file"
1583
+ return 1
1584
+ fi
1585
+ fi
1586
+
1459
1587
  _tmux_registry_upsert_current_session "$current_session" >/dev/null 2>&1 || true
1460
1588
 
1461
1589
  session_dir="$(tmux display-message -t "$current_session" -p '#{pane_current_path}' 2>/dev/null || echo "")"
1462
1590
  repo_root="$(_tmux_shared_root_for_path "$session_dir" 2>/dev/null || true)"
1463
1591
  if [[ -z "$repo_root" ]]; then
1592
+ rm -f "$active_file"
1464
1593
  err "Unable to determine Orchestra workspace for session"
1465
1594
  return 1
1466
1595
  fi
1467
1596
  _tmux_registry_sync_active_workspace_sessions "$repo_root" >/dev/null 2>&1 || true
1468
1597
 
1469
- db_path="$(_tmux_session_registry_path)"
1470
1598
  if [[ ! -f "$db_path" ]]; then
1599
+ rm -f "$active_file"
1471
1600
  err "No Orchestra session registry found"
1472
1601
  return 1
1473
1602
  fi
1474
- have_cmd python3 || {
1475
- err "python3 is required to read the Orchestra session registry"
1476
- return 1
1477
- }
1478
-
1479
- active_file="$(mktemp)"
1480
- tmux list-sessions -F '#{session_name}' > "$active_file" 2>/dev/null || {
1481
- rm -f "$active_file"
1482
- err "Unable to list tmux sessions"
1483
- return 1
1484
- }
1485
1603
 
1486
- target="$(python3 - "$db_path" "$repo_root" "$current_session" "$direction" "$active_file" <<'PY'
1604
+ if target="$(python3 - "$db_path" "$repo_root" "$current_session" "$direction" "$active_file" <<'PY'
1487
1605
  import sqlite3
1488
1606
  import sys
1489
1607
 
1490
1608
  db_path, repo_root, current_session, direction, active_path = sys.argv[1:6]
1491
1609
  with open(active_path, "r", encoding="utf-8") as handle:
1492
- active = {line.strip() for line in handle if line.strip()}
1610
+ active = {
1611
+ line.rstrip("\n").split("\t", 1)[0].strip()
1612
+ for line in handle
1613
+ if line.rstrip("\n").split("\t", 1)[0].strip()
1614
+ }
1493
1615
 
1494
1616
  conn = sqlite3.connect(db_path)
1495
1617
  rows = conn.execute(
@@ -1519,8 +1641,11 @@ else:
1519
1641
  index = (index - 1) % len(names)
1520
1642
  print(names[index])
1521
1643
  PY
1522
- )"
1523
- local query_status=$?
1644
+ )"; then
1645
+ query_status=0
1646
+ else
1647
+ query_status=$?
1648
+ fi
1524
1649
  rm -f "$active_file"
1525
1650
  if [[ $query_status -ne 0 ]]; then
1526
1651
  err "Unable to query Orchestra session registry"
@@ -1543,13 +1668,15 @@ tmux_cycle_workspace_session() {
1543
1668
  return 1
1544
1669
  fi
1545
1670
 
1546
- _tmux_refresh_orchestra_session_status "$target_session" >/dev/null 2>&1 || true
1547
-
1548
1671
  if [[ -n "$target_client" ]]; then
1549
1672
  tmux switch-client -c "$target_client" -t "$target_session" >/dev/null 2>&1 || return 1
1550
1673
  else
1551
1674
  tmux switch-client -t "$target_session" >/dev/null 2>&1 || return 1
1552
1675
  fi
1676
+
1677
+ {
1678
+ _tmux_refresh_orchestra_session_status "$target_session" >/dev/null 2>&1 || true
1679
+ } &
1553
1680
  }
1554
1681
 
1555
1682
  # Load .env file if it exists