xgem-cli 2.0.0-alpha.14 → 2.0.0-alpha.16

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/README.md CHANGED
@@ -54,7 +54,8 @@ xgem add Interactive state-aware addition of remaining fram
54
54
  xgem run <framework> <script> Run a workspace automation command script
55
55
  xgem terminate Purge all generated automated layouts completely
56
56
  xgem <framework> [help] View tailored instructions for a specific script layout
57
- xgem doctor Report on your environment (OS, arch, toolchains found)
57
+ xgem doctor Toolchain report: versions, where found (fvm/nvm/brew), update checks, install links
58
+ xgem update [tool] Update installed toolchains (flutter, node, rust, ...) after confirmation
58
59
  xgem doctor ios [path] Report on iOS build readiness: CocoaPods vs SPM, deployment
59
60
  targets per config, and whether they're reconciled
60
61
  xgem git cmt "message" Auto-stage, commit, rebase-pull, and push
@@ -72,7 +73,7 @@ xgem bootstrap Clone-to-running: detect framework, install, .env,
72
73
  xgem ci Run lint/test/build for every configured framework, summarized
73
74
  xgem --version Print xgem's version
74
75
 
75
- Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose
76
+ Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose, --no-network (skip update lookups)
76
77
  ```
77
78
 
78
79
  ## Supported frameworks
@@ -97,7 +98,9 @@ bin/xgem thin dispatcher — no business logic, just resolves paths and ro
97
98
  lib/
98
99
  logger.sh log levels + verbose mode
99
100
  utils.sh os/arch detection, confirm(), require_cmd()
100
- doctor.sh environment + feature detection ("xgem doctor")
101
+ toolchain.sh finds tools behind fvm/nvm/etc., offers installs, checks latest versions
102
+ update.sh xgem update
103
+ doctor.sh toolchain report ("xgem doctor")
101
104
  ios.sh the SwiftPM-aware iOS build engine
102
105
  flutter.sh flutter command group (build/hard-clean/build-runner), delegates iOS builds to ios.sh
103
106
  git.sh git cmt/init/branch/rm-remote/rm-branch
package/bin/xgem CHANGED
@@ -21,6 +21,10 @@ export XGEM_HOME
21
21
  source "$XGEM_HOME/lib/logger.sh"
22
22
  # shellcheck source=lib/utils.sh
23
23
  source "$XGEM_HOME/lib/utils.sh"
24
+ # shellcheck source=lib/toolchain.sh
25
+ source "$XGEM_HOME/lib/toolchain.sh"
26
+ # shellcheck source=lib/update.sh
27
+ source "$XGEM_HOME/lib/update.sh"
24
28
  # shellcheck source=lib/version.sh
25
29
  source "$XGEM_HOME/lib/version.sh"
26
30
  # shellcheck source=lib/doctor.sh
@@ -56,6 +60,7 @@ for arg in "$@"; do
56
60
  --yes) XGEM_YES=1 ;;
57
61
  --dry-run) XGEM_DRY_RUN=1 ;;
58
62
  --verbose) XGEM_VERBOSE=1 ;;
63
+ --no-network) XGEM_NO_NETWORK=1 ;;
59
64
  *) ARGS+=("$arg") ;;
60
65
  esac
61
66
  done
@@ -79,7 +84,8 @@ print_usage() {
79
84
  echo " xgem run <framework> <script> - Run a workspace automation command script"
80
85
  echo " xgem terminate - Purge all generated automated layouts completely"
81
86
  echo " xgem <framework> [help] - View tailored instructions for a specific script layout"
82
- echo " xgem doctor [ios] - Report on your environment / iOS SwiftPM build readiness"
87
+ echo " xgem doctor [ios] - Toolchain report (finds fvm/nvm installs, links, update checks) / iOS build readiness"
88
+ echo " xgem update [tool] - Update installed toolchains (flutter, node, rust, ...) after confirmation"
83
89
  echo " xgem git cmt \"message\" - Auto-stage, commit, rebase-pull, and push"
84
90
  echo " xgem git init - Setup local repo, attach remote tracker shortcuts"
85
91
  echo " xgem git branch - Pick, create, or switch branches; remembers your choice"
@@ -95,7 +101,7 @@ print_usage() {
95
101
  echo " xgem ci - Run lint/test/build for every configured framework, summarized"
96
102
  echo " xgem --version - Print xgem's version"
97
103
  echo ""
98
- echo "Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose"
104
+ echo "Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose, --no-network (skip update lookups)"
99
105
  }
100
106
 
101
107
  case "${1:-}" in
@@ -218,6 +224,11 @@ case "${1:-}" in
218
224
  exit 0
219
225
  ;;
220
226
 
227
+ update)
228
+ cmd_update "${2:-}"
229
+ exit 0
230
+ ;;
231
+
221
232
  release)
222
233
  cmd_release "${2:-}"
223
234
  exit 0
package/bin/xgem.js CHANGED
@@ -26,6 +26,7 @@ const { registryAdd, registryRemove } = require('../lib-win/registry');
26
26
  const { cmdStatus } = require('../lib-win/status');
27
27
  const { cmdBootstrap } = require('../lib-win/bootstrap');
28
28
  const { cmdCi } = require('../lib-win/ci');
29
+ const { cmdUpdate } = require('../lib-win/update');
29
30
 
30
31
  const CONFIG_DIR = '.xgem-automate';
31
32
 
@@ -47,7 +48,8 @@ function printUsage() {
47
48
  console.log(' xgem run <framework> <script> - Run a workspace automation command script');
48
49
  console.log(' xgem terminate - Purge all generated automated layouts completely');
49
50
  console.log(' xgem <framework> [help] - View tailored instructions for a specific script layout');
50
- console.log(' xgem doctor [ios] - Report on your environment / iOS build availability');
51
+ console.log(' xgem doctor [ios] - Toolchain report (finds fvm/nvm installs, links, update checks)');
52
+ console.log(' xgem update [tool] - Update installed toolchains (flutter, node, rust, ...) after confirmation');
51
53
  console.log(' xgem git cmt "message" - Auto-stage, commit, rebase-pull, and push');
52
54
  console.log(' xgem git init - Setup local repo, attach remote tracker shortcuts');
53
55
  console.log(' xgem git branch - Pick, create, or switch branches; remembers your choice');
@@ -63,7 +65,7 @@ function printUsage() {
63
65
  console.log(' xgem ci - Run lint/test/build for every configured framework, summarized');
64
66
  console.log(' xgem --version - Print xgem\'s version');
65
67
  console.log('');
66
- console.log('Flags (any command): --yes (skip confirmations), --dry-run (show, don\'t apply), --verbose');
68
+ console.log('Flags (any command): --yes (skip confirmations), --dry-run (show, don\'t apply), --verbose, --no-network (skip update lookups)');
67
69
  }
68
70
 
69
71
  async function selectFramework(options) {
@@ -175,6 +177,7 @@ async function main() {
175
177
  if (arg === '--yes') process.env.XGEM_YES = '1';
176
178
  else if (arg === '--dry-run') process.env.XGEM_DRY_RUN = '1';
177
179
  else if (arg === '--verbose') process.env.XGEM_VERBOSE = '1';
180
+ else if (arg === '--no-network') process.env.XGEM_NO_NETWORK = '1';
178
181
  else args.push(arg);
179
182
  }
180
183
 
@@ -189,6 +192,7 @@ async function main() {
189
192
  if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch|pr|sync|clean-branches|hooks>');
190
193
  return cmdGit(a2, a3, CONFIG_DIR);
191
194
  case 'doctor': return cmdDoctor(a2);
195
+ case 'update': return cmdUpdate(a2);
192
196
  case 'release': return cmdRelease(a2);
193
197
  case 'status': return cmdStatus(CONFIG_DIR);
194
198
  case 'bootstrap': return cmdBootstrap(CONFIG_DIR);
package/lib/bootstrap.sh CHANGED
@@ -30,6 +30,10 @@ _bootstrap_env_file() {
30
30
  local example
31
31
  for example in .env.example .env.sample; do
32
32
  [ -f "$example" ] || continue
33
+ if [ "$XGEM_DRY_RUN" = "1" ]; then
34
+ log_info "(dry-run) would create .env from $example"
35
+ return 0
36
+ fi
33
37
  cp "$example" .env
34
38
  log_success "Created .env from $example."
35
39
  local missing
@@ -50,50 +54,76 @@ _bootstrap_node_pm() {
50
54
  fi
51
55
  }
52
56
 
57
+ _bootstrap_preflight() {
58
+ local fw=$1 tool pm
59
+ tool=$(fw_required_tool "$fw")
60
+ [ -n "$tool" ] && [ "$fw" != "docker" ] && { tool_ensure "$tool" || return 1; }
61
+ case "$fw" in
62
+ flutter) tool_ensure dart || return 1 ;;
63
+ node|react|vue|angular|next)
64
+ pm=$(_bootstrap_node_pm)
65
+ if [ "$pm" != "npm" ] && ! has_cmd "$pm"; then
66
+ log_warn "This project uses $pm (lockfile found) but '$pm' isn't installed."
67
+ echo " Enable it via Node's bundled corepack: corepack enable (or: npm install -g $pm)"
68
+ if confirm "Run 'npm install -g $pm' now?"; then
69
+ _bootstrap_run npm install -g "$pm" || return 1
70
+ else
71
+ return 1
72
+ fi
73
+ fi
74
+ ;;
75
+ esac
76
+ return 0
77
+ }
78
+
79
+ _bootstrap_run() {
80
+ if [ "$XGEM_DRY_RUN" = "1" ]; then
81
+ log_info "(dry-run) would run: $*"
82
+ return 0
83
+ fi
84
+ "$@"
85
+ }
86
+
53
87
  _bootstrap_install() {
54
88
  local fw=$1
55
89
  case "$fw" in
56
90
  node|react|vue|angular|next)
57
91
  local pm
58
92
  pm=$(_bootstrap_node_pm)
93
+ confirm "Install dependencies with $pm?" || return 0
59
94
  log_info "Installing dependencies with $pm..."
60
95
  case "$pm" in
61
- yarn) yarn ;;
62
- pnpm) pnpm install ;;
63
- bun) bun install ;;
64
- *) npm install ;;
96
+ yarn) _bootstrap_run yarn ;;
97
+ pnpm) _bootstrap_run pnpm install ;;
98
+ bun) _bootstrap_run bun install ;;
99
+ *) _bootstrap_run npm install ;;
65
100
  esac
66
101
  ;;
67
102
  flutter)
68
- has_cmd flutter || { log_warn "flutter not found — skipping 'flutter pub get'."; return 0; }
69
- log_info "Running flutter pub get..."
70
- flutter pub get
103
+ confirm "Run 'flutter pub get'?" || return 0
104
+ _bootstrap_run flutter pub get
71
105
  ;;
72
106
  python)
73
107
  if [ -f pyproject.toml ] && has_cmd poetry; then
74
- log_info "Installing dependencies with poetry..."
75
- poetry install
108
+ confirm "Install dependencies with poetry?" || return 0
109
+ _bootstrap_run poetry install
76
110
  elif [ -f requirements.txt ]; then
77
- has_cmd python3 || { log_warn "python3 not found skipping install."; return 0; }
78
- [ -d .venv ] || python3 -m venv .venv
79
- log_info "Installing dependencies into .venv..."
80
- .venv/bin/pip install -r requirements.txt
111
+ confirm "Create .venv and install requirements.txt into it?" || return 0
112
+ [ -d .venv ] || _bootstrap_run python3 -m venv .venv
113
+ _bootstrap_run .venv/bin/pip install -r requirements.txt
81
114
  fi
82
115
  ;;
83
116
  go)
84
- has_cmd go || { log_warn "go not found — skipping 'go mod download'."; return 0; }
85
- log_info "Running go mod download..."
86
- go mod download
117
+ confirm "Run 'go mod download'?" || return 0
118
+ _bootstrap_run go mod download
87
119
  ;;
88
120
  rust)
89
- has_cmd cargo || { log_warn "cargo not found — skipping 'cargo fetch'."; return 0; }
90
- log_info "Running cargo fetch..."
91
- cargo fetch
121
+ confirm "Run 'cargo fetch'?" || return 0
122
+ _bootstrap_run cargo fetch
92
123
  ;;
93
124
  swift)
94
- has_cmd swift || { log_warn "swift not found — skipping 'swift package resolve'."; return 0; }
95
- log_info "Running swift package resolve..."
96
- swift package resolve
125
+ confirm "Run 'swift package resolve'?" || return 0
126
+ _bootstrap_run swift package resolve
97
127
  ;;
98
128
  docker)
99
129
  log_debug "Docker project — nothing to install locally."
@@ -152,6 +182,7 @@ cmd_bootstrap() {
152
182
  log_success "Detected: $fw"
153
183
  fi
154
184
 
185
+ _bootstrap_preflight "$fw" || die "Bootstrap stopped: a required tool is missing."
155
186
  _bootstrap_env_file
156
187
  _bootstrap_install "$fw"
157
188
  _bootstrap_migrations "$fw"
package/lib/ci.sh CHANGED
@@ -1,7 +1,35 @@
1
1
  #!/bin/bash
2
- # xgem ci: run lint -> test -> build for every configured framework before
3
- # you push, so a CI failure gets caught locally first.
4
- # Depends on lib/logger.sh.
2
+ # xgem ci: run the checks for every configured framework before you push,
3
+ # so a CI failure gets caught locally first.
4
+ # Depends on lib/logger.sh, lib/utils.sh.
5
+
6
+ # CI=true and a closed stdin make test runners (Vitest, Jest, Angular) run
7
+ # once and exit instead of entering watch mode or waiting on a prompt.
8
+ # Records into results/overall_status of the calling cmd_ci.
9
+ _ci_run_step() {
10
+ local label=$1
11
+ shift
12
+ log_info "Running $label..."
13
+ if CI=true "$@" < /dev/null; then
14
+ results+=("PASS $label")
15
+ else
16
+ results+=("FAIL $label")
17
+ overall_status=1
18
+ fi
19
+ }
20
+
21
+ _ci_flutter() {
22
+ if ! tool_ensure flutter; then
23
+ results+=("SKIP flutter (flutter not found)")
24
+ return 0
25
+ fi
26
+ _ci_run_step "flutter analyze" flutter analyze
27
+ if [ -d test ]; then
28
+ _ci_run_step "flutter test" flutter test
29
+ else
30
+ results+=("SKIP flutter test (no test/ directory)")
31
+ fi
32
+ }
5
33
 
6
34
  cmd_ci() {
7
35
  [ -d "$CONFIG_DIR" ] || die "No $CONFIG_DIR found — run 'xgem init' or 'xgem add' first."
@@ -14,20 +42,27 @@ cmd_ci() {
14
42
  for fw_dir in "$CONFIG_DIR"/*/; do
15
43
  [ -d "$fw_dir" ] || continue
16
44
  fw=$(basename "$fw_dir")
45
+
46
+ if [ "$fw" = "flutter" ]; then
47
+ _ci_flutter
48
+ continue
49
+ fi
50
+
51
+ local needed
52
+ needed=$(fw_required_tool "$fw")
53
+ if [ -n "$needed" ] && [ "$fw" != "docker" ] && ! tool_ensure "$needed"; then
54
+ results+=("SKIP $fw ($needed not found)")
55
+ continue
56
+ fi
57
+
17
58
  for step in "${steps[@]}"; do
18
59
  [ -f "$CONFIG_DIR/$fw/$step.sh" ] || continue
19
- log_info "Running $fw $step..."
20
- if bash "$CONFIG_DIR/$fw/$step.sh"; then
21
- results+=("PASS $fw $step")
22
- else
23
- results+=("FAIL $fw $step")
24
- overall_status=1
25
- fi
60
+ _ci_run_step "$fw $step" bash "$CONFIG_DIR/$fw/$step.sh"
26
61
  done
27
62
  done
28
63
 
29
64
  if [ ${#results[@]} -eq 0 ]; then
30
- log_warn "No lint/test/build scripts found under $CONFIG_DIR to run."
65
+ log_warn "No checks found under $CONFIG_DIR to run."
31
66
  return 0
32
67
  fi
33
68
 
package/lib/doctor.sh CHANGED
@@ -37,21 +37,93 @@ flutter_config_spm_enabled() {
37
37
  fi
38
38
  }
39
39
 
40
+ _doctor_tools() {
41
+ echo git flutter dart node python3 go cargo docker gh fvm
42
+ [ "$(detect_os)" = "darwin" ] && echo swift
43
+ }
44
+
45
+ _doctor_tool_row() {
46
+ local tool=$1 version latest where mgr pin
47
+ if ! tool_resolve "$tool"; then
48
+ printf ' %-9s %-11s %s\n' "$tool" "not found" "download: $(tool_url "$tool")"
49
+ local recipe
50
+ recipe=$(tool_install_cmd "$tool")
51
+ [ -n "$recipe" ] && echo " install: $recipe (or run: xgem update $tool)"
52
+ return 0
53
+ fi
54
+
55
+ version=$(tool_version_of "$tool")
56
+ mgr=$TOOL_MANAGER
57
+ where=$(_tool_pretty "$TOOL_PATH")
58
+ printf ' %-9s %-11s %s%s\n' "$tool" "${version:-unknown}" "$where" "$([ "$mgr" = system ] || [ "$mgr" = other ] || echo " ($mgr)")"
59
+
60
+ if [ "$TOOL_ON_PATH" = "0" ] && [ "$tool" != "dart" ]; then
61
+ echo " ! not on PATH: found via $mgr, so plain scripts and CI won't see it (an alias such as alias flutter='fvm flutter' isn't visible to scripts; xgem resolves it for its own commands)"
62
+ fi
63
+
64
+ latest=$(XGEM_NO_NETWORK=1 tool_latest "$tool")
65
+ if [ -n "$version" ] && [ -n "$latest" ] && _tool_ver_lt "$version" "$latest"; then
66
+ echo " ^ update available: $version -> $latest (run: xgem update $tool)"
67
+ fi
68
+
69
+ pin=""
70
+ [ "$tool" != "dart" ] && pin=$(tool_pin "$tool")
71
+ if [ -n "$pin" ] && [ -n "$version" ] && [[ "$pin" =~ ^[0-9] ]]; then
72
+ case "$version" in
73
+ "$pin"*) ;;
74
+ *) echo " ! this project pins $tool $pin but the active one is $version" ;;
75
+ esac
76
+ fi
77
+ }
78
+
79
+ _doctor_version_managers() {
80
+ local -a found=()
81
+ [ -d "${NVM_DIR:-$HOME/.nvm}" ] && found+=(nvm)
82
+ command -v fnm >/dev/null 2>&1 && found+=(fnm)
83
+ { [ -d "$HOME/.volta" ] || command -v volta >/dev/null 2>&1; } && found+=(volta)
84
+ { [ -d "$HOME/.asdf" ] || command -v asdf >/dev/null 2>&1; } && found+=(asdf)
85
+ command -v mise >/dev/null 2>&1 && found+=(mise)
86
+ { [ -d "$HOME/.pyenv" ] || command -v pyenv >/dev/null 2>&1; } && found+=(pyenv)
87
+ { [ -x "$HOME/.cargo/bin/rustup" ] || command -v rustup >/dev/null 2>&1; } && found+=(rustup)
88
+ { [ -d "$HOME/.puro" ] || command -v puro >/dev/null 2>&1; } && found+=(puro)
89
+ if [ ${#found[@]} -gt 0 ]; then
90
+ echo "Version managers: ${found[*]} (fvm is listed with the tools above)"
91
+ else
92
+ echo "Version managers: none detected"
93
+ fi
94
+ }
95
+
40
96
  doctor_print_general() {
41
97
  echo -e "\033[1;36m=== xgem doctor ===\033[0m"
42
- echo "OS: $(detect_os)"
43
- echo "Arch: $(detect_arch)$( is_apple_silicon && echo ' (Apple Silicon)' )"
44
- echo "git: $(doctor_tool_version git)"
45
- echo "flutter: $(doctor_tool_version flutter)"
46
- echo "dart: $(doctor_tool_version dart)"
47
- echo "node: $(doctor_tool_version node)"
48
- echo "python3: $(doctor_tool_version python3)"
49
- echo "go: $(doctor_tool_version go version)"
50
- echo "cargo: $(doctor_tool_version cargo)"
51
- echo "docker: $(doctor_tool_version docker)"
98
+ echo "OS: $(detect_os) Arch: $(detect_arch)$( is_apple_silicon && echo ' (Apple Silicon)' )"
99
+ echo ""
100
+
101
+ local t
102
+ if [ "$XGEM_NO_NETWORK" != "1" ]; then
103
+ log_info "Checking installed tools and the latest available versions..."
104
+ for t in $(_doctor_tools); do
105
+ ( tool_resolve "$t" && tool_latest "$t" ) >/dev/null 2>&1 &
106
+ done
107
+ wait
108
+ fi
109
+
110
+ echo -e "\033[1;36mToolchains\033[0m"
111
+ for t in $(_doctor_tools); do
112
+ _doctor_tool_row "$t"
113
+ done
114
+ echo ""
115
+ _doctor_version_managers
116
+
52
117
  if [ "$(detect_os)" = "darwin" ]; then
118
+ echo ""
53
119
  echo "xcodebuild: $(doctor_tool_version xcodebuild -version)"
54
120
  echo "pod (CocoaPods): $(doctor_tool_version pod)"
121
+ has_cmd pod || echo " install: brew install cocoapods (https://cocoapods.org)"
122
+ fi
123
+
124
+ if [ "$XGEM_NO_NETWORK" = "1" ] && [ ! -d "$XGEM_CACHE_DIR" ]; then
125
+ echo ""
126
+ log_info "Update checks were skipped (--no-network) and nothing is cached yet."
55
127
  fi
56
128
  }
57
129
 
@@ -115,6 +187,6 @@ cmd_doctor() {
115
187
  case "${1:-}" in
116
188
  ios) doctor_print_ios "${2:-.}" ;;
117
189
  "") doctor_print_general ;;
118
- *) die "Unknown doctor target '$1'. Usage: xgem doctor [ios]" ;;
190
+ *) die "Unknown doctor target '$1'. Usage: xgem doctor [ios] [--no-network]" ;;
119
191
  esac
120
192
  }
package/lib/flutter.sh CHANGED
@@ -6,6 +6,7 @@
6
6
  # Depends on lib/logger.sh, lib/utils.sh, lib/ios.sh.
7
7
 
8
8
  cmd_flutter_hard_clean() {
9
+ require_cmd flutter
9
10
  log_info "Cleaning Flutter project..."
10
11
  rm -f pubspec.lock
11
12
  flutter clean
@@ -41,6 +42,7 @@ cmd_flutter_hard_clean() {
41
42
  }
42
43
 
43
44
  cmd_flutter_build_runner() {
45
+ require_cmd flutter
44
46
  log_info "Running Flutter Build Runner..."
45
47
  local verbose_choice
46
48
  read -r -p "Run in verbose mode? (Y/n): " verbose_choice
@@ -158,6 +160,7 @@ _flutter_build_ios() {
158
160
  }
159
161
 
160
162
  cmd_flutter_build() {
163
+ require_cmd flutter
161
164
  echo -e "\033[1;34m--- Flutter Build Orchestrator ---\033[0m"
162
165
 
163
166
  local current_name="" current_num="" current_full
package/lib/git.sh CHANGED
@@ -33,6 +33,35 @@ _git_base_branch() {
33
33
  return 1
34
34
  }
35
35
 
36
+ _git_resolve_conflicts_help() {
37
+ local files
38
+ files=$(git diff --name-only --diff-filter=U 2>/dev/null)
39
+ if [ -n "$files" ]; then
40
+ log_warn "Files with conflicts:"
41
+ echo "$files" | sed 's/^/ - /'
42
+ fi
43
+ log_warn "Resolve them, then run: git add <files> && git rebase --continue (or give up with: git rebase --abort)"
44
+
45
+ local open_editor
46
+ read -r -p "Do you want to open VS Code to resolve this now? (y/n): " open_editor
47
+ [[ "$open_editor" == "y" || "$open_editor" == "Y" ]] || return 0
48
+
49
+ if has_cmd code; then
50
+ code .
51
+ return 0
52
+ fi
53
+ if [ "$(detect_os)" = "darwin" ]; then
54
+ local app
55
+ for app in "Visual Studio Code" "Visual Studio Code - Insiders"; do
56
+ if [ -d "/Applications/$app.app" ]; then
57
+ open -a "$app" .
58
+ return 0
59
+ fi
60
+ done
61
+ fi
62
+ log_warn "VS Code's 'code' command isn't on your PATH (a shell alias doesn't count — scripts can't see it). In VS Code run Cmd+Shift+P -> 'Shell Command: Install code command in PATH', or open the files above manually."
63
+ }
64
+
36
65
  cmd_git_cmt() {
37
66
  local commit_msg=$1
38
67
  [ -n "$commit_msg" ] || die "Missing commit message!"
@@ -115,9 +144,7 @@ cmd_git_cmt() {
115
144
  log_warn "Your commit is safe locally. Re-run 'xgem git cmt' once connectivity is restored, or push manually: git push $remote_name $current_branch"
116
145
  exit 1
117
146
  fi
118
- local open_editor
119
- read -r -p "Do you want to open VS Code to resolve this now? (y/n): " open_editor
120
- [[ "$open_editor" == "y" || "$open_editor" == "Y" ]] && code .
147
+ _git_resolve_conflicts_help
121
148
  exit 1
122
149
  fi
123
150
 
@@ -386,10 +413,7 @@ cmd_git_sync() {
386
413
  git_dir=$(git rev-parse --git-dir 2>/dev/null)
387
414
  if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ]; then
388
415
  log_error "MERGE CONFLICT DETECTED!"
389
- log_warn "Execution paused. Resolve conflicts, then 'git rebase --continue'."
390
- local open_editor
391
- read -r -p "Do you want to open VS Code to resolve this now? (y/n): " open_editor
392
- [[ "$open_editor" == "y" || "$open_editor" == "Y" ]] && code .
416
+ _git_resolve_conflicts_help
393
417
  else
394
418
  log_error "Rebase failed — this looks like a connection problem, not a merge conflict (see the git error above)."
395
419
  fi