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

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
@@ -62,6 +62,14 @@ xgem git init Setup local repo, attach remote tracker shortcuts
62
62
  xgem git branch Pick, create, or switch branches; remembers your choice
63
63
  xgem git rm-remote Drop a configured remote
64
64
  xgem git rm-branch Safely drop local and/or remote branch
65
+ xgem git pr Push current branch and open a GitHub PR (gh-backed)
66
+ xgem git sync Rebase current branch onto the repo's base branch
67
+ xgem git clean-branches Delete local branches already merged into the base branch
68
+ xgem git hooks <install|uninstall> Manage a pre-commit lint/test hook
69
+ xgem release [major|minor|patch] Bump version, update CHANGELOG.md, tag, push
70
+ xgem status Dashboard across every xgem-tracked project
71
+ xgem bootstrap Clone-to-running: detect framework, install, .env, migrate, launch
72
+ xgem ci Run lint/test/build for every configured framework, summarized
65
73
  xgem --version Print xgem's version
66
74
 
67
75
  Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose
package/bin/xgem CHANGED
@@ -35,6 +35,16 @@ source "$XGEM_HOME/lib/flutter.sh"
35
35
  source "$XGEM_HOME/lib/scaffold.sh"
36
36
  # shellcheck source=lib/create.sh
37
37
  source "$XGEM_HOME/lib/create.sh"
38
+ # shellcheck source=lib/release.sh
39
+ source "$XGEM_HOME/lib/release.sh"
40
+ # shellcheck source=lib/registry.sh
41
+ source "$XGEM_HOME/lib/registry.sh"
42
+ # shellcheck source=lib/status.sh
43
+ source "$XGEM_HOME/lib/status.sh"
44
+ # shellcheck source=lib/bootstrap.sh
45
+ source "$XGEM_HOME/lib/bootstrap.sh"
46
+ # shellcheck source=lib/ci.sh
47
+ source "$XGEM_HOME/lib/ci.sh"
38
48
 
39
49
  CONFIG_DIR=".xgem-automate"
40
50
 
@@ -75,6 +85,14 @@ print_usage() {
75
85
  echo " xgem git branch - Pick, create, or switch branches; remembers your choice"
76
86
  echo " xgem git rm-remote - Drop specified target remote tracing rules"
77
87
  echo " xgem git rm-branch - Safely drop local and remote workspace branch states"
88
+ echo " xgem git pr - Push current branch and open a GitHub PR (gh-backed)"
89
+ echo " xgem git sync - Rebase current branch onto the repo's base branch"
90
+ echo " xgem git clean-branches - Delete local branches already merged into the base branch"
91
+ echo " xgem git hooks <install|uninstall> - Manage a pre-commit lint/test hook"
92
+ echo " xgem release [major|minor|patch] - Bump version, update CHANGELOG.md, tag, push"
93
+ echo " xgem status - Dashboard across every xgem-tracked project"
94
+ echo " xgem bootstrap - Clone-to-running: detect framework, install, .env, migrate, launch"
95
+ echo " xgem ci - Run lint/test/build for every configured framework, summarized"
78
96
  echo " xgem --version - Print xgem's version"
79
97
  echo ""
80
98
  echo "Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose"
@@ -161,7 +179,7 @@ case "${1:-}" in
161
179
  else
162
180
  scaffold_run_script "$FRAMEWORK" "$SCRIPT_NAME" "$CONFIG_DIR"
163
181
  fi
164
- exit 0
182
+ exit $?
165
183
  ;;
166
184
 
167
185
  terminate)
@@ -178,6 +196,7 @@ case "${1:-}" in
178
196
  rm -rf "${CONFIG_DIR:?}/$fw"
179
197
  done
180
198
  rm -rf "$CONFIG_DIR"
199
+ registry_remove "$(pwd)"
181
200
 
182
201
  if [ -f .gitignore ]; then
183
202
  grep -v "^$CONFIG_DIR/" .gitignore > tmp_gitignore && mv tmp_gitignore .gitignore
@@ -189,7 +208,7 @@ case "${1:-}" in
189
208
  ;;
190
209
 
191
210
  git)
192
- [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>"
211
+ [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch|pr|sync|clean-branches|hooks>"
193
212
  cmd_git "$2" "${3:-}"
194
213
  exit 0
195
214
  ;;
@@ -199,6 +218,26 @@ case "${1:-}" in
199
218
  exit 0
200
219
  ;;
201
220
 
221
+ release)
222
+ cmd_release "${2:-}"
223
+ exit 0
224
+ ;;
225
+
226
+ status)
227
+ cmd_status
228
+ exit 0
229
+ ;;
230
+
231
+ bootstrap)
232
+ cmd_bootstrap
233
+ exit 0
234
+ ;;
235
+
236
+ ci)
237
+ cmd_ci
238
+ exit $?
239
+ ;;
240
+
202
241
  --version|-V|version)
203
242
  print_version
204
243
  exit 0
package/bin/xgem.js CHANGED
@@ -21,6 +21,11 @@ const { cmdDoctor } = require('../lib-win/doctor');
21
21
  const { cmdGit } = require('../lib-win/git');
22
22
  const { cmdFlutter } = require('../lib-win/flutter');
23
23
  const scaffold = require('../lib-win/scaffold');
24
+ const { cmdRelease } = require('../lib-win/release');
25
+ const { registryAdd, registryRemove } = require('../lib-win/registry');
26
+ const { cmdStatus } = require('../lib-win/status');
27
+ const { cmdBootstrap } = require('../lib-win/bootstrap');
28
+ const { cmdCi } = require('../lib-win/ci');
24
29
 
25
30
  const CONFIG_DIR = '.xgem-automate';
26
31
 
@@ -48,6 +53,14 @@ function printUsage() {
48
53
  console.log(' xgem git branch - Pick, create, or switch branches; remembers your choice');
49
54
  console.log(' xgem git rm-remote - Drop specified target remote tracing rules');
50
55
  console.log(' xgem git rm-branch - Safely drop local and remote workspace branch states');
56
+ console.log(' xgem git pr - Push current branch and open a GitHub PR (gh-backed)');
57
+ console.log(' xgem git sync - Rebase current branch onto the repo\'s base branch');
58
+ console.log(' xgem git clean-branches - Delete local branches already merged into the base branch');
59
+ console.log(' xgem git hooks <install|uninstall> - Manage a pre-commit lint/test hook');
60
+ console.log(' xgem release [major|minor|patch] - Bump version, update CHANGELOG.md, tag, push');
61
+ console.log(' xgem status - Dashboard across every xgem-tracked project');
62
+ console.log(' xgem bootstrap - Clone-to-running: detect framework, install, .env, migrate, launch');
63
+ console.log(' xgem ci - Run lint/test/build for every configured framework, summarized');
51
64
  console.log(' xgem --version - Print xgem\'s version');
52
65
  console.log('');
53
66
  console.log('Flags (any command): --yes (skip confirmations), --dry-run (show, don\'t apply), --verbose');
@@ -61,35 +74,6 @@ async function selectFramework(options) {
61
74
  return options[idx];
62
75
  }
63
76
 
64
- // Some teams want the generated scripts checked in so collaborators get the
65
- // same automation; others want them private/local-only. Ask instead of
66
- // always gitignoring.
67
- async function updateGitignore() {
68
- const gitignorePath = '.gitignore';
69
- const ignoreChoice = ((await prompt(`Should ${CONFIG_DIR}/ be ignored by git (private to you), or tracked so collaborators get the same scripts? [ignore/track]`, 'ignore'))).toLowerCase();
70
-
71
- if (ignoreChoice.startsWith('t')) {
72
- if (fs.existsSync(gitignorePath)) {
73
- const lines = fs.readFileSync(gitignorePath, 'utf8').split(/\r?\n/).filter((l) => l !== `${CONFIG_DIR}/`);
74
- fs.writeFileSync(gitignorePath, lines.join('\n'));
75
- logInfo(`Removed existing ${CONFIG_DIR}/ entry from .gitignore since you chose to track it.`);
76
- }
77
- logSuccess(`${CONFIG_DIR}/ will be tracked in git.`);
78
- return;
79
- }
80
-
81
- if (fs.existsSync(gitignorePath)) {
82
- const content = fs.readFileSync(gitignorePath, 'utf8');
83
- if (!content.includes(`${CONFIG_DIR}/`)) {
84
- fs.appendFileSync(gitignorePath, `\n${CONFIG_DIR}/\n`);
85
- logSuccess('Added automation tracking to .gitignore');
86
- }
87
- } else {
88
- fs.writeFileSync(gitignorePath, `${CONFIG_DIR}/\n`);
89
- logSuccess('Created .gitignore and hidden tracking layer folder references.');
90
- }
91
- }
92
-
93
77
  async function cmdInit() {
94
78
  printBanner();
95
79
  if (fs.existsSync(CONFIG_DIR)) {
@@ -97,14 +81,12 @@ async function cmdInit() {
97
81
  return;
98
82
  }
99
83
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
84
+ registryAdd(process.cwd());
100
85
 
101
86
  console.log('Select your starting framework architecture:');
102
87
  const fw = await selectFramework(scaffold.ALL_FRAMEWORKS);
103
88
  if (!fw) die('Invalid selection.');
104
- scaffold.injectTemplates(fw, CONFIG_DIR);
105
- logSuccess(`Successfully appended standard scripts for: ${CONFIG_DIR}/${fw}`);
106
-
107
- await updateGitignore();
89
+ await scaffold.bookkeeping(fw, CONFIG_DIR);
108
90
  }
109
91
 
110
92
  async function cmdAdd() {
@@ -154,6 +136,7 @@ async function cmdTerminate() {
154
136
  fs.rmSync(path.join(CONFIG_DIR, fw), { recursive: true, force: true });
155
137
  }
156
138
  fs.rmSync(CONFIG_DIR, { recursive: true, force: true });
139
+ registryRemove(process.cwd());
157
140
 
158
141
  if (fs.existsSync('.gitignore')) {
159
142
  const lines = fs.readFileSync('.gitignore', 'utf8').split(/\r?\n/).filter((l) => l !== `${CONFIG_DIR}/`);
@@ -203,9 +186,13 @@ async function main() {
203
186
  case 'run': return cmdRun(a2, a3);
204
187
  case 'terminate': return cmdTerminate();
205
188
  case 'git':
206
- if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>');
207
- return cmdGit(a2, a3);
189
+ if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch|pr|sync|clean-branches|hooks>');
190
+ return cmdGit(a2, a3, CONFIG_DIR);
208
191
  case 'doctor': return cmdDoctor(a2);
192
+ case 'release': return cmdRelease(a2);
193
+ case 'status': return cmdStatus(CONFIG_DIR);
194
+ case 'bootstrap': return cmdBootstrap(CONFIG_DIR);
195
+ case 'ci': return cmdCi(CONFIG_DIR);
209
196
  case '--version':
210
197
  case '-V':
211
198
  case 'version': {
@@ -0,0 +1,165 @@
1
+ #!/bin/bash
2
+ # xgem bootstrap: clone-to-running in one command, for a project you just
3
+ # checked out (not creating a new one — that's `xgem init`'s job).
4
+ # Depends on lib/logger.sh, lib/utils.sh, lib/scaffold.sh, lib/create.sh.
5
+
6
+ # _detect_framework -> a name from ALL_FRAMEWORKS, or empty if nothing
7
+ # recognizable was found. Never assumes; probes for marker files the same
8
+ # way lib/doctor.sh probes for tools instead of guessing.
9
+ _detect_framework() {
10
+ [ -f pubspec.yaml ] && { echo flutter; return 0; }
11
+ [ -f go.mod ] && { echo go; return 0; }
12
+ [ -f Cargo.toml ] && { echo rust; return 0; }
13
+ { [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f setup.py ]; } && { echo python; return 0; }
14
+ [ -f Package.swift ] && { echo swift; return 0; }
15
+ if [ -f package.json ]; then
16
+ if grep -q '"next"' package.json; then echo next
17
+ elif grep -q '"@angular/core"' package.json; then echo angular
18
+ elif grep -q '"vue"' package.json; then echo vue
19
+ elif grep -q '"react"' package.json; then echo react
20
+ else echo node
21
+ fi
22
+ return 0
23
+ fi
24
+ [ -f Dockerfile ] && { echo docker; return 0; }
25
+ return 1
26
+ }
27
+
28
+ _bootstrap_env_file() {
29
+ [ -f .env ] && { log_debug ".env already exists, leaving it as-is."; return 0; }
30
+ local example
31
+ for example in .env.example .env.sample; do
32
+ [ -f "$example" ] || continue
33
+ cp "$example" .env
34
+ log_success "Created .env from $example."
35
+ local missing
36
+ missing=$(grep -E '^[A-Za-z_][A-Za-z0-9_]*=[[:space:]]*$' .env | cut -d= -f1)
37
+ if [ -n "$missing" ]; then
38
+ log_warn "These .env keys are empty — fill them in:"
39
+ echo "$missing" | sed 's/^/ - /'
40
+ fi
41
+ return 0
42
+ done
43
+ }
44
+
45
+ _bootstrap_node_pm() {
46
+ if [ -f pnpm-lock.yaml ]; then echo pnpm
47
+ elif [ -f yarn.lock ]; then echo yarn
48
+ elif [ -f bun.lockb ] || [ -f bun.lock ]; then echo bun
49
+ else echo npm
50
+ fi
51
+ }
52
+
53
+ _bootstrap_install() {
54
+ local fw=$1
55
+ case "$fw" in
56
+ node|react|vue|angular|next)
57
+ local pm
58
+ pm=$(_bootstrap_node_pm)
59
+ log_info "Installing dependencies with $pm..."
60
+ case "$pm" in
61
+ yarn) yarn ;;
62
+ pnpm) pnpm install ;;
63
+ bun) bun install ;;
64
+ *) npm install ;;
65
+ esac
66
+ ;;
67
+ 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
71
+ ;;
72
+ python)
73
+ if [ -f pyproject.toml ] && has_cmd poetry; then
74
+ log_info "Installing dependencies with poetry..."
75
+ poetry install
76
+ 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
81
+ fi
82
+ ;;
83
+ 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
87
+ ;;
88
+ rust)
89
+ has_cmd cargo || { log_warn "cargo not found — skipping 'cargo fetch'."; return 0; }
90
+ log_info "Running cargo fetch..."
91
+ cargo fetch
92
+ ;;
93
+ 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
97
+ ;;
98
+ docker)
99
+ log_debug "Docker project — nothing to install locally."
100
+ ;;
101
+ esac
102
+ }
103
+
104
+ _bootstrap_migrations() {
105
+ local fw=$1
106
+ if [ "$fw" = "node" ] || [ "$fw" = "react" ] || [ "$fw" = "vue" ] || [ "$fw" = "angular" ] || [ "$fw" = "next" ]; then
107
+ if _package_json_has_script migrate; then
108
+ local pm
109
+ pm=$(_bootstrap_node_pm)
110
+ confirm "Run the project's 'migrate' script (via $pm)?" || return 0
111
+ case "$pm" in
112
+ yarn) yarn migrate ;;
113
+ pnpm) pnpm run migrate ;;
114
+ bun) bun run migrate ;;
115
+ *) npm run migrate ;;
116
+ esac
117
+ return 0
118
+ fi
119
+ fi
120
+ if [ -f prisma/schema.prisma ]; then
121
+ confirm "Run 'npx prisma migrate dev'?" && npx prisma migrate dev
122
+ elif [ -f manage.py ]; then
123
+ has_cmd python3 && confirm "Run 'python3 manage.py migrate'?" && python3 manage.py migrate
124
+ fi
125
+ }
126
+
127
+ _bootstrap_offer_dev_server() {
128
+ local fw=$1
129
+ local script=""
130
+ case "$fw" in
131
+ node) script=start ;;
132
+ react|vue|angular|next) script=dev ;;
133
+ *) return 0 ;;
134
+ esac
135
+ [ -f "$CONFIG_DIR/$fw/$script.sh" ] || return 0
136
+ confirm "Launch the dev server now ('xgem run $fw $script')?" && scaffold_run_script "$fw" "$script" "$CONFIG_DIR"
137
+ }
138
+
139
+ cmd_bootstrap() {
140
+ log_info "Detecting project framework..."
141
+ local fw
142
+ fw=$(_detect_framework)
143
+ if [ -z "$fw" ]; then
144
+ log_warn "Could not auto-detect the framework from any known marker file."
145
+ echo "Pick one:"
146
+ PS3="Enter target selection number: "
147
+ select fw in "${ALL_FRAMEWORKS[@]}"; do
148
+ [ -n "$fw" ] && break
149
+ echo "Invalid selection."
150
+ done
151
+ else
152
+ log_success "Detected: $fw"
153
+ fi
154
+
155
+ _bootstrap_env_file
156
+ _bootstrap_install "$fw"
157
+ _bootstrap_migrations "$fw"
158
+
159
+ if [ ! -d "$CONFIG_DIR" ]; then
160
+ confirm "Scaffold xgem automation scripts (.xgem-automate) for $fw too?" && _xgem_bookkeeping "$fw"
161
+ fi
162
+
163
+ _bootstrap_offer_dev_server "$fw"
164
+ log_success "Bootstrap complete."
165
+ }
package/lib/ci.sh ADDED
@@ -0,0 +1,44 @@
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.
5
+
6
+ cmd_ci() {
7
+ [ -d "$CONFIG_DIR" ] || die "No $CONFIG_DIR found — run 'xgem init' or 'xgem add' first."
8
+
9
+ local -a steps=(lint test build)
10
+ local -a results=()
11
+ local overall_status=0
12
+ local fw_dir fw step
13
+
14
+ for fw_dir in "$CONFIG_DIR"/*/; do
15
+ [ -d "$fw_dir" ] || continue
16
+ fw=$(basename "$fw_dir")
17
+ for step in "${steps[@]}"; do
18
+ [ -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
26
+ done
27
+ done
28
+
29
+ if [ ${#results[@]} -eq 0 ]; then
30
+ log_warn "No lint/test/build scripts found under $CONFIG_DIR to run."
31
+ return 0
32
+ fi
33
+
34
+ echo ""
35
+ echo "=== xgem ci summary ==="
36
+ printf '%s\n' "${results[@]}"
37
+
38
+ if [ "$overall_status" -eq 0 ]; then
39
+ log_success "All checks passed."
40
+ else
41
+ log_error "Some checks failed."
42
+ fi
43
+ return "$overall_status"
44
+ }
package/lib/create.sh CHANGED
@@ -42,6 +42,7 @@ XGEM_CREATED_NEW_FOLDER=""
42
42
  _xgem_bookkeeping() {
43
43
  local fw=$1
44
44
  mkdir -p "$CONFIG_DIR"
45
+ registry_add "$(pwd)"
45
46
  scaffold_inject_templates "$fw" "$CONFIG_DIR"
46
47
  log_success "Successfully appended standard scripts for: $CONFIG_DIR/$fw"
47
48
 
@@ -98,15 +99,6 @@ _enter_project_dir() {
98
99
  mkdir -p "$project_dir" && cd "$project_dir" || die "Could not enter $project_dir"
99
100
  }
100
101
 
101
- _open_url() {
102
- local url=$1
103
- case "$(detect_os)" in
104
- darwin) open "$url" 2>/dev/null ;;
105
- linux) xdg-open "$url" 2>/dev/null ;;
106
- *) log_debug "Don't know how to open a browser on this OS — visit $url manually." ;;
107
- esac
108
- }
109
-
110
102
  # _launch_dev_server_and_open_browser <cmd...>
111
103
  # Starts the dev server in the background, tails its log for the first
112
104
  # http://localhost URL it prints — this works across Vite/CRA/Next/Angular