@humanu/orchestra 0.5.49 → 0.5.52

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 (32) hide show
  1. package/README.md +1 -0
  2. package/bin/orchestra.js +4 -1
  3. package/install.js +25 -2
  4. package/package.json +1 -1
  5. package/resources/prebuilt/linux-x64/gw-env-copy +0 -0
  6. package/resources/prebuilt/linux-x64/orchestra +0 -0
  7. package/resources/prebuilt/macos-arm64/gw-env-copy +0 -0
  8. package/resources/prebuilt/macos-arm64/orchestra +0 -0
  9. package/resources/prebuilt/macos-intel/gw-env-copy +0 -0
  10. package/resources/prebuilt/macos-intel/orchestra +0 -0
  11. package/resources/scripts/gw-bridge.sh +43 -3
  12. package/resources/scripts/gw.sh +4 -7
  13. package/resources/scripts/shell/AGENTS.md +13 -29
  14. package/resources/scripts/shell/bridge/ai.sh +3 -3
  15. package/resources/scripts/shell/env_copy_command.sh +29 -0
  16. package/resources/scripts/shell/git/bridge_create_worktree.sh +8 -3
  17. package/resources/scripts/shell/git/checkout_worktree.sh +3 -3
  18. package/resources/scripts/shell/git/create_worktree.sh +2 -2
  19. package/resources/scripts/shell/gw_env_copy.sh +65 -0
  20. package/resources/scripts/shell/gw_load.sh +1 -0
  21. package/resources/scripts/shell/gwr/check-updates.sh +24 -7
  22. package/resources/scripts/shell/tmux/new_session_command.sh +2 -2
  23. package/resources/scripts/copy_env.sh +0 -251
  24. package/resources/scripts/shell/bridge/copy_env.sh +0 -84
  25. package/resources/scripts/shell/env/copy_env_command.sh +0 -27
  26. package/resources/scripts/shell/env/copy_env_constants.sh +0 -3
  27. package/resources/scripts/shell/env/copy_env_core.sh +0 -178
  28. package/resources/scripts/shell/env/copy_env_debug_parse.sh +0 -14
  29. package/resources/scripts/shell/env/copy_env_load.sh +0 -9
  30. package/resources/scripts/shell/env/copy_env_locations.sh +0 -34
  31. package/resources/scripts/shell/env/copy_env_logging.sh +0 -17
  32. package/resources/scripts/shell/env/copy_env_state.sh +0 -5
package/README.md CHANGED
@@ -16,6 +16,7 @@ npm install -g @humanu/orchestra
16
16
 
17
17
  ```bash
18
18
  orchestra # Launch the Rust TUI
19
+ orchestra upgrade # Check for available updates
19
20
  ```
20
21
 
21
22
  For full documentation and release notes, visit https://github.com/humanunsupervised/orchestra
package/bin/orchestra.js CHANGED
@@ -4,7 +4,10 @@ const path = require('path');
4
4
  const { spawnSync } = require('child_process');
5
5
 
6
6
  const installScript = path.join(__dirname, '..', 'install.js');
7
- const result = spawnSync(process.execPath, [installScript, '--invoke=orchestra', ...process.argv.slice(2)], {
7
+ const args = process.argv.slice(2);
8
+ const upgradeCommands = new Set(['upgrade', 'update', '--update', '--check-updates']);
9
+ const invoke = upgradeCommands.has(args[0]) ? 'gw' : 'orchestra';
10
+ const result = spawnSync(process.execPath, [installScript, `--invoke=${invoke}`, ...args], {
8
11
  stdio: 'inherit',
9
12
  });
10
13
  process.exit(result.status ?? 0);
package/install.js CHANGED
@@ -5,7 +5,8 @@ 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', 'copy_env.sh', 'orchestra-local.sh'];
8
+ const ENV_COPY_NAME = 'gw-env-copy';
9
+ const SHELL_SCRIPTS = ['gwr.sh', 'gw.sh', 'gw-bridge.sh', 'orchestra-local.sh'];
9
10
  const SHELL_DIRS = ['shell'];
10
11
  const SUPPORTED_PLATFORMS = {
11
12
  'darwin-x64': 'macos-intel',
@@ -135,6 +136,7 @@ function linkCompatibilityBinary(binaryPath) {
135
136
  function installPrebuiltBinary() {
136
137
  const platform = getPlatformKey();
137
138
  const source = path.join(prebuiltDir, platform, BINARY_NAME);
139
+ const envCopySource = path.join(prebuiltDir, platform, ENV_COPY_NAME);
138
140
 
139
141
  if (!fs.existsSync(source)) {
140
142
  console.log(`⚠️ No prebuilt binary found for ${platform}`);
@@ -159,6 +161,13 @@ function installPrebuiltBinary() {
159
161
  fs.copyFileSync(source, destination);
160
162
  fs.chmodSync(destination, 0o755);
161
163
  linkCompatibilityBinary(destination);
164
+ if (fs.existsSync(envCopySource)) {
165
+ const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
166
+ fs.copyFileSync(envCopySource, envCopyDestination);
167
+ fs.chmodSync(envCopyDestination, 0o755);
168
+ } else {
169
+ console.warn(`⚠️ Missing ${ENV_COPY_NAME} for ${platform}`);
170
+ }
162
171
  return true;
163
172
  }
164
173
 
@@ -226,12 +235,26 @@ function buildFromSource() {
226
235
  fs.copyFileSync(builtBinary, destination);
227
236
  fs.chmodSync(destination, 0o755);
228
237
  linkCompatibilityBinary(destination);
238
+
239
+ const envCopyBinary = path.join(projectRoot, 'gw-tui', 'target', 'release', ENV_COPY_NAME);
240
+ if (!fs.existsSync(envCopyBinary)) {
241
+ console.error(`❌ ${ENV_COPY_NAME} not found after build.`);
242
+ console.error(' Expected location:', envCopyBinary);
243
+ process.exit(1);
244
+ }
245
+ const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
246
+ fs.copyFileSync(envCopyBinary, envCopyDestination);
247
+ fs.chmodSync(envCopyDestination, 0o755);
229
248
 
230
249
  console.log('✅ Successfully built from source!');
231
250
  }
232
251
 
233
252
  function assetsReady() {
234
- return fs.existsSync(path.join(distDir, BINARY_NAME)) && fs.existsSync(path.join(distDir, 'gwr.sh'));
253
+ return (
254
+ fs.existsSync(path.join(distDir, BINARY_NAME)) &&
255
+ fs.existsSync(path.join(distDir, ENV_COPY_NAME)) &&
256
+ fs.existsSync(path.join(distDir, 'gwr.sh'))
257
+ );
235
258
  }
236
259
 
237
260
  function printShellWrapperInstructions(binaryPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanu/orchestra",
3
- "version": "0.5.49",
3
+ "version": "0.5.52",
4
4
  "description": "AI-powered Git worktree and tmux session manager with modern TUI",
5
5
  "keywords": [
6
6
  "git",
@@ -16,14 +16,12 @@ set -euo pipefail
16
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
17
  source "$SCRIPT_DIR/api/git.sh"
18
18
  source "$SCRIPT_DIR/api/tmux.sh"
19
- source "$SCRIPT_DIR/shell/env/copy_env_load.sh"
20
- copy_env_init
19
+ source "$SCRIPT_DIR/shell/gw_env_copy.sh"
21
20
 
22
21
  # Source bridge modules
23
22
  source "$SCRIPT_DIR/shell/bridge/utils.sh"
24
23
  source "$SCRIPT_DIR/shell/bridge/tmux.sh"
25
24
  source "$SCRIPT_DIR/shell/bridge/ai.sh"
26
- source "$SCRIPT_DIR/shell/bridge/copy_env.sh"
27
25
 
28
26
  # Source git bridge modules (now modular)
29
27
  source "$SCRIPT_DIR/shell/git/bridge_worktree.sh"
@@ -37,6 +35,48 @@ err() { printf '❌ %s\n' "$*" >&2; }
37
35
  info() { printf '%s\n' "$*"; }
38
36
  have_cmd() { command -v "$1" >/dev/null 2>&1; }
39
37
 
38
+ bridge_copy_env_files() {
39
+ if [[ -z "${1:-}" ]] || [[ -z "${2:-}" ]]; then
40
+ json_error "Source and target paths required"
41
+ return 1
42
+ fi
43
+ local source_path="$1"
44
+ local target_path="$2"
45
+ local raw_locations="${3-}"
46
+
47
+ local repo_root
48
+ repo_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
49
+ local shared_root
50
+ shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
51
+ if [[ -n "$shared_root" ]]; then
52
+ if [[ "$shared_root" != /* ]]; then
53
+ shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
54
+ fi
55
+ shared_root="$(dirname "$shared_root")"
56
+ else
57
+ shared_root="$repo_root"
58
+ fi
59
+
60
+ local bin
61
+ if ! bin="$(gw_env_copy_bin)"; then
62
+ json_error "gw-env-copy binary not found"
63
+ return 1
64
+ fi
65
+
66
+ local args=(
67
+ --source "$source_path"
68
+ --target "$target_path"
69
+ --repo "$shared_root"
70
+ --format boolean
71
+ --quiet
72
+ )
73
+ if [[ -n "$raw_locations" ]]; then
74
+ args+=(--locations-json "$raw_locations")
75
+ fi
76
+
77
+ "$bin" "${args[@]}"
78
+ }
79
+
40
80
  # Ensure JSON backend is available (jq preferred, python/node fallback)
41
81
  bridge_init_json_backend
42
82
 
@@ -47,16 +47,13 @@ fi
47
47
 
48
48
  # Source command-line operations
49
49
  COMMANDS_DIR="$SCRIPT_DIR/shell"
50
- source "$SCRIPT_DIR/shell/env/copy_env_load.sh"
51
- copy_env_init
52
-
53
50
  for command_file in \
54
51
  "git/create_worktree.sh" \
55
52
  "git/delete_worktree.sh" \
56
53
  "git/list_worktrees.sh" \
57
54
  "git/checkout_worktree.sh" \
58
55
  "git/status.sh" \
59
- "env/copy_env_command.sh" \
56
+ "env_copy_command.sh" \
60
57
  "tmux/new_session_command.sh"
61
58
  do
62
59
  if [[ -f "$COMMANDS_DIR/$command_file" ]]; then
@@ -137,7 +134,7 @@ usage() {
137
134
  echo " ch, checkout <branch> Switch to the worktree of <branch>"
138
135
  echo " copy-env <worktreename> Copy env files from current worktree to target"
139
136
  echo " status [git status args] Show current worktree + git status"
140
- echo " update, --update Check for available updates"
137
+ echo " upgrade, update, --update Check for available updates"
141
138
  echo ""
142
139
  echo "Tmux flags:"
143
140
  echo " --branch <branch> Branch name for tmux session worktree"
@@ -149,7 +146,7 @@ usage() {
149
146
  echo " $(basename "$0") ls # list worktrees only"
150
147
  echo " $(basename "$0") ch -b feat/x # create and switch to worktree"
151
148
  echo " $(basename "$0") --branch main --new-tmux --cmd \"git status\""
152
- echo " $(basename "$0") update # check for available updates"
149
+ echo " $(basename "$0") upgrade # check for available updates"
153
150
  echo ""
154
151
  echo "Note: tmux session management is available via flags or the interactive menu."
155
152
  }
@@ -242,7 +239,7 @@ case "$COMMAND" in
242
239
  ch|checkout) cmd_checkout_worktree "$@" ;;
243
240
  status) cmd_status "$@" ;;
244
241
  copy-env) cmd_copy_env "$@" ;;
245
- update|--update) "$SCRIPT_DIR/shell/gwr/check-updates.sh" ;;
242
+ upgrade|update|--update) "$SCRIPT_DIR/shell/gwr/check-updates.sh" ;;
246
243
  help|-h|--help) usage ;;
247
244
  *) err "Unknown command '$COMMAND'"; usage ;;
248
245
  esac
@@ -20,7 +20,7 @@ shell/
20
20
  ├── gw_git.sh # Git operations for gw CLI
21
21
  ├── gw_worktree.sh # Worktree management for gw CLI
22
22
  ├── gw_usage.sh # Usage text for gw command
23
- ├── copy_env.sh # Environment variable copying system
23
+ ├── gw_env_copy.sh # Rust env copy helper/wrapper
24
24
  ├── commands.sh # Shared command utilities
25
25
  ├── build/ # Build system components
26
26
  │ ├── build_bridge.sh # Bridge building logic
@@ -30,15 +30,6 @@ shell/
30
30
  │ ├── build_logging.sh # Build-specific logging
31
31
  │ ├── build_rust.sh # Rust compilation logic
32
32
  │ └── build_usage.sh # Build command usage
33
- ├── env/ # Environment management
34
- │ ├── copy_env_command.sh # Command processing for env copying
35
- │ ├── copy_env_constants.sh # Constants and paths
36
- │ ├── copy_env_core.sh # Core environment copying logic
37
- │ ├── copy_env_debug_parse.sh # Debug parsing for env copying
38
- │ ├── copy_env_load.sh # Environment loader
39
- │ ├── copy_env_locations.sh # Path and location management
40
- │ ├── copy_env_logging.sh # Environment copying logging
41
- │ └── copy_env_state.sh # State management for env copying
42
33
  ├── git/ # Git-specific utilities
43
34
  │ └── gwr_worktree_title.sh # Worktree title generation
44
35
  └── gwr/ # gwr-specific components
@@ -122,13 +113,12 @@ shell/
122
113
 
123
114
  ## Shared System Files
124
115
 
125
- ### copy_env.sh
126
- **Purpose**: Environment variable copying system
127
- **Usage**: Copies environment between shell contexts
116
+ ### gw_env_copy.sh
117
+ **Purpose**: Rust env copy helper for CLI and bridge flows
118
+ **Usage**: Locates and invokes the `gw-env-copy` binary
128
119
  **Key Functions**:
129
- - `copy_env()` - Main function for environment copying
130
- - Critical for worktree directory changes and session management
131
- - Used by both gwr and gw systems
120
+ - `gw_env_copy_bin()` - Resolve the env copy binary path
121
+ - `gw_env_copy_files()` - Execute env copy with repo config
132
122
 
133
123
  ### commands.sh
134
124
  **Purpose**: Shared command utilities
@@ -157,17 +147,11 @@ shell/
157
147
  **Usage**: Handle specific aspects of the build process
158
148
  **Key Functions**: Each focuses on a specific build operation
159
149
 
160
- ## Environment System (env/)
150
+ ## Rust Env Copy
161
151
 
162
- ### copy_env_core.sh
163
- **Purpose**: Core environment copying logic
164
- **Usage**: Main implementation of environment variable copying
165
- **Key Functions**: `copy_env()` - Core function for environment management
166
-
167
- ### copy_env_command.sh, copy_env_constants.sh, copy_env_debug_parse.sh, copy_env_load.sh, copy_env_locations.sh, copy_env_logging.sh, copy_env_state.sh
168
- **Purpose**: Specialized environment copying components
169
- **Usage**: Handle specific aspects of environment management
170
- **Key Functions**: Each module handles a specific aspect of the complex environment copying system
152
+ Environment file copying is implemented in Rust via the `gw-env-copy` binary.
153
+ Shell wrappers invoke the binary and read `.orchestra/options.json` for
154
+ `copy_locations` when needed.
171
155
 
172
156
  ## Git Utilities (git/)
173
157
 
@@ -196,7 +180,7 @@ shell/
196
180
 
197
181
  ### gw.sh Integration
198
182
  - Sources `gw_load.sh` to load all gw components
199
- - Uses shared `copy_env.sh` for environment management
183
+ - Uses `gw_env_copy.sh` to invoke the Rust env copy binary
200
184
  - Provides CLI interface to same underlying functionality
201
185
 
202
186
  ### Bridge System Integration
@@ -209,7 +193,7 @@ shell/
209
193
  1. **Modular Loading**: Each system (gwr/gw) has its own loader that sources components in dependency order
210
194
  2. **Color Consistency**: Separate color files ensure consistent theming across components
211
195
  3. **Logging Standardization**: Standardized logging functions provide consistent output
212
- 4. **Environment Isolation**: Complex environment copying system handles shell context changes
196
+ 4. **Environment Isolation**: Rust env copy binary handles .env file replication across worktrees
213
197
  5. **Binary Discovery**: Sophisticated binary finding supports development, build, and installed scenarios
214
198
  6. **Update Integration**: Update checking integrated into main launcher with installation method detection
215
199
 
@@ -233,4 +217,4 @@ shell/
233
217
  3. Follow existing build system patterns
234
218
  4. Update `build_usage.sh` for new build options
235
219
 
236
- This documentation provides the foundation for AI agents to understand and work effectively with the Orchestrator shell system.
220
+ This documentation provides the foundation for AI agents to understand and work effectively with the Orchestrator shell system.
@@ -160,8 +160,8 @@ bridge_manual_rename_session() {
160
160
  return 0
161
161
  fi
162
162
 
163
- # Validate new display name (alphanumeric and underscore only)
164
- if [[ ! "$new_display_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
163
+ # Validate new display name (alphanumeric, underscore, hyphen only)
164
+ if [[ ! "$new_display_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
165
165
  echo "\"invalid_name\""
166
166
  return 0
167
167
  fi
@@ -172,4 +172,4 @@ bridge_manual_rename_session() {
172
172
  else
173
173
  echo "\"rename_failed\""
174
174
  fi
175
- }
175
+ }
@@ -0,0 +1,29 @@
1
+ # shellcheck shell=bash
2
+
3
+ cmd_copy_env() {
4
+ local target_branch="${1-}"
5
+ if [[ -z "$target_branch" ]]; then
6
+ err "Target worktree name required"
7
+ echo "Usage: gw copy-env <worktreename>"
8
+ return 1
9
+ fi
10
+
11
+ git_require_repo || return 1
12
+
13
+ local target_path
14
+ target_path="$(git_branch_to_worktree_path "$target_branch")"
15
+ if [[ -z "$target_path" ]]; then
16
+ err "Worktree for branch '$target_branch' not found"
17
+ return 1
18
+ fi
19
+
20
+ local root
21
+ root="$(git_repo_root)"
22
+ local shared_root
23
+ shared_root="$(git_shared_root 2>/dev/null)"
24
+ [[ -z "$shared_root" ]] && shared_root="$root"
25
+
26
+ info "Copying env files from '$root' -> '$target_path'"
27
+ gw_env_copy_files "$root" "$target_path" "$shared_root"
28
+ info "Env files synced to '$target_branch'"
29
+ }
@@ -25,11 +25,16 @@ bridge_create_worktree() {
25
25
  shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
26
26
  fi
27
27
 
28
- copy_env_files "$source_path" "$target_path" "$shared_root" 1 >/dev/null
29
- shopt -u nullglob
28
+ local env_copy_bin
29
+ if ! env_copy_bin="$(gw_env_copy_bin)"; then
30
+ json_error "gw-env-copy binary not found"
31
+ return 1
32
+ fi
33
+
34
+ "$env_copy_bin" --source "$source_path" --target "$target_path" --repo "$shared_root" --format boolean --quiet >/dev/null
30
35
 
31
36
  echo "\"$worktree_path\""
32
37
  else
33
38
  json_error "Not a git repository"
34
39
  fi
35
- }
40
+ }
@@ -6,7 +6,7 @@ cmd_checkout_worktree() {
6
6
  local root; root="$(git_require_repo_root)" || return 1
7
7
  local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
8
8
  [[ -z "$shared_root" ]] && shared_root="$root"
9
- local source_root="$(git_current_path)"
9
+ local source_root="$root"
10
10
 
11
11
  if [[ "${1-}" == "-b" ]]; then
12
12
  [[ -z "${2-}" ]] && { err "Branch name required for -b"; echo "Usage: gw checkout -b <branch>"; return 1; }
@@ -30,7 +30,7 @@ cmd_checkout_worktree() {
30
30
  # Create worktree for existing branch
31
31
  info "🌳 Creating worktree for existing branch '$branch_name'..."
32
32
  wt="$(git_ensure_worktree_for_branch "$branch_name")"
33
- copy_env_files "$source_root" "$wt" "$shared_root"
33
+ gw_env_copy_files "$source_root" "$wt" "$shared_root"
34
34
  echo "cd \"$wt\""
35
35
  fi
36
36
  return 0
@@ -57,7 +57,7 @@ cmd_checkout_worktree() {
57
57
  if git_branch_exists "$branch_name"; then
58
58
  info "🌳 Creating worktree for existing branch '$branch_name'..."
59
59
  wt="$(git_ensure_worktree_for_branch "$branch_name")"
60
- copy_env_files "$source_root" "$wt" "$shared_root"
60
+ gw_env_copy_files "$source_root" "$wt" "$shared_root"
61
61
  echo "cd \"$wt\""
62
62
  return 0
63
63
  else
@@ -10,13 +10,13 @@ cmd_create_worktree() {
10
10
  local root; root="$(git_require_repo_root)" || return 1
11
11
  local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
12
12
  [[ -z "$shared_root" ]] && shared_root="$root"
13
- local source_root="$(git_current_path)"
13
+ local source_root="$root"
14
14
 
15
15
  local dir; dir="$(git_build_worktree_path "$branch_name")"
16
16
 
17
17
  info "🌳 Creating branch '$branch_name' and worktree at '$dir'..."
18
18
  dir="$(git_create_branch_and_worktree "$branch_name")"
19
- copy_env_files "$source_root" "$dir" "$shared_root"
19
+ gw_env_copy_files "$source_root" "$dir" "$shared_root"
20
20
  info "✅ Success! Worktree created at '$dir'."
21
21
  echo "cd \"$dir\""
22
22
  (cd "$dir" && git_status_short)
@@ -0,0 +1,65 @@
1
+ # shellcheck shell=bash
2
+
3
+ gw_env_copy_bin() {
4
+ local candidate=""
5
+
6
+ if [[ -n "${GW_ENV_COPY_BIN:-}" && -x "$GW_ENV_COPY_BIN" ]]; then
7
+ printf '%s\n' "$GW_ENV_COPY_BIN"
8
+ return 0
9
+ fi
10
+
11
+ if [[ -n "${SCRIPT_DIR:-}" ]]; then
12
+ candidate="$SCRIPT_DIR/gw-env-copy"
13
+ if [[ -x "$candidate" ]]; then
14
+ printf '%s\n' "$candidate"
15
+ return 0
16
+ fi
17
+ candidate="$SCRIPT_DIR/gw-tui/target/release/gw-env-copy"
18
+ if [[ -x "$candidate" ]]; then
19
+ printf '%s\n' "$candidate"
20
+ return 0
21
+ fi
22
+ candidate="$SCRIPT_DIR/gw-tui/target/debug/gw-env-copy"
23
+ if [[ -x "$candidate" ]]; then
24
+ printf '%s\n' "$candidate"
25
+ return 0
26
+ fi
27
+ fi
28
+
29
+ if [[ -n "${GW_ORCHESTRATOR_ROOT:-}" ]]; then
30
+ candidate="$GW_ORCHESTRATOR_ROOT/gw-env-copy"
31
+ if [[ -x "$candidate" ]]; then
32
+ printf '%s\n' "$candidate"
33
+ return 0
34
+ fi
35
+ fi
36
+
37
+ if command -v gw-env-copy >/dev/null 2>&1; then
38
+ command -v gw-env-copy
39
+ return 0
40
+ fi
41
+
42
+ return 1
43
+ }
44
+
45
+ gw_env_copy_files() {
46
+ local source_root="$1"
47
+ local target_root="$2"
48
+ local repo_root="${3:-$source_root}"
49
+
50
+ local bin
51
+ if ! bin="$(gw_env_copy_bin)"; then
52
+ if declare -f err >/dev/null 2>&1; then
53
+ err "gw-env-copy binary not found"
54
+ else
55
+ printf 'Error: %s\n' "gw-env-copy binary not found" >&2
56
+ fi
57
+ return 1
58
+ fi
59
+
60
+ "$bin" \
61
+ --source "$source_root" \
62
+ --target "$target_root" \
63
+ --repo "$repo_root" \
64
+ --format text
65
+ }
@@ -5,5 +5,6 @@ gw_load_core() {
5
5
  source "$SCRIPT_DIR/shell/gw_info.sh"
6
6
  source "$SCRIPT_DIR/shell/gw_debug.sh"
7
7
  source "$SCRIPT_DIR/shell/gw_have_cmd.sh"
8
+ source "$SCRIPT_DIR/shell/gw_env_copy.sh"
8
9
  source "$SCRIPT_DIR/shell/gw_legacy_wrappers.sh"
9
10
  }
@@ -37,9 +37,17 @@ get_npm_latest_version() {
37
37
  # Function to get latest homebrew version
38
38
  get_brew_latest_version() {
39
39
  if command -v brew >/dev/null 2>&1; then
40
- brew info --json "$BREW_FORMULA" 2>/dev/null | \
41
- python3 -c "import sys, json; data = json.load(sys.stdin); print(data[0]['versions']['stable'])" 2>/dev/null || \
42
- echo "unknown"
40
+ if command -v jq >/dev/null 2>&1; then
41
+ brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
42
+ jq -r '.[0].versions.stable // "unknown"' 2>/dev/null || \
43
+ echo "unknown"
44
+ elif command -v node >/dev/null 2>&1; then
45
+ brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
46
+ node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d[0]?.versions?.stable||'unknown');" 2>/dev/null || \
47
+ echo "unknown"
48
+ else
49
+ echo "unknown"
50
+ fi
43
51
  else
44
52
  echo "brew_not_found"
45
53
  fi
@@ -106,9 +114,18 @@ check_updates() {
106
114
  latest_version=$(get_npm_latest_version)
107
115
  ;;
108
116
  "brew")
109
- current_version=$(brew info --json "$BREW_FORMULA" 2>/dev/null | \
110
- python3 -c "import sys, json; data = json.load(sys.stdin); print(data[0]['installed'][0]['version'])" 2>/dev/null || \
111
- echo "$CURRENT_VERSION")
117
+ if command -v jq >/dev/null 2>&1; then
118
+ current_version=$(brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
119
+ jq -r '.[0].installed[0].version // ""' 2>/dev/null)
120
+ elif command -v node >/dev/null 2>&1; then
121
+ current_version=$(brew info --json=v2 "$BREW_FORMULA" 2>/dev/null | \
122
+ node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d[0]?.installed?.[0]?.version||'');" 2>/dev/null)
123
+ else
124
+ current_version=""
125
+ fi
126
+ if [[ -z "$current_version" ]]; then
127
+ current_version="$CURRENT_VERSION"
128
+ fi
112
129
  latest_version=$(get_brew_latest_version)
113
130
  ;;
114
131
  "local")
@@ -171,4 +188,4 @@ check_updates() {
171
188
  }
172
189
 
173
190
  # Run the update check
174
- check_updates "$@"
191
+ check_updates "$@"
@@ -7,7 +7,7 @@ cmd_new_tmux_session_with_command() {
7
7
  local root; root="$(git_require_repo_root)" || return 1
8
8
  local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
9
9
  [[ -z "$shared_root" ]] && shared_root="$root"
10
- local source_root; source_root="$(git_current_path)"
10
+ local source_root="$root"
11
11
 
12
12
  if ! tmux_available; then
13
13
  err "tmux not installed"
@@ -30,7 +30,7 @@ cmd_new_tmux_session_with_command() {
30
30
  if [[ -z "$wt" ]]; then
31
31
  info "🌳 Creating worktree for existing branch '$branch_name'..."
32
32
  wt="$(git_ensure_worktree_for_branch "$branch_name")"
33
- copy_env_files "$source_root" "$wt" "$shared_root"
33
+ gw_env_copy_files "$source_root" "$wt" "$shared_root"
34
34
  fi
35
35
  else
36
36
  err "Branch '$branch_name' does not exist"
@@ -1,251 +0,0 @@
1
- #!/bin/bash
2
-
3
- ###############################################################################
4
- # copy_env.sh – Shared helpers for copying .env/config files into worktrees
5
- # ---------------------------------------------------------------------------
6
- # Provides reusable functions consumed by commands.sh, gw-bridge.sh, and any
7
- # other shell entry point. Adds structured logging so we can troubleshoot which
8
- # files were selected and whether individual copy operations succeeded.
9
- ###############################################################################
10
-
11
- # Global variables populated by copy_env_files
12
- COPY_ENV_LAST_COPIED=0
13
- COPY_ENV_LAST_TARGET=""
14
- COPY_ENV_LOG_FILE=""
15
-
16
- # Internal: write a log line (timestamped). Respects COPY_ENV_VERBOSE: if set to
17
- # 1, the message is also echoed to stderr for real-time debugging.
18
- copy_env_log() {
19
- local level="$1"
20
- shift
21
- local message="$*"
22
- local timestamp
23
- timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
24
- if [[ -n "$COPY_ENV_LOG_FILE" ]]; then
25
- if ! printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >>"$COPY_ENV_LOG_FILE" 2>/dev/null; then
26
- # If we cannot write to the configured log file, drop logging to disk
27
- COPY_ENV_LOG_FILE=""
28
- fi
29
- fi
30
- if [[ ${COPY_ENV_VERBOSE:-0} -eq 1 ]]; then
31
- printf '[copy-env][%s] %s\n' "$level" "$message" >&2
32
- fi
33
- }
34
-
35
- # Determine which locations to copy by reading <repo>/.orchestra/options.json.
36
- copy_env_load_locations() {
37
- local repo_root="$1"
38
- local config_file="$repo_root/.orchestra/options.json"
39
-
40
- if [[ -f "$config_file" ]]; then
41
- if command -v jq >/dev/null 2>&1; then
42
- jq -r '(.copy_locations // ["/"])[]' "$config_file" 2>/dev/null || printf '/\n'
43
- return
44
- elif command -v python3 >/dev/null 2>&1; then
45
- python3 - "$config_file" <<'PY'
46
- import json
47
- import sys
48
- from pathlib import Path
49
-
50
- config_path = Path(sys.argv[1])
51
- try:
52
- data = json.loads(config_path.read_text())
53
- except Exception:
54
- data = {}
55
- locations = data.get("copy_locations")
56
- if isinstance(locations, list) and locations:
57
- for entry in locations:
58
- if isinstance(entry, str):
59
- print(entry)
60
- else:
61
- print("/")
62
- PY
63
- return
64
- fi
65
- fi
66
- # Fallback: default to repo root
67
- printf '/\n'
68
- }
69
-
70
- # Copy .env files from the source worktree into the target worktree.
71
- # Arguments:
72
- # $1 - source root (worktree we are copying from)
73
- # $2 - target root (new or existing worktree)
74
- # $3 - repository shared root (defaults to source root)
75
- # $4 - quiet flag (1 suppresses user-facing echo output)
76
- copy_env_files() {
77
- local source_root="$1"
78
- local target_root="$2"
79
- local repo_root="${3:-$source_root}"
80
- local quiet="${4:-0}"
81
-
82
- COPY_ENV_LAST_COPIED=0
83
- COPY_ENV_LAST_TARGET="$target_root"
84
-
85
- local enable_logs=${COPY_ENV_ENABLE_LOG:-0}
86
- local log_file="${COPY_ENV_LOG_FILE:-}"
87
- local log_dir="${COPY_ENV_LOG_DIR:-}"
88
-
89
- if [[ -n "$log_file" ]]; then
90
- mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
91
- COPY_ENV_LOG_FILE="$log_file"
92
- elif [[ $enable_logs -eq 1 || -n "$log_dir" ]]; then
93
- log_dir="${log_dir:-$repo_root/.orchestra/logs}"
94
- mkdir -p "$log_dir" 2>/dev/null || true
95
- COPY_ENV_LOG_FILE="$log_dir/copy-env.log"
96
- else
97
- COPY_ENV_LOG_FILE=""
98
- fi
99
-
100
- copy_env_log "INFO" "Starting copy (source=$source_root, target=$target_root, repo=$repo_root)"
101
-
102
- local override_locations="${COPY_ENV_OVERRIDE_LOCATIONS:-}"
103
- unset COPY_ENV_OVERRIDE_LOCATIONS
104
- local locations=()
105
- if [[ -n "$override_locations" ]]; then
106
- while IFS= read -r line; do
107
- [[ -z "$line" ]] && continue
108
- locations+=("$line")
109
- done <<<"$override_locations"
110
- else
111
- while IFS= read -r line; do
112
- [[ -z "$line" ]] && continue
113
- locations+=("$line")
114
- done < <(copy_env_load_locations "$repo_root")
115
- fi
116
-
117
- if [[ ${#locations[@]} -eq 0 ]]; then
118
- locations=("/")
119
- fi
120
-
121
- local bases=("$source_root")
122
- if [[ -n "$repo_root" && "$repo_root" != "$source_root" ]]; then
123
- bases+=("$repo_root")
124
- fi
125
-
126
- local overall_copied=0
127
- local overall_failed=0
128
-
129
- (( quiet != 1 )) && echo "🔧 Copying .env files to worktree..."
130
-
131
- for raw_location in "${locations[@]}"; do
132
- local trimmed
133
- trimmed="$(printf '%s' "$raw_location" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
134
- [[ -z "$trimmed" ]] && continue
135
-
136
- local rel="${trimmed#/}"
137
- rel="${rel#./}"
138
-
139
- local location_copied=0
140
- local location_failed=0
141
-
142
- for base in "${bases[@]}"; do
143
- [[ -z "$base" ]] && continue
144
-
145
- copy_env_log "DEBUG" "Checking location '$trimmed' in base '$base'"
146
-
147
- if [[ "$trimmed" == "/" ]]; then
148
- local found_root=0
149
- while IFS= read -r -d '' env_file; do
150
- [[ -f "$env_file" ]] || continue
151
- local rel_path="${env_file#$base/}"
152
- if [[ "$rel_path" == "$env_file" ]]; then
153
- rel_path="$(basename "$env_file")"
154
- fi
155
- local dest_path="$target_root/$rel_path"
156
- mkdir -p "$(dirname "$dest_path")"
157
- local err_msg
158
- err_msg="$(cp "$env_file" "$dest_path" 2>&1)"
159
- if [[ $? -eq 0 ]]; then
160
- (( overall_copied++ ))
161
- location_copied=1
162
- found_root=1
163
- (( quiet != 1 )) && echo " ✅ Copied $rel_path from $base/"
164
- copy_env_log "INFO" "Copied $rel_path from $base/"
165
- else
166
- (( overall_failed++ ))
167
- (( location_failed++ ))
168
- (( quiet != 1 )) && echo " ❌ Failed to copy $rel_path from $base/"
169
- copy_env_log "ERROR" "Failed to copy $rel_path from $base/: ${err_msg:-unknown error}"
170
- fi
171
- done < <(find "$base" \
172
- \( -path "$target_root" -o -path "$target_root/*" -o -path "$base/.git" -o -path "$base/.git/*" -o -path "$base/worktrees" -o -path "$base/worktrees/*" \) -prune -o \
173
- -type f -name '.env*' -print0 2>/dev/null)
174
- if [[ $found_root -eq 1 ]]; then
175
- break
176
- fi
177
- else
178
- local source_path="$base/$rel"
179
- if [[ -d "$source_path" ]]; then
180
- local dest_dir="$target_root/$rel"
181
- mkdir -p "$dest_dir"
182
- local found_any=0
183
- for env_file in "$source_path"/.env*; do
184
- [[ -f "$env_file" ]] || continue
185
- local err_msg
186
- err_msg="$(cp "$env_file" "$dest_dir/$(basename "$env_file")" 2>&1)"
187
- if [[ $? -eq 0 ]]; then
188
- (( overall_copied++ ))
189
- location_copied=1
190
- found_any=1
191
- (( quiet != 1 )) && echo " ✅ Copied $(basename "$env_file") from $rel/"
192
- copy_env_log "INFO" "Copied $(basename "$env_file") from $rel/"
193
- else
194
- (( overall_failed++ ))
195
- (( location_failed++ ))
196
- (( quiet != 1 )) && echo " ❌ Failed to copy $(basename "$env_file") from $rel/"
197
- copy_env_log "ERROR" "Failed to copy $(basename "$env_file") from $rel/: ${err_msg:-unknown error}"
198
- fi
199
- done
200
- if [[ $found_any -eq 1 ]]; then
201
- break
202
- fi
203
- elif [[ -f "$source_path" ]]; then
204
- local dest_dir
205
- dest_dir="$(dirname "$rel")"
206
- if [[ "$dest_dir" != "." ]]; then
207
- mkdir -p "$target_root/$dest_dir"
208
- else
209
- mkdir -p "$target_root"
210
- fi
211
- local err_msg
212
- err_msg="$(cp "$source_path" "$target_root/$rel" 2>&1)"
213
- if [[ $? -eq 0 ]]; then
214
- (( overall_copied++ ))
215
- location_copied=1
216
- (( quiet != 1 )) && echo " ✅ Copied $rel"
217
- copy_env_log "INFO" "Copied file $rel"
218
- else
219
- (( overall_failed++ ))
220
- (( location_failed++ ))
221
- (( quiet != 1 )) && echo " ❌ Failed to copy $rel"
222
- copy_env_log "ERROR" "Failed to copy file $rel: ${err_msg:-unknown error}"
223
- fi
224
- break
225
- fi
226
- fi
227
- done
228
-
229
- if [[ $location_copied -eq 0 ]]; then
230
- if [[ "$trimmed" == "/" ]]; then
231
- (( quiet != 1 )) && echo " ⚠️ No .env files found in any root"
232
- copy_env_log "WARN" "No .env files discovered for '/'"
233
- else
234
- (( quiet != 1 )) && echo " ⚠️ No .env files found for $trimmed"
235
- copy_env_log "WARN" "No .env files found for '$trimmed'"
236
- fi
237
- fi
238
- done
239
-
240
- COPY_ENV_LAST_COPIED=$overall_copied
241
-
242
- if [[ $overall_copied -eq 0 ]]; then
243
- copy_env_log "WARN" "Completed with no files copied"
244
- (( quiet != 1 )) && echo " ⚠️ No environment files copied."
245
- else
246
- copy_env_log "INFO" "Completed: $overall_copied file(s) copied, failures=$overall_failed"
247
- (( quiet != 1 )) && echo " ✅ Environment files copied ($overall_copied)."
248
- fi
249
-
250
- return 0
251
- }
@@ -1,84 +0,0 @@
1
- #!/bin/bash
2
-
3
- # shellcheck shell=bash
4
-
5
- # Copy environment files
6
- bridge_copy_env_files() {
7
- if [[ -z "${1:-}" ]] || [[ -z "${2:-}" ]]; then
8
- json_error "Source and target paths required"
9
- return 1
10
- fi
11
- source_path="$1"
12
- target_path="$2"
13
- raw_locations="${3-}"
14
-
15
- repo_root="$(cd "$source_path" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$source_path")"
16
- shared_root="$(cd "$source_path" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || echo "")"
17
- if [[ -n "$shared_root" ]]; then
18
- if [[ "$shared_root" != /* ]]; then
19
- shared_root="$(cd "$source_path" 2>/dev/null && pwd -P)/$shared_root"
20
- fi
21
- shared_root="$(dirname "$shared_root")"
22
- else
23
- shared_root="$repo_root"
24
- fi
25
-
26
- if [[ -n "$raw_locations" ]]; then
27
- if have_cmd jq; then
28
- override_list="$(printf '%s' "$raw_locations" | jq -r '.[]' 2>/dev/null || printf '/\n')"
29
- else
30
- bridge_init_json_backend
31
- case "$BRIDGE_JSON_BACKEND" in
32
- python)
33
- override_list="$(python3 - <<'PY' "$raw_locations"
34
- import json
35
- import sys
36
- try:
37
- data = json.loads(sys.argv[1])
38
- if isinstance(data, list) and data:
39
- for item in data:
40
- if isinstance(item, str):
41
- print(item)
42
- else:
43
- print('/')
44
- except Exception:
45
- print('/')
46
- PY
47
- )"
48
- ;;
49
- node)
50
- override_list="$(node - <<'NODE' "$raw_locations"
51
- let data;
52
- try {
53
- data = JSON.parse(process.argv[2]);
54
- if (Array.isArray(data) && data.length) {
55
- for (const item of data) {
56
- if (typeof item === 'string') {
57
- process.stdout.write(item + '\n');
58
- }
59
- }
60
- } else {
61
- process.stdout.write('/\n');
62
- }
63
- } catch (err) {
64
- process.stdout.write('/\n');
65
- }
66
- NODE
67
- )"
68
- ;;
69
- *)
70
- override_list="/\n"
71
- ;;
72
- esac
73
- fi
74
- else
75
- override_list=""
76
- fi
77
-
78
- COPY_ENV_OVERRIDE_LOCATIONS="$override_list" copy_env_files "$source_path" "$target_path" "$shared_root" 1 >/dev/null
79
- if [[ ${COPY_ENV_LAST_COPIED:-0} -gt 0 ]]; then
80
- echo "true"
81
- else
82
- echo "false"
83
- fi
84
- }
@@ -1,27 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- cmd_copy_env() {
4
- local target_branch="${1-}"
5
- if [[ -z "$target_branch" ]]; then
6
- err "Target worktree name required"
7
- echo "Usage: gw copy-env <worktreename>"
8
- return 1
9
- fi
10
-
11
- git_require_repo || return 1
12
-
13
- # Find absolute path of target worktree by branch name
14
- local target_path; target_path="$(git_branch_to_worktree_path "$target_branch")"
15
- if [[ -z "$target_path" ]]; then
16
- err "Worktree for branch '$target_branch' not found"
17
- return 1
18
- fi
19
-
20
- local root; root="$(git_repo_root)"
21
- local shared_root; shared_root="$(git_shared_root 2>/dev/null)"
22
- [[ -z "$shared_root" ]] && shared_root="$root"
23
- local source_root="$root"
24
- info "🔧 Copying env files from '$source_root' -> '$target_path'"
25
- copy_env_files "$source_root" "$target_path" "$shared_root"
26
- info "✅ Env files synced to '$target_branch'"
27
- }
@@ -1,3 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- COPY_ENV_DEFAULT_LOCATION="/"
@@ -1,178 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- copy_env_files() {
4
- local source_root="$1"
5
- local target_root="$2"
6
- local repo_root="${3:-$source_root}"
7
- local quiet="${4:-0}"
8
-
9
- COPY_ENV_LAST_COPIED=0
10
- COPY_ENV_LAST_TARGET="$target_root"
11
-
12
- local enable_logs=${COPY_ENV_ENABLE_LOG:-0}
13
- local log_file="${COPY_ENV_LOG_FILE:-}"
14
- local log_dir="${COPY_ENV_LOG_DIR:-}"
15
-
16
- if [[ -n "$log_file" ]]; then
17
- mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
18
- COPY_ENV_LOG_FILE="$log_file"
19
- elif [[ $enable_logs -eq 1 || -n "$log_dir" ]]; then
20
- log_dir="${log_dir:-$repo_root/.orchestra/logs}"
21
- mkdir -p "$log_dir" 2>/dev/null || true
22
- COPY_ENV_LOG_FILE="$log_dir/copy-env.log"
23
- else
24
- COPY_ENV_LOG_FILE=""
25
- fi
26
-
27
- copy_env_log "INFO" "Starting copy (source=$source_root, target=$target_root, repo=$repo_root)"
28
-
29
- local override_locations="${COPY_ENV_OVERRIDE_LOCATIONS:-}"
30
- unset COPY_ENV_OVERRIDE_LOCATIONS
31
- local locations=()
32
- if [[ -n "$override_locations" ]]; then
33
- while IFS= read -r line; do
34
- [[ -z "$line" ]] && continue
35
- locations+=("$line")
36
- done <<<"$override_locations"
37
- else
38
- while IFS= read -r line; do
39
- [[ -z "$line" ]] && continue
40
- locations+=("$line")
41
- done < <(copy_env_load_locations "$repo_root")
42
- fi
43
-
44
- if [[ ${#locations[@]} -eq 0 ]]; then
45
- locations=("$COPY_ENV_DEFAULT_LOCATION")
46
- fi
47
-
48
- local bases=("$source_root")
49
- if [[ -n "$repo_root" && "$repo_root" != "$source_root" ]]; then
50
- bases+=("$repo_root")
51
- fi
52
-
53
- local overall_copied=0
54
- local overall_failed=0
55
-
56
- (( quiet != 1 )) && echo "🔧 Copying .env files to worktree..."
57
-
58
- for raw_location in "${locations[@]}"; do
59
- local trimmed
60
- trimmed="$(printf '%s' "$raw_location" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
61
- [[ -z "$trimmed" ]] && continue
62
-
63
- local rel="${trimmed#/}"
64
- rel="${rel#./}"
65
-
66
- local location_copied=0
67
- local location_failed=0
68
-
69
- for base in "${bases[@]}"; do
70
- [[ -z "$base" ]] && continue
71
-
72
- copy_env_log "DEBUG" "Checking location '$trimmed' in base '$base'"
73
-
74
- if [[ "$trimmed" == "/" ]]; then
75
- local found_root=0
76
- while IFS= read -r -d '' env_file; do
77
- [[ -f "$env_file" ]] || continue
78
- local rel_path="${env_file#$base/}"
79
- if [[ "$rel_path" == "$env_file" ]]; then
80
- rel_path="$(basename "$env_file")"
81
- fi
82
- local dest_path="$target_root/$rel_path"
83
- mkdir -p "$(dirname "$dest_path")"
84
- local err_msg
85
- err_msg="$(cp "$env_file" "$dest_path" 2>&1)"
86
- if [[ $? -eq 0 ]]; then
87
- (( overall_copied++ ))
88
- location_copied=1
89
- found_root=1
90
- (( quiet != 1 )) && echo " ✅ Copied $rel_path from $base/"
91
- copy_env_log "INFO" "Copied $rel_path from $base/"
92
- else
93
- (( overall_failed++ ))
94
- (( location_failed++ ))
95
- (( quiet != 1 )) && echo " ❌ Failed to copy $rel_path from $base/"
96
- copy_env_log "ERROR" "Failed to copy $rel_path from $base/: ${err_msg:-unknown error}"
97
- fi
98
- done < <(find "$base" \
99
- \( -path "$target_root" -o -path "$target_root/*" -o -path "$base/.git" -o -path "$base/.git/*" -o -path "$base/worktrees" -o -path "$base/worktrees/*" \) -prune -o \
100
- -type f -name '.env*' -print0 2>/dev/null)
101
- if [[ $found_root -eq 1 ]]; then
102
- break
103
- fi
104
- else
105
- local source_path="$base/$rel"
106
- if [[ -d "$source_path" ]]; then
107
- local dest_dir="$target_root/$rel"
108
- mkdir -p "$dest_dir"
109
- local found_any=0
110
- for env_file in "$source_path"/.env*; do
111
- [[ -f "$env_file" ]] || continue
112
- local err_msg
113
- err_msg="$(cp "$env_file" "$dest_dir/$(basename "$env_file")" 2>&1)"
114
- if [[ $? -eq 0 ]]; then
115
- (( overall_copied++ ))
116
- location_copied=1
117
- found_any=1
118
- (( quiet != 1 )) && echo " ✅ Copied $(basename "$env_file") from $rel/"
119
- copy_env_log "INFO" "Copied $(basename "$env_file") from $rel/"
120
- else
121
- (( overall_failed++ ))
122
- (( location_failed++ ))
123
- (( quiet != 1 )) && echo " ❌ Failed to copy $(basename "$env_file") from $rel/"
124
- copy_env_log "ERROR" "Failed to copy $(basename "$env_file") from $rel/: ${err_msg:-unknown error}"
125
- fi
126
- done
127
- if [[ $found_any -eq 1 ]]; then
128
- break
129
- fi
130
- elif [[ -f "$source_path" ]]; then
131
- local dest_dir
132
- dest_dir="$(dirname "$rel")"
133
- if [[ "$dest_dir" != "." ]]; then
134
- mkdir -p "$target_root/$dest_dir"
135
- else
136
- mkdir -p "$target_root"
137
- fi
138
- local err_msg
139
- err_msg="$(cp "$source_path" "$target_root/$rel" 2>&1)"
140
- if [[ $? -eq 0 ]]; then
141
- (( overall_copied++ ))
142
- location_copied=1
143
- (( quiet != 1 )) && echo " ✅ Copied $rel"
144
- copy_env_log "INFO" "Copied file $rel"
145
- else
146
- (( overall_failed++ ))
147
- (( location_failed++ ))
148
- (( quiet != 1 )) && echo " ❌ Failed to copy $rel"
149
- copy_env_log "ERROR" "Failed to copy file $rel: ${err_msg:-unknown error}"
150
- fi
151
- break
152
- fi
153
- fi
154
- done
155
-
156
- if [[ $location_copied -eq 0 ]]; then
157
- if [[ "$trimmed" == "/" ]]; then
158
- (( quiet != 1 )) && echo " ⚠️ No .env files found in any root"
159
- copy_env_log "WARN" "No .env files discovered for '/'"
160
- else
161
- (( quiet != 1 )) && echo " ⚠️ No .env files found for $trimmed"
162
- copy_env_log "WARN" "No .env files found for '$trimmed'"
163
- fi
164
- fi
165
- done
166
-
167
- COPY_ENV_LAST_COPIED=$overall_copied
168
-
169
- if [[ $overall_copied -eq 0 ]]; then
170
- copy_env_log "WARN" "Completed with no files copied"
171
- (( quiet != 1 )) && echo " ⚠️ No environment files copied."
172
- else
173
- copy_env_log "INFO" "Completed: $overall_copied file(s) copied, failures=$overall_failed"
174
- (( quiet != 1 )) && echo " ✅ Environment files copied ($overall_copied)."
175
- fi
176
-
177
- return 0
178
- }
@@ -1,14 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- copy_env_init
4
- source "$SCRIPT_DIR/api/git.sh"
5
- source "$SCRIPT_DIR/api/tmux.sh"
6
-
7
- SESSION="orchestra__performance-analysis__cdedeb95__20250827__155000__auto_test_session"
8
-
9
- export GW_DEBUG_RENAME=1
10
-
11
- echo "Testing parsing of: $SESSION"
12
- echo ""
13
-
14
- tmux_rename_session "$SESSION" "test_name" 2>&1
@@ -1,9 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- copy_env_init() {
4
- source "$SCRIPT_DIR/shell/env/copy_env_state.sh"
5
- source "$SCRIPT_DIR/shell/env/copy_env_constants.sh"
6
- source "$SCRIPT_DIR/shell/env/copy_env_logging.sh"
7
- source "$SCRIPT_DIR/shell/env/copy_env_locations.sh"
8
- source "$SCRIPT_DIR/shell/env/copy_env_core.sh"
9
- }
@@ -1,34 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- copy_env_load_locations() {
4
- local repo_root="$1"
5
- local config_file="$repo_root/.orchestra/options.json"
6
-
7
- if [[ -f "$config_file" ]]; then
8
- if command -v jq >/dev/null 2>&1; then
9
- jq -r '(.copy_locations // ["/"])[]' "$config_file" 2>/dev/null || printf '/\n'
10
- return
11
- elif command -v python3 >/dev/null 2>&1; then
12
- python3 - "$config_file" <<'PY'
13
- import json
14
- import sys
15
- from pathlib import Path
16
-
17
- config_path = Path(sys.argv[1])
18
- try:
19
- data = json.loads(config_path.read_text())
20
- except Exception:
21
- data = {}
22
- locations = data.get("copy_locations")
23
- if isinstance(locations, list) and locations:
24
- for entry in locations:
25
- if isinstance(entry, str):
26
- print(entry)
27
- else:
28
- print("/")
29
- PY
30
- return
31
- fi
32
- fi
33
- printf '/\n'
34
- }
@@ -1,17 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- copy_env_log() {
4
- local level="$1"
5
- shift
6
- local message="$*"
7
- local timestamp
8
- timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
9
- if [[ -n "$COPY_ENV_LOG_FILE" ]]; then
10
- if ! printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >>"$COPY_ENV_LOG_FILE" 2>/dev/null; then
11
- COPY_ENV_LOG_FILE=""
12
- fi
13
- fi
14
- if [[ ${COPY_ENV_VERBOSE:-0} -eq 1 ]]; then
15
- printf '[copy-env][%s] %s\n' "$level" "$message" >&2
16
- fi
17
- }
@@ -1,5 +0,0 @@
1
- # shellcheck shell=bash
2
-
3
- COPY_ENV_LAST_COPIED=0
4
- COPY_ENV_LAST_TARGET=""
5
- COPY_ENV_LOG_FILE=""