@humanu/orchestra 0.5.19 → 0.5.21

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/install.js CHANGED
@@ -5,7 +5,7 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
 
7
7
  const BINARY_NAME = 'orchestra';
8
- const SHELL_SCRIPTS = ['gwr.sh', 'gw.sh', 'gw-bridge.sh', 'commands.sh', 'orchestra-local.sh'];
8
+ const SHELL_SCRIPTS = ['gwr.sh', 'gw.sh', 'gw-bridge.sh', 'commands.sh', 'copy_env.sh', 'orchestra-local.sh'];
9
9
  const SUPPORTED_PLATFORMS = {
10
10
  'darwin-x64': 'macos-intel',
11
11
  'darwin-arm64': 'macos-arm64',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanu/orchestra",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
4
4
  "description": "AI-powered Git worktree and tmux session manager with modern TUI",
5
5
  "keywords": [
6
6
  "git",
@@ -25,6 +25,19 @@ git_current_path() {
25
25
  pwd -P
26
26
  }
27
27
 
28
+ # Get the primary repository root (shared across all worktrees)
29
+ git_shared_root() {
30
+ local common_dir
31
+ common_dir="$(git rev-parse --git-common-dir 2>/dev/null)" || return 1
32
+ if [[ -z "$common_dir" ]]; then
33
+ return 1
34
+ fi
35
+ if [[ "$common_dir" != /* ]]; then
36
+ common_dir="$(pwd -P)/$common_dir"
37
+ fi
38
+ dirname "$common_dir"
39
+ }
40
+
28
41
  # Convert branch name to slug (replace / with -)
29
42
  git_branch_to_slug() {
30
43
  echo "$1" | tr '/' '-'
@@ -88,14 +101,42 @@ git_ensure_ignore_worktrees() {
88
101
  git_list_worktrees() {
89
102
  local root; root="$(git_repo_root)"
90
103
  [[ -z "$root" ]] && return 1
91
-
92
- (cd "$root" && git worktree list) | awk '
93
- {
94
- p=""; for (i=1; i<=NF-2; i++) p=(p==""?$i:p" " $i);
95
- sha_full=$(NF-1); sha=substr(sha_full,1,7);
96
- b=$NF; gsub(/^\[|\]$/, "", b);
97
- print p "\t" b "\t" sha;
98
- }'
104
+
105
+ local current_path=""
106
+ local current_branch=""
107
+ local current_head=""
108
+
109
+ while IFS= read -r line || [[ -n "$line" ]]; do
110
+ case "$line" in
111
+ worktree\ *)
112
+ current_path="${line#worktree }"
113
+ ;;
114
+ HEAD\ *)
115
+ current_head="${line#HEAD }"
116
+ ;;
117
+ branch\ *)
118
+ current_branch="${line#branch }"
119
+ if [[ "$current_branch" == refs/heads/* ]]; then
120
+ current_branch="${current_branch#refs/heads/}"
121
+ fi
122
+ ;;
123
+ *)
124
+ ;;
125
+ esac
126
+
127
+ if [[ -z "$line" ]]; then
128
+ if [[ -n "$current_path" ]]; then
129
+ printf '%s\t%s\t%s\n' "$current_path" "$current_branch" "${current_head:0:7}"
130
+ fi
131
+ current_path=""
132
+ current_branch=""
133
+ current_head=""
134
+ fi
135
+ done < <(cd "$root" && git worktree list --porcelain)
136
+
137
+ if [[ -n "$current_path" ]]; then
138
+ printf '%s\t%s\t%s\n' "$current_path" "$current_branch" "${current_head:0:7}"
139
+ fi
99
140
  }
100
141
 
101
142
  # Get branch name for a given worktree path
@@ -103,13 +144,36 @@ git_worktree_path_to_branch() {
103
144
  local path="$1"
104
145
  local root; root="$(git_repo_root)"
105
146
  [[ -z "$root" ]] && return 1
106
-
107
- (cd "$root" && git worktree list) | awk -v target="$path" '
108
- {
109
- p=""; for (i=1; i<=NF-2; i++) p=(p==""?$i:p" " $i);
110
- b=$NF; gsub(/^\[|\]$/, "", b);
111
- if (p==target) {print b; exit}
112
- }'
147
+
148
+ local current_path=""
149
+ local current_branch=""
150
+
151
+ while IFS= read -r line || [[ -n "$line" ]]; do
152
+ case "$line" in
153
+ worktree\ *)
154
+ current_path="${line#worktree }"
155
+ ;;
156
+ branch\ *)
157
+ current_branch="${line#branch }"
158
+ if [[ "$current_branch" == refs/heads/* ]]; then
159
+ current_branch="${current_branch#refs/heads/}"
160
+ fi
161
+ ;;
162
+ *)
163
+ ;;
164
+ esac
165
+
166
+ if [[ -z "$line" ]]; then
167
+ if [[ "$current_path" == "$path" ]]; then
168
+ printf '%s\n' "$current_branch"
169
+ return 0
170
+ fi
171
+ current_path=""
172
+ current_branch=""
173
+ fi
174
+ done < <(cd "$root" && git worktree list --porcelain)
175
+
176
+ return 1
113
177
  }
114
178
 
115
179
  # Get worktree path for a given branch name
@@ -117,12 +181,36 @@ git_branch_to_worktree_path() {
117
181
  local branch_name="$1"
118
182
  local root; root="$(git_repo_root)"
119
183
  [[ -z "$root" ]] && return 1
120
-
121
- (cd "$root" && git worktree list) | awk -v target="[$branch_name]" '
122
- {
123
- p=""; for (i=1; i<=NF-2; i++) p=(p==""?$i:p" " $i);
124
- if ($NF==target) {print p; exit}
125
- }'
184
+
185
+ local current_path=""
186
+ local current_branch=""
187
+
188
+ while IFS= read -r line || [[ -n "$line" ]]; do
189
+ case "$line" in
190
+ worktree\ *)
191
+ current_path="${line#worktree }"
192
+ ;;
193
+ branch\ *)
194
+ current_branch="${line#branch }"
195
+ if [[ "$current_branch" == refs/heads/* ]]; then
196
+ current_branch="${current_branch#refs/heads/}"
197
+ fi
198
+ ;;
199
+ *)
200
+ ;;
201
+ esac
202
+
203
+ if [[ -z "$line" ]]; then
204
+ if [[ "$current_branch" == "$branch_name" ]]; then
205
+ printf '%s\n' "$current_path"
206
+ return 0
207
+ fi
208
+ current_path=""
209
+ current_branch=""
210
+ fi
211
+ done < <(cd "$root" && git worktree list --porcelain)
212
+
213
+ return 1
126
214
  }
127
215
 
128
216
  # Create a new worktree with a new branch
@@ -13,39 +13,11 @@ if ! declare -f git_repo_root >/dev/null 2>&1; then
13
13
  return 1 2>/dev/null || exit 1
14
14
  fi
15
15
 
16
- # --------------------------- Command Functions ------------------------------
16
+ # Source shared copy helpers
17
+ COMMANDS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ source "$COMMANDS_DIR/copy_env.sh"
17
19
 
18
- # Copy .env files from source to target worktree
19
- copy_env_files() {
20
- local source_root="$1"
21
- local target_root="$2"
22
- echo "🔧 Copying .env files to new worktree..."
23
- if [ -f "$source_root/.env" ]; then
24
- cp "$source_root/.env" "$target_root/.env" && echo " ✅ Copied .env from root"
25
- fi
26
- if [ -d "$source_root/nodejs" ]; then
27
- mkdir -p "$target_root/nodejs"
28
- for env_file in "$source_root/nodejs"/.env*; do
29
- [ -f "$env_file" ] || continue
30
- cp "$env_file" "$target_root/nodejs/$(basename "$env_file")" && echo " ✅ Copied $(basename "$env_file") from nodejs/"
31
- done
32
- fi
33
- if [ -d "$source_root/nextjs" ]; then
34
- mkdir -p "$target_root/nextjs"
35
- for env_file in "$source_root/nextjs"/.env*; do
36
- [ -f "$env_file" ] || continue
37
- cp "$env_file" "$target_root/nextjs/$(basename "$env_file")" && echo " ✅ Copied $(basename "$env_file") from nextjs/"
38
- done
39
- fi
40
- if [ -d "$source_root/ingest_py" ]; then
41
- mkdir -p "$target_root/ingest_py"
42
- for env_file in "$source_root/ingest_py"/.env*; do
43
- [ -f "$env_file" ] || continue
44
- cp "$env_file" "$target_root/ingest_py/$(basename "$env_file")" && echo " ✅ Copied $(basename "$env_file") from ingest_py/"
45
- done
46
- fi
47
- echo "🔧 Environment files copied successfully!"; echo ""
48
- }
20
+ # --------------------------- Command Functions ------------------------------
49
21
 
50
22
  # Create branch + worktree
51
23
  cmd_create_worktree() {
@@ -56,12 +28,15 @@ cmd_create_worktree() {
56
28
  fi
57
29
  local branch_name="$1"
58
30
  local root; root="$(git_require_repo_root)" || return 1
59
-
31
+ local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
32
+ [[ -z "$shared_root" ]] && shared_root="$root"
33
+ local source_root="$(git_current_path)"
34
+
60
35
  local dir; dir="$(git_build_worktree_path "$branch_name")"
61
-
36
+
62
37
  info "🌳 Creating branch '$branch_name' and worktree at '$dir'..."
63
38
  dir="$(git_create_branch_and_worktree "$branch_name")"
64
- copy_env_files "$root" "$dir"
39
+ copy_env_files "$source_root" "$dir" "$shared_root"
65
40
  info "✅ Success! Worktree created at '$dir'."
66
41
  echo "cd \"$dir\""
67
42
  (cd "$dir" && git_status_short)
@@ -121,6 +96,9 @@ cmd_checkout_worktree() {
121
96
  local create_new=false
122
97
  local branch_name=""
123
98
  local root; root="$(git_require_repo_root)" || return 1
99
+ local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
100
+ [[ -z "$shared_root" ]] && shared_root="$root"
101
+ local source_root="$(git_current_path)"
124
102
 
125
103
  if [[ "${1-}" == "-b" ]]; then
126
104
  [[ -z "${2-}" ]] && { err "Branch name required for -b"; echo "Usage: gw checkout -b <branch>"; return 1; }
@@ -144,7 +122,7 @@ cmd_checkout_worktree() {
144
122
  # Create worktree for existing branch
145
123
  info "🌳 Creating worktree for existing branch '$branch_name'..."
146
124
  wt="$(git_ensure_worktree_for_branch "$branch_name")"
147
- copy_env_files "$root" "$wt"
125
+ copy_env_files "$source_root" "$wt" "$shared_root"
148
126
  echo "cd \"$wt\""
149
127
  fi
150
128
  return 0
@@ -166,12 +144,12 @@ cmd_checkout_worktree() {
166
144
  echo "cd \"$wt\""
167
145
  return 0
168
146
  fi
169
-
147
+
170
148
  # Try to create worktree for existing branch
171
149
  if git_branch_exists "$branch_name"; then
172
150
  info "🌳 Creating worktree for existing branch '$branch_name'..."
173
151
  wt="$(git_ensure_worktree_for_branch "$branch_name")"
174
- copy_env_files "$root" "$wt"
152
+ copy_env_files "$source_root" "$wt" "$shared_root"
175
153
  echo "cd \"$wt\""
176
154
  return 0
177
155
  else
@@ -220,8 +198,11 @@ cmd_copy_env() {
220
198
  return 1
221
199
  fi
222
200
 
223
- local source_root; source_root="$(git_current_path)"
201
+ local root; root="$(git_repo_root)"
202
+ local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
203
+ [[ -z "$shared_root" ]] && shared_root="$root"
204
+ local source_root="$root"
224
205
  info "🔧 Copying env files from '$source_root' -> '$target_path'"
225
- copy_env_files "$source_root" "$target_path"
206
+ copy_env_files "$source_root" "$target_path" "$shared_root"
226
207
  info "✅ Env files synced to '$target_branch'"
227
- }
208
+ }
@@ -1,10 +1,13 @@
1
1
  #!/bin/bash
2
2
 
3
3
  ###############################################################################
4
- # gw-bridge.sh – API bridge between Rust TUI and bash APIs
4
+ # gw-bridge.sh – API bridge between the Rust TUI and the shell context
5
5
  # ---------------------------------------------------------------------------
6
- # This script sources the existing git.sh and tmux.sh APIs and exposes them
7
- # via a JSON interface for the Rust TUI application to consume.
6
+ # The UI lives in Rust, but all operational logic (git, worktrees, tmux, env
7
+ # copying, etc.) lives in bash. The TUI calls into this script (through
8
+ # `BridgeApi`), and we fan out to the shell utilities (`api/git.sh`, `commands.sh`,
9
+ # `api/tmux.sh`, …). Responses are normalised (usually JSON) so the Rust side can
10
+ # remain strongly-typed while we stay anchored in the shell environment.
8
11
  ###############################################################################
9
12
 
10
13
  set -euo pipefail
@@ -13,6 +16,7 @@ set -euo pipefail
13
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
17
  source "$SCRIPT_DIR/api/git.sh"
15
18
  source "$SCRIPT_DIR/api/tmux.sh"
19
+ source "$SCRIPT_DIR/commands.sh"
16
20
 
17
21
  # Define utilities we need
18
22
  err() { printf '❌ %s\n' "$*" >&2; }
@@ -492,6 +496,22 @@ PY
492
496
 
493
497
  if git_require_repo_root >/dev/null 2>&1; then
494
498
  worktree_path="$(git_create_branch_and_worktree "$branch_name")"
499
+
500
+ source_path="$(git_repo_root)"
501
+ target_path="$worktree_path"
502
+ shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
503
+ if [[ -n "$shared_root" ]]; then
504
+ if [[ "$shared_root" != /* ]]; then
505
+ shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
506
+ fi
507
+ shared_root="$(dirname "$shared_root")"
508
+ else
509
+ shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
510
+ fi
511
+
512
+ copy_env_files "$source_path" "$target_path" "$shared_root" 1 >/dev/null
513
+ shopt -u nullglob
514
+
495
515
  echo "\"$worktree_path\""
496
516
  else
497
517
  json_error "Not a git repository"
@@ -629,17 +649,20 @@ PY
629
649
  current_path="$(git_current_path)"
630
650
  current_branch="$(git_current_branch)"
631
651
  current_commit="$(git_current_commit_short)"
652
+ shared_root="$(git_shared_root 2>/dev/null || echo "$root")"
632
653
 
633
654
  jq -n \
634
655
  --arg root "$root" \
635
656
  --arg current_path "$current_path" \
636
657
  --arg current_branch "$current_branch" \
637
658
  --arg current_commit "$current_commit" \
659
+ --arg shared_root "$shared_root" \
638
660
  '{
639
661
  root: $root,
640
662
  current_path: $current_path,
641
663
  current_branch: $current_branch,
642
- current_commit: $current_commit
664
+ current_commit: $current_commit,
665
+ shared_root: $shared_root
643
666
  }'
644
667
  else
645
668
  json_error "Not a git repository"
@@ -1087,43 +1110,27 @@ PY
1087
1110
  fi
1088
1111
  source_path="$2"
1089
1112
  target_path="$3"
1090
-
1091
- # Copy .env files using the same logic as commands.sh
1092
- copy_success=0
1093
-
1094
- # Copy root .env file
1095
- if [[ -f "$source_path/.env" ]]; then
1096
- cp "$source_path/.env" "$target_path/.env" 2>/dev/null && copy_success=1
1097
- fi
1098
-
1099
- # Copy nodejs .env files
1100
- if [[ -d "$source_path/nodejs" ]]; then
1101
- mkdir -p "$target_path/nodejs"
1102
- for env_file in "$source_path/nodejs"/.env*; do
1103
- [[ -f "$env_file" ]] || continue
1104
- cp "$env_file" "$target_path/nodejs/$(basename "$env_file")" 2>/dev/null && copy_success=1
1105
- done
1106
- fi
1107
-
1108
- # Copy nextjs .env files
1109
- if [[ -d "$source_path/nextjs" ]]; then
1110
- mkdir -p "$target_path/nextjs"
1111
- for env_file in "$source_path/nextjs"/.env*; do
1112
- [[ -f "$env_file" ]] || continue
1113
- cp "$env_file" "$target_path/nextjs/$(basename "$env_file")" 2>/dev/null && copy_success=1
1114
- done
1113
+ raw_locations="${4-}"
1114
+
1115
+ repo_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
1116
+ shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
1117
+ if [[ -n "$shared_root" ]]; then
1118
+ if [[ "$shared_root" != /* ]]; then
1119
+ shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
1120
+ fi
1121
+ shared_root="$(dirname "$shared_root")"
1122
+ else
1123
+ shared_root="$repo_root"
1115
1124
  fi
1116
-
1117
- # Copy ingest_py .env files
1118
- if [[ -d "$source_path/ingest_py" ]]; then
1119
- mkdir -p "$target_path/ingest_py"
1120
- for env_file in "$source_path/ingest_py"/.env*; do
1121
- [[ -f "$env_file" ]] || continue
1122
- cp "$env_file" "$target_path/ingest_py/$(basename "$env_file")" 2>/dev/null && copy_success=1
1123
- done
1125
+
1126
+ if [[ -n "$raw_locations" ]]; then
1127
+ override_list="$(printf '%s' "$raw_locations" | jq -r '.[]' 2>/dev/null || printf '/\n')"
1128
+ else
1129
+ override_list=""
1124
1130
  fi
1125
-
1126
- if [[ $copy_success -eq 1 ]]; then
1131
+
1132
+ COPY_ENV_OVERRIDE_LOCATIONS="$override_list" copy_env_files "$source_path" "$target_path" "$shared_root" 1 >/dev/null
1133
+ if [[ ${COPY_ENV_LAST_COPIED:-0} -gt 0 ]]; then
1127
1134
  echo "true"
1128
1135
  else
1129
1136
  echo "false"
@@ -71,6 +71,46 @@ else
71
71
  exit 1
72
72
  fi
73
73
 
74
+ get_current_worktree_title() {
75
+ local branch=""
76
+ local worktree=""
77
+
78
+ if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then
79
+ branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
80
+ if [[ -n "$branch" && "$branch" != "HEAD" ]]; then
81
+ worktree="$branch"
82
+ else
83
+ worktree="$(git rev-parse --short HEAD 2>/dev/null || echo "")"
84
+ fi
85
+ fi
86
+
87
+ if [[ -z "$worktree" ]]; then
88
+ worktree="$(pwd -P)"
89
+ worktree="$(basename "$worktree")"
90
+ fi
91
+
92
+ worktree="${worktree//\//-}"
93
+ worktree="${worktree// /-}"
94
+
95
+ if [[ -z "$worktree" ]]; then
96
+ worktree="unknown"
97
+ fi
98
+
99
+ printf 'orchestra [orchestra/%s]' "$worktree"
100
+ }
101
+
102
+ set_terminal_title() {
103
+ local tty_device
104
+ tty_device="$(tty 2>/dev/null || true)"
105
+ if [[ -z "$tty_device" || ! -w "$tty_device" ]]; then
106
+ return 0
107
+ fi
108
+
109
+ local title
110
+ title="$(get_current_worktree_title)"
111
+ printf '\033]0;%s\007' "$title" >"$tty_device" 2>/dev/null || true
112
+ }
113
+
74
114
  # Interactive functionality is provided by the Rust TUI (gw-tui)
75
115
  # No longer sourcing interactive.sh - Rust TUI is the primary interface
76
116
 
@@ -107,12 +147,15 @@ if [ $# -eq 0 ]; then
107
147
  # Interactive mode by default - prefer Rust TUI if available
108
148
  if command -v gw-tui >/dev/null 2>&1; then
109
149
  # Use Rust TUI if available
150
+ set_terminal_title
110
151
  gw-tui --bridge-path "$SCRIPT_DIR/gw-bridge.sh"
111
152
  elif [[ -f "$SCRIPT_DIR/gw-tui/target/release/gw-tui" ]]; then
112
153
  # Use locally built Rust TUI
154
+ set_terminal_title
113
155
  "$SCRIPT_DIR/gw-tui/target/release/gw-tui" --bridge-path "$SCRIPT_DIR/gw-bridge.sh"
114
156
  elif [[ -f "$SCRIPT_DIR/gw-tui/target/debug/gw-tui" ]]; then
115
157
  # Use debug build of Rust TUI
158
+ set_terminal_title
116
159
  "$SCRIPT_DIR/gw-tui/target/debug/gw-tui" --bridge-path "$SCRIPT_DIR/gw-bridge.sh"
117
160
  else
118
161
  # No Rust TUI available - provide clear guidance
@@ -75,6 +75,46 @@ check_git_repo() {
75
75
  fi
76
76
  }
77
77
 
78
+ get_current_worktree_title() {
79
+ local branch=""
80
+ local worktree=""
81
+
82
+ if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then
83
+ branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
84
+ if [[ -n "$branch" && "$branch" != "HEAD" ]]; then
85
+ worktree="$branch"
86
+ else
87
+ worktree="$(git rev-parse --short HEAD 2>/dev/null || echo "")"
88
+ fi
89
+ fi
90
+
91
+ if [[ -z "$worktree" ]]; then
92
+ worktree="$(pwd -P)"
93
+ worktree="$(basename "$worktree")"
94
+ fi
95
+
96
+ worktree="${worktree//\//-}"
97
+ worktree="${worktree// /-}"
98
+
99
+ if [[ -z "$worktree" ]]; then
100
+ worktree="unknown"
101
+ fi
102
+
103
+ printf 'orchestra [orchestra/%s]' "$worktree"
104
+ }
105
+
106
+ set_terminal_title() {
107
+ local tty_device
108
+ tty_device="$(tty 2>/dev/null || true)"
109
+ if [[ -z "$tty_device" || ! -w "$tty_device" ]]; then
110
+ return 0
111
+ fi
112
+
113
+ local title
114
+ title="$(get_current_worktree_title)"
115
+ printf '\033]0;%s\007' "$title" >"$tty_device" 2>/dev/null || true
116
+ }
117
+
78
118
  # Function to check if bridge script exists
79
119
  check_bridge_script() {
80
120
  local bridge_path="$SCRIPT_DIR/gw-bridge.sh"
@@ -167,5 +207,7 @@ fi
167
207
  # Get bridge script path
168
208
  BRIDGE_PATH="$SCRIPT_DIR/gw-bridge.sh"
169
209
 
210
+ set_terminal_title
211
+
170
212
  # Run the Rust TUI with bridge path and all arguments passed through
171
213
  exec "$BINARY_PATH" --bridge-path "$BRIDGE_PATH" "$@"