git-stack-cli 2.7.0 → 2.7.1

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/dist/js/index.js CHANGED
@@ -31938,7 +31938,7 @@ var import_react21 = __toESM(require_react(), 1);
31938
31938
  var React56 = __toESM(require_react(), 1);
31939
31939
 
31940
31940
  // src/app/AutoUpdate.tsx
31941
- var React18 = __toESM(require_react(), 1);
31941
+ var React19 = __toESM(require_react(), 1);
31942
31942
 
31943
31943
  // src/app/Brackets.tsx
31944
31944
  var React10 = __toESM(require_react(), 1);
@@ -31993,24 +31993,34 @@ function FormatText(props) {
31993
31993
  });
31994
31994
  }
31995
31995
 
31996
+ // src/app/Url.tsx
31997
+ var React13 = __toESM(require_react(), 1);
31998
+ function Url(props) {
31999
+ return /* @__PURE__ */ React13.createElement(Text, {
32000
+ bold: true,
32001
+ color: colors.blue,
32002
+ ...props
32003
+ }, props.children);
32004
+ }
32005
+
31996
32006
  // src/app/YesNoPrompt.tsx
31997
- var React14 = __toESM(require_react(), 1);
32007
+ var React15 = __toESM(require_react(), 1);
31998
32008
 
31999
32009
  // src/app/Parens.tsx
32000
- var React13 = __toESM(require_react(), 1);
32010
+ var React14 = __toESM(require_react(), 1);
32001
32011
  function Parens(props) {
32002
32012
  const color = colors.blue;
32003
- return /* @__PURE__ */ React13.createElement(Text, null, /* @__PURE__ */ React13.createElement(Text, {
32013
+ return /* @__PURE__ */ React14.createElement(Text, null, /* @__PURE__ */ React14.createElement(Text, {
32004
32014
  color
32005
- }, "("), props.children, /* @__PURE__ */ React13.createElement(Text, {
32015
+ }, "("), props.children, /* @__PURE__ */ React14.createElement(Text, {
32006
32016
  color
32007
32017
  }, ")"));
32008
32018
  }
32009
32019
 
32010
32020
  // src/app/YesNoPrompt.tsx
32011
32021
  function YesNoPrompt(props) {
32012
- const [answer, set_answer] = React14.useState("");
32013
- const answered_ref = React14.useRef(false);
32022
+ const [answer, set_answer] = React15.useState("");
32023
+ const answered_ref = React15.useRef(false);
32014
32024
  use_input_default((input) => {
32015
32025
  if (answered_ref.current) {
32016
32026
  return;
@@ -32032,11 +32042,11 @@ function YesNoPrompt(props) {
32032
32042
  }
32033
32043
  });
32034
32044
  const choices = function get_choices() {
32035
- const y3 = /* @__PURE__ */ React14.createElement(Text, {
32045
+ const y3 = /* @__PURE__ */ React15.createElement(Text, {
32036
32046
  bold: true,
32037
32047
  color: colors.green
32038
32048
  }, "Y");
32039
- const n3 = /* @__PURE__ */ React14.createElement(Text, {
32049
+ const n3 = /* @__PURE__ */ React15.createElement(Text, {
32040
32050
  color: colors.red
32041
32051
  }, "n");
32042
32052
  switch (answer) {
@@ -32045,19 +32055,19 @@ function YesNoPrompt(props) {
32045
32055
  case "n":
32046
32056
  return n3;
32047
32057
  default:
32048
- return /* @__PURE__ */ React14.createElement(FormatText, {
32058
+ return /* @__PURE__ */ React15.createElement(FormatText, {
32049
32059
  message: "{y}/{n}",
32050
32060
  values: { y: y3, n: n3 }
32051
32061
  });
32052
32062
  }
32053
32063
  }();
32054
- return /* @__PURE__ */ React14.createElement(Box_default, {
32064
+ return /* @__PURE__ */ React15.createElement(Box_default, {
32055
32065
  flexDirection: "column"
32056
- }, /* @__PURE__ */ React14.createElement(Box_default, {
32066
+ }, /* @__PURE__ */ React15.createElement(Box_default, {
32057
32067
  alignItems: "flex-end"
32058
- }, typeof props.message === "object" ? props.message : /* @__PURE__ */ React14.createElement(Text, {
32068
+ }, typeof props.message === "object" ? props.message : /* @__PURE__ */ React15.createElement(Text, {
32059
32069
  color: colors.yellow
32060
- }, props.message), /* @__PURE__ */ React14.createElement(Text, null, " "), /* @__PURE__ */ React14.createElement(Parens, null, /* @__PURE__ */ React14.createElement(Text, {
32070
+ }, props.message), /* @__PURE__ */ React15.createElement(Text, null, " "), /* @__PURE__ */ React15.createElement(Parens, null, /* @__PURE__ */ React15.createElement(Text, {
32061
32071
  color: colors.gray
32062
32072
  }, choices))));
32063
32073
  }
@@ -32071,7 +32081,7 @@ function assertNever(value) {
32071
32081
  import * as child from "node:child_process";
32072
32082
 
32073
32083
  // src/app/Store.tsx
32074
- var React17 = __toESM(require_react(), 1);
32084
+ var React18 = __toESM(require_react(), 1);
32075
32085
 
32076
32086
  // node_modules/.pnpm/zustand@4.4.4_@types+react@18.2.33_immer@10.0.3_react@18.2.0/node_modules/zustand/esm/vanilla.mjs
32077
32087
  var createStoreImpl = (createState) => {
@@ -32702,7 +32712,7 @@ var immerImpl = (initializer) => (set2, get, store) => {
32702
32712
  var immer2 = immerImpl;
32703
32713
 
32704
32714
  // src/app/Exit.tsx
32705
- var React15 = __toESM(require_react(), 1);
32715
+ var React16 = __toESM(require_react(), 1);
32706
32716
 
32707
32717
  // src/core/sleep.ts
32708
32718
  async function sleep(time) {
@@ -32711,7 +32721,7 @@ async function sleep(time) {
32711
32721
 
32712
32722
  // src/app/Exit.tsx
32713
32723
  function Exit(props) {
32714
- React15.useEffect(() => {
32724
+ React16.useEffect(() => {
32715
32725
  Exit.handle_exit(props).catch((err) => {
32716
32726
  console.error(err);
32717
32727
  });
@@ -32728,7 +32738,7 @@ Exit.handle_exit = async function handle_exit(props) {
32728
32738
  }
32729
32739
  if (state.is_dirty_check_stash) {
32730
32740
  await cli("git stash pop");
32731
- actions.output(/* @__PURE__ */ React15.createElement(Text, {
32741
+ actions.output(/* @__PURE__ */ React16.createElement(Text, {
32732
32742
  color: colors.green
32733
32743
  }, "✅ Changes restored from stash"));
32734
32744
  }
@@ -32742,7 +32752,7 @@ Exit.handle_exit = async function handle_exit(props) {
32742
32752
  };
32743
32753
 
32744
32754
  // src/app/LogTimestamp.tsx
32745
- var React16 = __toESM(require_react(), 1);
32755
+ var React17 = __toESM(require_react(), 1);
32746
32756
 
32747
32757
  // node_modules/.pnpm/luxon@3.4.4/node_modules/luxon/src/errors.js
32748
32758
  class LuxonError extends Error {
@@ -37037,7 +37047,7 @@ function friendlyDateTime(dateTimeish) {
37037
37047
 
37038
37048
  // src/app/LogTimestamp.tsx
37039
37049
  function LogTimestamp() {
37040
- return /* @__PURE__ */ React16.createElement(Text, {
37050
+ return /* @__PURE__ */ React17.createElement(Text, {
37041
37051
  dimColor: true
37042
37052
  }, DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] "));
37043
37053
  }
@@ -37081,7 +37091,7 @@ var BaseStore = createStore()(immer2((set2, get) => ({
37081
37091
  state.exit_mode = "normal";
37082
37092
  }
37083
37093
  let clear = args?.clear ?? true;
37084
- const node = /* @__PURE__ */ React17.createElement(Exit, {
37094
+ const node = /* @__PURE__ */ React18.createElement(Exit, {
37085
37095
  clear,
37086
37096
  code
37087
37097
  });
@@ -37109,17 +37119,17 @@ var BaseStore = createStore()(immer2((set2, get) => ({
37109
37119
  error(error) {
37110
37120
  let node;
37111
37121
  if (typeof error === "string") {
37112
- node = /* @__PURE__ */ React17.createElement(Text, {
37122
+ node = /* @__PURE__ */ React18.createElement(Text, {
37113
37123
  color: colors.red
37114
37124
  }, error);
37115
37125
  } else if (error instanceof Error) {
37116
- node = /* @__PURE__ */ React17.createElement(Box_default, {
37126
+ node = /* @__PURE__ */ React18.createElement(Box_default, {
37117
37127
  flexDirection: "column"
37118
- }, /* @__PURE__ */ React17.createElement(Text, {
37128
+ }, /* @__PURE__ */ React18.createElement(Text, {
37119
37129
  color: colors.red
37120
37130
  }, error.stack));
37121
37131
  } else {
37122
- node = /* @__PURE__ */ React17.createElement(Text, {
37132
+ node = /* @__PURE__ */ React18.createElement(Text, {
37123
37133
  color: colors.red
37124
37134
  }, `Unhandled error: ${JSON.stringify(error)}`);
37125
37135
  }
@@ -37203,12 +37213,12 @@ function renderOutputArgs(args) {
37203
37213
  case "boolean":
37204
37214
  case "number":
37205
37215
  case "string":
37206
- output = /* @__PURE__ */ React17.createElement(Text, {
37216
+ output = /* @__PURE__ */ React18.createElement(Text, {
37207
37217
  dimColor: args.debug
37208
37218
  }, String(args.node));
37209
37219
  }
37210
37220
  if (args.debug) {
37211
- return /* @__PURE__ */ React17.createElement(React17.Fragment, null, args.withoutTimestamp ? null : /* @__PURE__ */ React17.createElement(LogTimestamp, null), output);
37221
+ return /* @__PURE__ */ React18.createElement(React18.Fragment, null, args.withoutTimestamp ? null : /* @__PURE__ */ React18.createElement(LogTimestamp, null), output);
37212
37222
  }
37213
37223
  return output;
37214
37224
  }
@@ -37430,127 +37440,28 @@ function reducer(state, patch) {
37430
37440
  return { ...state, ...patch };
37431
37441
  }
37432
37442
  function AutoUpdate(props) {
37433
- const props_ref = React18.useRef(props);
37443
+ const props_ref = React19.useRef(props);
37434
37444
  props_ref.current = props;
37435
- const [output, set_output] = React18.useState([]);
37436
- const [state, patch] = React18.useReducer(reducer, {
37437
- error: null,
37445
+ const [output, set_output] = React19.useState([]);
37446
+ const [state, patch] = React19.useReducer(reducer, {
37447
+ status: "init",
37438
37448
  local_version: null,
37439
37449
  latest_version: null,
37440
- status: "init",
37441
37450
  is_brew_bun_standalone: false
37442
37451
  });
37443
- function handle_output(node) {
37444
- if (typeof props.onOutput === "function") {
37445
- props.onOutput(node);
37446
- } else {
37447
- set_output((current2) => {
37448
- return [...current2, node];
37449
- });
37450
- }
37451
- }
37452
- React18.useEffect(() => {
37452
+ React19.useEffect(handle_init_state, []);
37453
+ React19.useEffect(handle_status, [state.latest_version]);
37454
+ React19.useEffect(handle_on_done, [state.status]);
37455
+ const status = render_status();
37456
+ return /* @__PURE__ */ React19.createElement(React19.Fragment, null, output, status);
37457
+ function render_status() {
37453
37458
  switch (state.status) {
37454
37459
  case "init":
37455
- case "prompt":
37460
+ return null;
37456
37461
  case "install":
37457
- break;
37458
- case "done": {
37459
- props.onDone?.();
37460
- break;
37461
- }
37462
- default:
37463
- assertNever(state.status);
37464
- }
37465
- }, [state.status]);
37466
- React18.useEffect(() => {
37467
- let status2 = "init";
37468
- let latest_version = null;
37469
- let is_brew_bun_standalone = false;
37470
- const local_version = "2.7.0";
37471
- const is_output = props_ref.current.verbose || props_ref.current.force;
37472
- async function auto_update() {
37473
- if (!local_version) {
37474
- throw new Error("Auto update requires process.env.CLI_VERSION to be set");
37475
- }
37476
- const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
37477
- const npm_json = await Promise.race([
37478
- fetch_json(`https://registry.npmjs.org/${props.name}`),
37479
- sleep(timeout_ms).then(() => {
37480
- throw new Error("AutoUpdate timeout");
37481
- })
37482
- ]);
37483
- latest_version = npm_json?.["dist-tags"]?.latest;
37484
- if (!latest_version) {
37485
- throw new Error("Unable to retrieve latest version from npm");
37486
- }
37487
- const binary_path = process.argv[1];
37488
- if (props_ref.current.verbose) {
37489
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37490
- dimColor: true
37491
- }, JSON.stringify({ binary_path })));
37492
- }
37493
- is_brew_bun_standalone = binary_path.startsWith("/$bunfs");
37494
- if (props_ref.current.verbose) {
37495
- if (is_brew_bun_standalone) {
37496
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37497
- dimColor: true
37498
- }, "brew install detected (compiled bun standalone)"));
37499
- } else {
37500
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37501
- dimColor: true
37502
- }, "npm install detected"));
37503
- }
37504
- }
37505
- if (props_ref.current.verbose) {
37506
- handle_output(/* @__PURE__ */ React18.createElement(FormatText, {
37507
- key: "versions",
37508
- wrapper: /* @__PURE__ */ React18.createElement(Text, null),
37509
- message: "Auto update found latest version {latest_version} and current local version {local_version}",
37510
- values: {
37511
- latest_version: /* @__PURE__ */ React18.createElement(Brackets, null, latest_version),
37512
- local_version: /* @__PURE__ */ React18.createElement(Brackets, null, local_version)
37513
- }
37514
- }));
37515
- }
37516
- const semver_result = semver_compare(latest_version, local_version);
37517
- if (props_ref.current.verbose) {
37518
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37519
- dimColor: true
37520
- }, JSON.stringify({ semver_result })));
37521
- }
37522
- if (semver_result === 0) {
37523
- status2 = "done";
37524
- if (is_output) {
37525
- handle_output(/* @__PURE__ */ React18.createElement(Text, null, "✅ Everything up to date. ", /* @__PURE__ */ React18.createElement(Brackets, null, latest_version)));
37526
- }
37527
- return;
37528
- }
37529
- if (semver_result === 1) {
37530
- status2 = "prompt";
37531
- }
37532
- throw new Error("AutoUpdate failed");
37533
- }
37534
- const onError = props_ref.current.onError || (() => {
37535
- });
37536
- auto_update().then(() => {
37537
- patch({ status: status2, local_version, latest_version, is_brew_bun_standalone });
37538
- }).catch((error) => {
37539
- if (props_ref.current.verbose) {
37540
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37541
- key: "error",
37542
- color: colors.red
37543
- }, error?.message));
37544
- }
37545
- status2 = "done";
37546
- patch({ status: status2, error, local_version, latest_version, is_brew_bun_standalone });
37547
- onError(error);
37548
- });
37549
- }, []);
37550
- const status = function render_status() {
37551
- switch (state.status) {
37552
- case "init":
37553
37462
  return null;
37463
+ case "done":
37464
+ return props.children;
37554
37465
  case "prompt": {
37555
37466
  let install_command = "";
37556
37467
  if (state.is_brew_bun_standalone) {
@@ -37558,27 +37469,21 @@ function AutoUpdate(props) {
37558
37469
  } else {
37559
37470
  install_command = `npm install -g ${props.name}@latest`;
37560
37471
  }
37561
- return /* @__PURE__ */ React18.createElement(YesNoPrompt, {
37562
- message: /* @__PURE__ */ React18.createElement(Box_default, {
37563
- flexDirection: "column"
37564
- }, /* @__PURE__ */ React18.createElement(Box_default, {
37565
- flexDirection: "column"
37566
- }, /* @__PURE__ */ React18.createElement(Text, {
37567
- color: colors.yellow
37568
- }, /* @__PURE__ */ React18.createElement(FormatText, {
37569
- wrapper: /* @__PURE__ */ React18.createElement(Text, null),
37570
- message: "New version available {latest_version}",
37571
- values: {
37572
- latest_version: /* @__PURE__ */ React18.createElement(Brackets, null, state.latest_version)
37573
- }
37574
- }), ","), /* @__PURE__ */ React18.createElement(Text, null, " "), /* @__PURE__ */ React18.createElement(Command, null, install_command), /* @__PURE__ */ React18.createElement(Text, null, " ")), /* @__PURE__ */ React18.createElement(Box_default, null, /* @__PURE__ */ React18.createElement(FormatText, {
37575
- wrapper: /* @__PURE__ */ React18.createElement(Text, {
37472
+ return /* @__PURE__ */ React19.createElement(YesNoPrompt, {
37473
+ message: /* @__PURE__ */ React19.createElement(Box_default, {
37474
+ flexDirection: "column",
37475
+ gap: 1
37476
+ }, /* @__PURE__ */ React19.createElement(Command, null, install_command), /* @__PURE__ */ React19.createElement(FormatText, {
37477
+ wrapper: /* @__PURE__ */ React19.createElement(Text, {
37576
37478
  color: colors.yellow
37577
37479
  }),
37578
37480
  message: "Would you like to run the above command to update?"
37579
- }))),
37481
+ })),
37482
+ onNo: () => {
37483
+ patch({ status: "done" });
37484
+ },
37580
37485
  onYes: async () => {
37581
- handle_output(/* @__PURE__ */ React18.createElement(Command, null, install_command));
37486
+ info(/* @__PURE__ */ React19.createElement(Command, null, install_command));
37582
37487
  patch({ status: "install" });
37583
37488
  await cli(install_command, {
37584
37489
  env: {
@@ -37586,34 +37491,164 @@ function AutoUpdate(props) {
37586
37491
  HOMEBREW_COLOR: "1"
37587
37492
  },
37588
37493
  onOutput: (data) => {
37589
- handle_output(/* @__PURE__ */ React18.createElement(Text, null, data));
37494
+ info(/* @__PURE__ */ React19.createElement(Text, null, data));
37590
37495
  }
37591
37496
  });
37592
- handle_output(/* @__PURE__ */ React18.createElement(Text, {
37497
+ info(/* @__PURE__ */ React19.createElement(Text, {
37593
37498
  key: "done"
37594
- }, "✅ Installed ", /* @__PURE__ */ React18.createElement(Brackets, null, state.latest_version)));
37595
- patch({ status: "done" });
37596
- },
37597
- onNo: () => {
37499
+ }, "✅ Installed ", /* @__PURE__ */ React19.createElement(Brackets, null, state.latest_version)));
37598
37500
  patch({ status: "done" });
37599
37501
  }
37600
37502
  });
37601
37503
  }
37504
+ }
37505
+ }
37506
+ function handle_on_done() {
37507
+ switch (state.status) {
37508
+ case "init":
37509
+ case "prompt":
37602
37510
  case "install":
37603
- return null;
37604
- case "done":
37605
- return props.children;
37511
+ break;
37512
+ case "done": {
37513
+ props.onDone?.();
37514
+ break;
37515
+ }
37516
+ default:
37517
+ assertNever(state.status);
37606
37518
  }
37607
- }();
37608
- return /* @__PURE__ */ React18.createElement(React18.Fragment, null, output, status);
37519
+ }
37520
+ function handle_init_state() {
37521
+ init_state().catch(abort);
37522
+ async function init_state() {
37523
+ if (state.latest_version !== null)
37524
+ return;
37525
+ const local_version = "2.7.1";
37526
+ const latest_version = await get_latest_version();
37527
+ const is_brew_bun_standalone = get_is_brew_bun_standalone();
37528
+ patch({ local_version, latest_version, is_brew_bun_standalone });
37529
+ }
37530
+ async function get_latest_version() {
37531
+ const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
37532
+ const npm_json = await Promise.race([
37533
+ fetch_json(`https://registry.npmjs.org/${props.name}`),
37534
+ sleep(timeout_ms).then(() => {
37535
+ abort(new Error("AutoUpdate timeout"));
37536
+ })
37537
+ ]);
37538
+ const maybe_version = npm_json?.["dist-tags"]?.latest;
37539
+ if (typeof maybe_version === "string") {
37540
+ return maybe_version;
37541
+ }
37542
+ throw new Error("Unable to retrieve latest version from npm");
37543
+ }
37544
+ function get_is_brew_bun_standalone() {
37545
+ const binary_path = process.argv[1];
37546
+ debug(/* @__PURE__ */ React19.createElement(Text, {
37547
+ dimColor: true
37548
+ }, JSON.stringify({ binary_path })));
37549
+ const is_bunfs_path = binary_path.startsWith("/$bunfs");
37550
+ debug(/* @__PURE__ */ React19.createElement(Text, {
37551
+ dimColor: true
37552
+ }, is_bunfs_path ? "brew install detected (compiled bun standalone)" : "npm install detected"));
37553
+ return is_bunfs_path;
37554
+ }
37555
+ }
37556
+ function handle_status() {
37557
+ const latest_version = state.latest_version;
37558
+ if (latest_version === null) {
37559
+ return;
37560
+ }
37561
+ const local_version = state.local_version;
37562
+ if (!local_version) {
37563
+ throw new Error("Auto update requires process.env.CLI_VERSION to be set");
37564
+ }
37565
+ debug(/* @__PURE__ */ React19.createElement(FormatText, {
37566
+ key: "versions",
37567
+ wrapper: /* @__PURE__ */ React19.createElement(Text, {
37568
+ dimColor: true
37569
+ }),
37570
+ message: "Auto update found latest version {latest_version} and current local version {local_version}",
37571
+ values: {
37572
+ latest_version: /* @__PURE__ */ React19.createElement(Brackets, null, latest_version),
37573
+ local_version: /* @__PURE__ */ React19.createElement(Brackets, null, local_version)
37574
+ }
37575
+ }));
37576
+ const semver_result = semver_compare(latest_version, local_version);
37577
+ debug(/* @__PURE__ */ React19.createElement(Text, {
37578
+ dimColor: true
37579
+ }, JSON.stringify({ semver_result })));
37580
+ switch (semver_result) {
37581
+ case 0: {
37582
+ info(/* @__PURE__ */ React19.createElement(Text, null, "✅ Everything up to date. ", /* @__PURE__ */ React19.createElement(Brackets, null, latest_version)));
37583
+ return patch({ status: "done" });
37584
+ }
37585
+ case 1: {
37586
+ const old_tag = local_version;
37587
+ const new_tag = state.latest_version;
37588
+ const url = `https://github.com/magus/git-stack-cli/compare/${old_tag}...${new_tag}`;
37589
+ info(/* @__PURE__ */ React19.createElement(Box_default, {
37590
+ flexDirection: "column",
37591
+ gap: 1,
37592
+ paddingTop: 1,
37593
+ paddingBottom: 1
37594
+ }, /* @__PURE__ */ React19.createElement(Text, null, "\uD83C\uDD95 New version available! ", /* @__PURE__ */ React19.createElement(Brackets, null, latest_version)), /* @__PURE__ */ React19.createElement(Box_default, {
37595
+ flexDirection: "column"
37596
+ }, /* @__PURE__ */ React19.createElement(Text, {
37597
+ dimColor: true
37598
+ }, "Changelog"), /* @__PURE__ */ React19.createElement(Url, null, url))));
37599
+ return patch({ status: "prompt" });
37600
+ }
37601
+ case -1: {
37602
+ info(/* @__PURE__ */ React19.createElement(FormatText, {
37603
+ message: "⚠️ Local version {local_version} is newer than latest version {latest_version}",
37604
+ values: {
37605
+ local_version: /* @__PURE__ */ React19.createElement(Brackets, null, local_version),
37606
+ latest_version: /* @__PURE__ */ React19.createElement(Brackets, null, latest_version)
37607
+ }
37608
+ }));
37609
+ return patch({ status: "done" });
37610
+ }
37611
+ default: {
37612
+ assertNever(semver_result);
37613
+ abort(new Error("AutoUpdate failed"));
37614
+ }
37615
+ }
37616
+ }
37617
+ function info(node) {
37618
+ if (props_ref.current.verbose || props_ref.current.force) {
37619
+ handle_output(node);
37620
+ }
37621
+ }
37622
+ function debug(node) {
37623
+ if (props_ref.current.verbose) {
37624
+ handle_output(node);
37625
+ }
37626
+ }
37627
+ function abort(error) {
37628
+ info(/* @__PURE__ */ React19.createElement(Text, {
37629
+ key: "error",
37630
+ color: colors.red
37631
+ }, error.message));
37632
+ patch({ status: "done" });
37633
+ props_ref.current.onError?.(error);
37634
+ }
37635
+ function handle_output(node) {
37636
+ if (typeof props.onOutput === "function") {
37637
+ props.onOutput(node);
37638
+ } else {
37639
+ set_output((current2) => {
37640
+ return [...current2, node];
37641
+ });
37642
+ }
37643
+ }
37609
37644
  }
37610
37645
 
37611
37646
  // src/app/CherryPickCheck.tsx
37612
- var React20 = __toESM(require_react(), 1);
37647
+ var React21 = __toESM(require_react(), 1);
37613
37648
  import path from "node:path";
37614
37649
 
37615
37650
  // src/app/Await.tsx
37616
- var React19 = __toESM(require_react(), 1);
37651
+ var React20 = __toESM(require_react(), 1);
37617
37652
 
37618
37653
  // src/core/cache.ts
37619
37654
  function cache3(cacheable) {
@@ -37661,8 +37696,8 @@ function invariant(condition, message) {
37661
37696
 
37662
37697
  // src/app/Await.tsx
37663
37698
  function Await(props) {
37664
- const [display_fallback, set_display_fallback] = React19.useState(false);
37665
- const cacheRef = React19.useRef(null);
37699
+ const [display_fallback, set_display_fallback] = React20.useState(false);
37700
+ const cacheRef = React20.useRef(null);
37666
37701
  if (!cacheRef.current) {
37667
37702
  cacheRef.current = cache3(props.function);
37668
37703
  }
@@ -37672,7 +37707,7 @@ function Await(props) {
37672
37707
  } else {
37673
37708
  delayFallbackMs = 1000;
37674
37709
  }
37675
- React19.useEffect(() => {
37710
+ React20.useEffect(() => {
37676
37711
  const cache4 = cacheRef.current;
37677
37712
  if (!cache4) {
37678
37713
  return;
@@ -37689,13 +37724,13 @@ function Await(props) {
37689
37724
  }, [delayFallbackMs]);
37690
37725
  invariant(cacheRef.current, "cache must exist");
37691
37726
  if ("fallback" in props) {
37692
- return /* @__PURE__ */ React19.createElement(React19.Suspense, {
37727
+ return /* @__PURE__ */ React20.createElement(React20.Suspense, {
37693
37728
  fallback: !display_fallback ? null : props.fallback
37694
- }, /* @__PURE__ */ React19.createElement(ReadCache, {
37729
+ }, /* @__PURE__ */ React20.createElement(ReadCache, {
37695
37730
  cache: cacheRef.current
37696
37731
  }, props.children));
37697
37732
  }
37698
- return /* @__PURE__ */ React19.createElement(ReadCache, {
37733
+ return /* @__PURE__ */ React20.createElement(ReadCache, {
37699
37734
  cache: cacheRef.current
37700
37735
  });
37701
37736
  }
@@ -37721,17 +37756,17 @@ function reducer2(state, patch) {
37721
37756
  }
37722
37757
  function CherryPickCheck(props) {
37723
37758
  const actions = Store.useActions();
37724
- const [state, patch] = React20.useReducer(reducer2, {
37759
+ const [state, patch] = React21.useReducer(reducer2, {
37725
37760
  status: "init"
37726
37761
  });
37727
37762
  switch (state.status) {
37728
37763
  case "done":
37729
37764
  return props.children;
37730
37765
  case "prompt":
37731
- return /* @__PURE__ */ React20.createElement(YesNoPrompt, {
37732
- message: /* @__PURE__ */ React20.createElement(Text, {
37766
+ return /* @__PURE__ */ React21.createElement(YesNoPrompt, {
37767
+ message: /* @__PURE__ */ React21.createElement(Text, {
37733
37768
  color: colors.yellow
37734
- }, /* @__PURE__ */ React20.createElement(Command, null, "git cherry-pick"), " detected, would you like to abort it?"),
37769
+ }, /* @__PURE__ */ React21.createElement(Command, null, "git cherry-pick"), " detected, would you like to abort it?"),
37735
37770
  onYes: async () => {
37736
37771
  await cli(`git cherry-pick --abort`);
37737
37772
  patch({ status: "done" });
@@ -37741,11 +37776,11 @@ function CherryPickCheck(props) {
37741
37776
  }
37742
37777
  });
37743
37778
  default:
37744
- return /* @__PURE__ */ React20.createElement(Await, {
37779
+ return /* @__PURE__ */ React21.createElement(Await, {
37745
37780
  function: run,
37746
- fallback: /* @__PURE__ */ React20.createElement(Text, {
37781
+ fallback: /* @__PURE__ */ React21.createElement(Text, {
37747
37782
  color: colors.yellow
37748
- }, "Checking for ", /* @__PURE__ */ React20.createElement(Command, null, "git cherry-pick"), "…")
37783
+ }, "Checking for ", /* @__PURE__ */ React21.createElement(Command, null, "git cherry-pick"), "…")
37749
37784
  });
37750
37785
  }
37751
37786
  async function run() {
@@ -37771,7 +37806,7 @@ function CherryPickCheck(props) {
37771
37806
  }
37772
37807
 
37773
37808
  // src/app/Debug.tsx
37774
- var React21 = __toESM(require_react(), 1);
37809
+ var React22 = __toESM(require_react(), 1);
37775
37810
  import fs4 from "node:fs/promises";
37776
37811
  import path2 from "node:path";
37777
37812
 
@@ -37824,14 +37859,14 @@ function Debug() {
37824
37859
  const state = Store.useState((state2) => state2);
37825
37860
  const argv = Store.useState((state2) => state2.argv);
37826
37861
  const debug = Store.useState((state2) => state2.select.debug(state2));
37827
- React21.useEffect(function debugMessageOnce() {
37862
+ React22.useEffect(function debugMessageOnce() {
37828
37863
  if (debug) {
37829
- actions.output(/* @__PURE__ */ React21.createElement(Text, {
37864
+ actions.output(/* @__PURE__ */ React22.createElement(Text, {
37830
37865
  color: colors.yellow
37831
37866
  }, "Debug mode enabled"));
37832
37867
  }
37833
37868
  }, [argv]);
37834
- React21.useEffect(function sync_state_json() {
37869
+ React22.useEffect(function sync_state_json() {
37835
37870
  if (!argv?.["write-state-json"]) {
37836
37871
  return;
37837
37872
  }
@@ -37850,16 +37885,6 @@ function Debug() {
37850
37885
  // src/app/DependencyCheck.tsx
37851
37886
  var React23 = __toESM(require_react(), 1);
37852
37887
 
37853
- // src/app/Url.tsx
37854
- var React22 = __toESM(require_react(), 1);
37855
- function Url(props) {
37856
- return /* @__PURE__ */ React22.createElement(Text, {
37857
- bold: true,
37858
- color: colors.blue,
37859
- ...props
37860
- }, props.children);
37861
- }
37862
-
37863
37888
  // src/core/is_command_available.ts
37864
37889
  import fs5 from "node:fs";
37865
37890
  import path3 from "node:path";
@@ -45537,7 +45562,7 @@ async function command2(argv, options = {}) {
45537
45562
  if (options.env_config) {
45538
45563
  builder = builder.config(options.env_config);
45539
45564
  }
45540
- const parsed = await builder.scriptName("git stack").usage("Usage: git stack [command] [options]").command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions)).command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit)).command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false)).command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs).command(["update", "upgrade"], "Check and install the latest version of git stack", (yargs) => yargs).command("config", "Generate a one-time configuration json based on the passed arguments", (yargs) => yargs.options(DefaultOptions)).option("verbose", GlobalOptions.verbose).wrap(123).strict().version("2.7.0").showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`").help("help", "Show usage via `git stack help`");
45565
+ const parsed = await builder.scriptName("git stack").usage("Usage: git stack [command] [options]").command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions)).command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit)).command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false)).command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs).command(["update", "upgrade"], "Check and install the latest version of git stack", (yargs) => yargs).command("config", "Generate a one-time configuration json based on the passed arguments", (yargs) => yargs.options(DefaultOptions)).option("verbose", GlobalOptions.verbose).wrap(123).strict().version("2.7.1").showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`").help("help", "Show usage via `git stack help`");
45541
45566
  const result = parsed.argv;
45542
45567
  return result;
45543
45568
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -5,6 +5,7 @@ import * as Ink from "ink-cjs";
5
5
  import { Brackets } from "~/app/Brackets";
6
6
  import { Command } from "~/app/Command";
7
7
  import { FormatText } from "~/app/FormatText";
8
+ import { Url } from "~/app/Url";
8
9
  import { YesNoPrompt } from "~/app/YesNoPrompt";
9
10
  import { assertNever } from "~/core/assertNever";
10
11
  import { cli } from "~/core/cli";
@@ -26,10 +27,9 @@ type Props = {
26
27
  };
27
28
 
28
29
  type State = {
29
- error: null | Error;
30
+ status: "init" | "prompt" | "install" | "done";
30
31
  local_version: null | string;
31
32
  latest_version: null | string;
32
- status: "init" | "prompt" | "install" | "done";
33
33
  is_brew_bun_standalone: boolean;
34
34
  };
35
35
 
@@ -44,24 +44,94 @@ export function AutoUpdate(props: Props) {
44
44
  const [output, set_output] = React.useState<Array<React.ReactNode>>([]);
45
45
 
46
46
  const [state, patch] = React.useReducer(reducer, {
47
- error: null,
47
+ status: "init",
48
48
  local_version: null,
49
49
  latest_version: null,
50
- status: "init",
51
50
  is_brew_bun_standalone: false,
51
+
52
+ // // debugging
53
+ // status: "prompt",
54
+ // local_version: "2.5.3",
55
+ // latest_version: "2.7.0",
56
+ // is_brew_bun_standalone: true,
52
57
  });
53
58
 
54
- function handle_output(node: React.ReactNode) {
55
- if (typeof props.onOutput === "function") {
56
- props.onOutput(node);
57
- } else {
58
- set_output((current) => {
59
- return [...current, node];
60
- });
59
+ React.useEffect(handle_init_state, []);
60
+ React.useEffect(handle_status, [state.latest_version]);
61
+ React.useEffect(handle_on_done, [state.status]);
62
+
63
+ const status = render_status();
64
+
65
+ return (
66
+ <React.Fragment>
67
+ {output}
68
+ {status}
69
+ </React.Fragment>
70
+ );
71
+
72
+ function render_status() {
73
+ switch (state.status) {
74
+ case "init":
75
+ return null;
76
+
77
+ case "install":
78
+ return null;
79
+
80
+ case "done":
81
+ return props.children;
82
+
83
+ case "prompt": {
84
+ let install_command = "";
85
+ if (state.is_brew_bun_standalone) {
86
+ install_command = "brew install magus/git-stack/git-stack";
87
+ } else {
88
+ install_command = `npm install -g ${props.name}@latest`;
89
+ }
90
+
91
+ return (
92
+ <YesNoPrompt
93
+ message={
94
+ <Ink.Box flexDirection="column" gap={1}>
95
+ <Command>{install_command}</Command>
96
+ <FormatText
97
+ wrapper={<Ink.Text color={colors.yellow} />}
98
+ message="Would you like to run the above command to update?"
99
+ />
100
+ </Ink.Box>
101
+ }
102
+ onNo={() => {
103
+ patch({ status: "done" });
104
+ }}
105
+ onYes={async () => {
106
+ info(<Command>{install_command}</Command>);
107
+
108
+ patch({ status: "install" });
109
+
110
+ await cli(install_command, {
111
+ env: {
112
+ ...process.env,
113
+ HOMEBREW_COLOR: "1",
114
+ },
115
+ onOutput: (data: string) => {
116
+ info(<Ink.Text>{data}</Ink.Text>);
117
+ },
118
+ });
119
+
120
+ info(
121
+ <Ink.Text key="done">
122
+ ✅ Installed <Brackets>{state.latest_version}</Brackets>
123
+ </Ink.Text>,
124
+ );
125
+
126
+ patch({ status: "done" });
127
+ }}
128
+ />
129
+ );
130
+ }
61
131
  }
62
132
  }
63
133
 
64
- React.useEffect(() => {
134
+ function handle_on_done() {
65
135
  switch (state.status) {
66
136
  case "init":
67
137
  case "prompt":
@@ -76,199 +146,165 @@ export function AutoUpdate(props: Props) {
76
146
  default:
77
147
  assertNever(state.status);
78
148
  }
79
- }, [state.status]);
149
+ }
80
150
 
81
- React.useEffect(() => {
82
- let status: State["status"] = "init";
83
- let latest_version: string | null = null;
84
- let is_brew_bun_standalone = false;
151
+ function handle_init_state() {
152
+ init_state().catch(abort);
85
153
 
86
- const local_version = process.env.CLI_VERSION;
87
- const is_output = props_ref.current.verbose || props_ref.current.force;
154
+ async function init_state() {
155
+ if (state.latest_version !== null) return;
88
156
 
89
- async function auto_update() {
90
- if (!local_version) {
91
- throw new Error("Auto update requires process.env.CLI_VERSION to be set");
92
- }
157
+ const local_version = process.env.CLI_VERSION;
158
+ const latest_version = await get_latest_version();
159
+ const is_brew_bun_standalone = get_is_brew_bun_standalone();
160
+ patch({ local_version, latest_version, is_brew_bun_standalone });
161
+ }
93
162
 
163
+ async function get_latest_version() {
94
164
  const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
95
165
 
96
166
  const npm_json = await Promise.race([
97
167
  fetch_json(`https://registry.npmjs.org/${props.name}`),
98
168
 
99
169
  sleep(timeout_ms).then(() => {
100
- throw new Error("AutoUpdate timeout");
170
+ abort(new Error("AutoUpdate timeout"));
101
171
  }),
102
172
  ]);
103
173
 
104
- latest_version = npm_json?.["dist-tags"]?.latest;
174
+ const maybe_version = npm_json?.["dist-tags"]?.latest;
105
175
 
106
- if (!latest_version) {
107
- throw new Error("Unable to retrieve latest version from npm");
176
+ if (typeof maybe_version === "string") {
177
+ return maybe_version;
108
178
  }
109
179
 
180
+ throw new Error("Unable to retrieve latest version from npm");
181
+ }
182
+
183
+ function get_is_brew_bun_standalone() {
110
184
  const binary_path = process.argv[1];
185
+ debug(<Ink.Text dimColor>{JSON.stringify({ binary_path })}</Ink.Text>);
186
+
187
+ const is_bunfs_path = binary_path.startsWith("/$bunfs");
188
+ debug(
189
+ <Ink.Text dimColor>
190
+ {is_bunfs_path
191
+ ? "brew install detected (compiled bun standalone)"
192
+ : "npm install detected"}
193
+ </Ink.Text>,
194
+ );
195
+
196
+ return is_bunfs_path;
197
+ }
198
+ }
199
+
200
+ function handle_status() {
201
+ const latest_version = state.latest_version;
202
+
203
+ if (latest_version === null) {
204
+ return;
205
+ }
206
+
207
+ const local_version = state.local_version;
208
+
209
+ if (!local_version) {
210
+ throw new Error("Auto update requires process.env.CLI_VERSION to be set");
211
+ }
212
+
213
+ debug(
214
+ <FormatText
215
+ key="versions"
216
+ wrapper={<Ink.Text dimColor />}
217
+ message="Auto update found latest version {latest_version} and current local version {local_version}"
218
+ values={{
219
+ latest_version: <Brackets>{latest_version}</Brackets>,
220
+ local_version: <Brackets>{local_version}</Brackets>,
221
+ }}
222
+ />,
223
+ );
224
+
225
+ const semver_result = semver_compare(latest_version, local_version);
226
+ debug(<Ink.Text dimColor>{JSON.stringify({ semver_result })}</Ink.Text>);
227
+
228
+ switch (semver_result) {
229
+ case 0: {
230
+ info(
231
+ <Ink.Text>
232
+ ✅ Everything up to date. <Brackets>{latest_version}</Brackets>
233
+ </Ink.Text>,
234
+ );
111
235
 
112
- if (props_ref.current.verbose) {
113
- handle_output(<Ink.Text dimColor>{JSON.stringify({ binary_path })}</Ink.Text>);
236
+ return patch({ status: "done" });
114
237
  }
115
238
 
116
- is_brew_bun_standalone = binary_path.startsWith("/$bunfs");
239
+ case 1: {
240
+ const old_tag = local_version;
241
+ const new_tag = state.latest_version;
242
+ const url = `https://github.com/magus/git-stack-cli/compare/${old_tag}...${new_tag}`;
117
243
 
118
- if (props_ref.current.verbose) {
119
- if (is_brew_bun_standalone) {
120
- handle_output(
121
- <Ink.Text dimColor>brew install detected (compiled bun standalone)</Ink.Text>,
122
- );
123
- } else {
124
- handle_output(<Ink.Text dimColor>npm install detected</Ink.Text>);
125
- }
244
+ info(
245
+ <Ink.Box flexDirection="column" gap={1} paddingTop={1} paddingBottom={1}>
246
+ <Ink.Text>
247
+ 🆕 New version available! <Brackets>{latest_version}</Brackets>
248
+ </Ink.Text>
249
+ <Ink.Box flexDirection="column">
250
+ <Ink.Text dimColor>Changelog</Ink.Text>
251
+ <Url>{url}</Url>
252
+ </Ink.Box>
253
+ </Ink.Box>,
254
+ );
255
+
256
+ return patch({ status: "prompt" });
126
257
  }
127
258
 
128
- if (props_ref.current.verbose) {
129
- handle_output(
259
+ case -1: {
260
+ info(
130
261
  <FormatText
131
- key="versions"
132
- wrapper={<Ink.Text />}
133
- message="Auto update found latest version {latest_version} and current local version {local_version}"
262
+ message="⚠️ Local version {local_version} is newer than latest version {latest_version}"
134
263
  values={{
135
- latest_version: <Brackets>{latest_version}</Brackets>,
136
264
  local_version: <Brackets>{local_version}</Brackets>,
265
+ latest_version: <Brackets>{latest_version}</Brackets>,
137
266
  }}
138
267
  />,
139
268
  );
140
- }
141
269
 
142
- const semver_result = semver_compare(latest_version, local_version);
143
- if (props_ref.current.verbose) {
144
- handle_output(<Ink.Text dimColor>{JSON.stringify({ semver_result })}</Ink.Text>);
270
+ return patch({ status: "done" });
145
271
  }
146
272
 
147
- if (semver_result === 0) {
148
- status = "done";
149
-
150
- if (is_output) {
151
- handle_output(
152
- <Ink.Text>
153
- ✅ Everything up to date. <Brackets>{latest_version}</Brackets>
154
- </Ink.Text>,
155
- );
156
- }
157
- return;
158
- }
159
-
160
- if (semver_result === 1) {
161
- // trigger yes no prompt
162
- status = "prompt";
273
+ default: {
274
+ assertNever(semver_result);
275
+ abort(new Error("AutoUpdate failed"));
163
276
  }
277
+ }
278
+ }
164
279
 
165
- throw new Error("AutoUpdate failed");
280
+ function info(node: React.ReactNode) {
281
+ if (props_ref.current.verbose || props_ref.current.force) {
282
+ handle_output(node);
166
283
  }
284
+ }
167
285
 
168
- const onError = props_ref.current.onError || (() => {});
169
-
170
- auto_update()
171
- .then(() => {
172
- patch({ status, local_version, latest_version, is_brew_bun_standalone });
173
- })
174
- .catch((error) => {
175
- if (props_ref.current.verbose) {
176
- handle_output(
177
- <Ink.Text key="error" color={colors.red}>
178
- {error?.message}
179
- </Ink.Text>,
180
- );
181
- }
286
+ function debug(node: React.ReactNode) {
287
+ if (props_ref.current.verbose) {
288
+ handle_output(node);
289
+ }
290
+ }
291
+ function abort(error: Error) {
292
+ info(
293
+ <Ink.Text key="error" color={colors.red}>
294
+ {error.message}
295
+ </Ink.Text>,
296
+ );
297
+ patch({ status: "done" });
298
+ props_ref.current.onError?.(error);
299
+ }
182
300
 
183
- // ensure we always exit
184
- status = "done";
185
- patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
186
- onError(error);
301
+ function handle_output(node: React.ReactNode) {
302
+ if (typeof props.onOutput === "function") {
303
+ props.onOutput(node);
304
+ } else {
305
+ set_output((current) => {
306
+ return [...current, node];
187
307
  });
188
- }, []);
189
-
190
- const status = (function render_status() {
191
- switch (state.status) {
192
- case "init":
193
- return null;
194
-
195
- case "prompt": {
196
- let install_command = "";
197
- if (state.is_brew_bun_standalone) {
198
- install_command = "brew install magus/git-stack/git-stack";
199
- } else {
200
- install_command = `npm install -g ${props.name}@latest`;
201
- }
202
-
203
- return (
204
- <YesNoPrompt
205
- message={
206
- <Ink.Box flexDirection="column">
207
- <Ink.Box flexDirection="column">
208
- <Ink.Text color={colors.yellow}>
209
- <FormatText
210
- wrapper={<Ink.Text />}
211
- message="New version available {latest_version}"
212
- values={{
213
- latest_version: <Brackets>{state.latest_version}</Brackets>,
214
- }}
215
- />
216
- ,
217
- </Ink.Text>
218
- <Ink.Text> </Ink.Text>
219
- <Command>{install_command}</Command>
220
- <Ink.Text> </Ink.Text>
221
- </Ink.Box>
222
- <Ink.Box>
223
- <FormatText
224
- wrapper={<Ink.Text color={colors.yellow} />}
225
- message="Would you like to run the above command to update?"
226
- />
227
- </Ink.Box>
228
- </Ink.Box>
229
- }
230
- onYes={async () => {
231
- handle_output(<Command>{install_command}</Command>);
232
-
233
- patch({ status: "install" });
234
-
235
- await cli(install_command, {
236
- env: {
237
- ...process.env,
238
- HOMEBREW_COLOR: "1",
239
- },
240
- onOutput: (data: string) => {
241
- handle_output(<Ink.Text>{data}</Ink.Text>);
242
- },
243
- });
244
-
245
- handle_output(
246
- <Ink.Text key="done">
247
- ✅ Installed <Brackets>{state.latest_version}</Brackets>
248
- </Ink.Text>,
249
- );
250
-
251
- patch({ status: "done" });
252
- }}
253
- onNo={() => {
254
- patch({ status: "done" });
255
- }}
256
- />
257
- );
258
- }
259
-
260
- case "install":
261
- return null;
262
-
263
- case "done":
264
- return props.children;
265
308
  }
266
- })();
267
-
268
- return (
269
- <React.Fragment>
270
- {output}
271
- {status}
272
- </React.Fragment>
273
- );
309
+ }
274
310
  }