shortcutxl 0.3.57 → 0.3.59

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-06-03T02:10:49.171Z",
3
+ "generatedAt": "2026-06-03T05:23:21.387Z",
4
4
  "package": "shortcutxl",
5
5
  "binaryExtensions": [
6
6
  ".dll",
@@ -11,7 +11,7 @@
11
11
  "files": [
12
12
  {
13
13
  "path": "xll/ShortcutXL.xll",
14
- "sha256": "91d5e9bc42975aa414b3a4ac7f18ea1ba3d4bcbc94d4bee3d4d7dfcf33fbbaae",
14
+ "sha256": "3852e0b282c00d510c51da0501cac4971f335d4c99dba6272f15b335ad5068fd",
15
15
  "source": "ShortcutXL native XLL build",
16
16
  "version": "package",
17
17
  "builtBy": "shortcut",
@@ -523,7 +523,7 @@
523
523
  },
524
524
  {
525
525
  "path": "xll/python/Scripts/httpx.exe",
526
- "sha256": "5ece6fa700d232d712b90b2e70541b1f51fcdd3e9d3dc91e7f1c44f37d30a5c3",
526
+ "sha256": "84a59a562fcdd84adaa995aba93673f883ad1b3dc06708fcb268ea3275f839cd",
527
527
  "source": "httpx console launcher installed into embedded Python",
528
528
  "version": "see packaged httpx distribution",
529
529
  "builtBy": "third-party",
@@ -531,7 +531,7 @@
531
531
  },
532
532
  {
533
533
  "path": "xll/python/Scripts/idna.exe",
534
- "sha256": "abd6d50042c4b240cf42a5a7d6081a0cb79d15358c18641ba856b60322b2b74c",
534
+ "sha256": "a5bd06e740a8c4af1bd8d7883efc7ad16b0b2151b2a9e59ef6d64282c164a56b",
535
535
  "source": "Python package console launcher installed into embedded Python",
536
536
  "version": "see owning Python package metadata in site-packages",
537
537
  "builtBy": "third-party",
@@ -539,7 +539,7 @@
539
539
  },
540
540
  {
541
541
  "path": "xll/python/Scripts/pip.exe",
542
- "sha256": "efa224c01b807029586cfbce3c40d7fb543d905b52cf765d898e9d3db1123f28",
542
+ "sha256": "2e4bed34ea1c99aee03259cf79f1fd9a56a9900cafd7ecb60c818c45e1125533",
543
543
  "source": "pip console launcher installed into embedded Python",
544
544
  "version": "see packaged pip distribution",
545
545
  "builtBy": "third-party",
@@ -547,7 +547,7 @@
547
547
  },
548
548
  {
549
549
  "path": "xll/python/Scripts/pip3.13.exe",
550
- "sha256": "efa224c01b807029586cfbce3c40d7fb543d905b52cf765d898e9d3db1123f28",
550
+ "sha256": "2e4bed34ea1c99aee03259cf79f1fd9a56a9900cafd7ecb60c818c45e1125533",
551
551
  "source": "pip console launcher installed into embedded Python",
552
552
  "version": "see packaged pip distribution",
553
553
  "builtBy": "third-party",
@@ -555,7 +555,7 @@
555
555
  },
556
556
  {
557
557
  "path": "xll/python/Scripts/pip3.exe",
558
- "sha256": "efa224c01b807029586cfbce3c40d7fb543d905b52cf765d898e9d3db1123f28",
558
+ "sha256": "2e4bed34ea1c99aee03259cf79f1fd9a56a9900cafd7ecb60c818c45e1125533",
559
559
  "source": "pip console launcher installed into embedded Python",
560
560
  "version": "see packaged pip distribution",
561
561
  "builtBy": "third-party",
@@ -563,7 +563,7 @@
563
563
  },
564
564
  {
565
565
  "path": "xll/python/Scripts/pywin32_postinstall.exe",
566
- "sha256": "84f0a317e3016c29569be3f101db2d3557914d84c4cca693a707866d12b45fdb",
566
+ "sha256": "7c02775810d083f1e70a026c2ea80ea086373bdaac811a9a4f1c19dad9e54e6f",
567
567
  "source": "Python package console launcher installed into embedded Python",
568
568
  "version": "see owning Python package metadata in site-packages",
569
569
  "builtBy": "third-party",
@@ -571,7 +571,7 @@
571
571
  },
572
572
  {
573
573
  "path": "xll/python/Scripts/pywin32_testall.exe",
574
- "sha256": "3b6e27fe46b31904a8c760d3c70518d5eeb0d95b585e02daf93682fbe2c290f1",
574
+ "sha256": "ba14591c7c28b8e58a291040d70b0acff797598645abfc4f9eb4e2f3b6e0b9c7",
575
575
  "source": "Python package console launcher installed into embedded Python",
576
576
  "version": "see owning Python package metadata in site-packages",
577
577
  "builtBy": "third-party",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.59]
4
+
5
+ - **Security and model updates** - Strengthened account policy checks and updated model routing/defaults.
6
+
3
7
  ## [0.3.57]
4
8
 
5
9
  - **Improved Windows reliability** - Improved ShortcutXL install, upgrade, and uninstall behavior on Windows.
package/dist/cli.js CHANGED
@@ -26528,7 +26528,7 @@ var require_snapshot_recorder = __commonJS({
26528
26528
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
26529
26529
  "use strict";
26530
26530
  var { writeFile: writeFile10, readFile: readFile11, mkdir: mkdir11 } = __require("node:fs/promises");
26531
- var { dirname: dirname37, resolve: resolve22 } = __require("node:path");
26531
+ var { dirname: dirname38, resolve: resolve22 } = __require("node:path");
26532
26532
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
26533
26533
  var { InvalidArgumentError, UndiciError } = require_errors();
26534
26534
  var { hashId, isUrlExcludedFactory, normalizeHeaders: normalizeHeaders2, createHeaderFilters } = require_snapshot_utils();
@@ -26759,7 +26759,7 @@ var require_snapshot_recorder = __commonJS({
26759
26759
  throw new InvalidArgumentError("Snapshot path is required");
26760
26760
  }
26761
26761
  const resolvedPath = resolve22(path21);
26762
- await mkdir11(dirname37(resolvedPath), { recursive: true });
26762
+ await mkdir11(dirname38(resolvedPath), { recursive: true });
26763
26763
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
26764
26764
  hash: hash2,
26765
26765
  snapshot
@@ -167005,9 +167005,9 @@ var require_buffer_from = __commonJS({
167005
167005
  }
167006
167006
  });
167007
167007
 
167008
- // ../../node_modules/.pnpm/source-map-support@0.5.21/node_modules/source-map-support/source-map-support.js
167008
+ // ../../node_modules/.pnpm/source-map-support@0.5.13/node_modules/source-map-support/source-map-support.js
167009
167009
  var require_source_map_support = __commonJS({
167010
- "../../node_modules/.pnpm/source-map-support@0.5.21/node_modules/source-map-support/source-map-support.js"(exports, module) {
167010
+ "../../node_modules/.pnpm/source-map-support@0.5.13/node_modules/source-map-support/source-map-support.js"(exports) {
167011
167011
  var SourceMapConsumer = require_source_map().SourceMapConsumer;
167012
167012
  var path21 = __require("path");
167013
167013
  var fs16;
@@ -167019,9 +167019,6 @@ var require_source_map_support = __commonJS({
167019
167019
  } catch (err) {
167020
167020
  }
167021
167021
  var bufferFrom = require_buffer_from();
167022
- function dynamicRequire(mod, request) {
167023
- return mod.require(request);
167024
- }
167025
167022
  var errorFormatterInstalled = false;
167026
167023
  var uncaughtShimInstalled = false;
167027
167024
  var emptyCacheBetweenOperations = false;
@@ -167041,23 +167038,6 @@ var require_source_map_support = __commonJS({
167041
167038
  function hasGlobalProcessEventEmitter() {
167042
167039
  return typeof process === "object" && process !== null && typeof process.on === "function";
167043
167040
  }
167044
- function globalProcessVersion() {
167045
- if (typeof process === "object" && process !== null) {
167046
- return process.version;
167047
- } else {
167048
- return "";
167049
- }
167050
- }
167051
- function globalProcessStderr() {
167052
- if (typeof process === "object" && process !== null) {
167053
- return process.stderr;
167054
- }
167055
- }
167056
- function globalProcessExit(code) {
167057
- if (typeof process === "object" && process !== null && typeof process.exit === "function") {
167058
- return process.exit(code);
167059
- }
167060
- }
167061
167041
  function handlerExec(list2) {
167062
167042
  return function(arg) {
167063
167043
  for (var i2 = 0; i2 < list2.length; i2++) {
@@ -167282,20 +167262,15 @@ var require_source_map_support = __commonJS({
167282
167262
  object3.toString = CallSiteToString;
167283
167263
  return object3;
167284
167264
  }
167285
- function wrapCallSite(frame, state) {
167286
- if (state === void 0) {
167287
- state = { nextPosition: null, curPosition: null };
167288
- }
167265
+ function wrapCallSite(frame) {
167289
167266
  if (frame.isNative()) {
167290
- state.curPosition = null;
167291
167267
  return frame;
167292
167268
  }
167293
167269
  var source = frame.getFileName() || frame.getScriptNameOrSourceURL();
167294
167270
  if (source) {
167295
167271
  var line = frame.getLineNumber();
167296
167272
  var column = frame.getColumnNumber() - 1;
167297
- var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;
167298
- var headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62;
167273
+ var headerLength = 62;
167299
167274
  if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) {
167300
167275
  column -= headerLength;
167301
167276
  }
@@ -167304,14 +167279,10 @@ var require_source_map_support = __commonJS({
167304
167279
  line,
167305
167280
  column
167306
167281
  });
167307
- state.curPosition = position;
167308
167282
  frame = cloneCallSite(frame);
167309
167283
  var originalFunctionName = frame.getFunctionName;
167310
167284
  frame.getFunctionName = function() {
167311
- if (state.nextPosition == null) {
167312
- return originalFunctionName();
167313
- }
167314
- return state.nextPosition.name || originalFunctionName();
167285
+ return position.name || originalFunctionName();
167315
167286
  };
167316
167287
  frame.getFileName = function() {
167317
167288
  return position.source;
@@ -167346,14 +167317,9 @@ var require_source_map_support = __commonJS({
167346
167317
  var name = error2.name || "Error";
167347
167318
  var message = error2.message || "";
167348
167319
  var errorString = name + ": " + message;
167349
- var state = { nextPosition: null, curPosition: null };
167350
- var processedStack = [];
167351
- for (var i2 = stack.length - 1; i2 >= 0; i2--) {
167352
- processedStack.push("\n at " + wrapCallSite(stack[i2], state));
167353
- state.nextPosition = state.curPosition;
167354
- }
167355
- state.curPosition = state.nextPosition = null;
167356
- return errorString + processedStack.reverse().join("");
167320
+ return errorString + stack.map(function(frame) {
167321
+ return "\n at " + wrapCallSite(frame);
167322
+ }).join("");
167357
167323
  }
167358
167324
  function getErrorSource(error2) {
167359
167325
  var match2 = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error2.stack);
@@ -167380,16 +167346,15 @@ var require_source_map_support = __commonJS({
167380
167346
  }
167381
167347
  function printErrorAndExit(error2) {
167382
167348
  var source = getErrorSource(error2);
167383
- var stderr = globalProcessStderr();
167384
- if (stderr && stderr._handle && stderr._handle.setBlocking) {
167385
- stderr._handle.setBlocking(true);
167349
+ if (process.stderr._handle && process.stderr._handle.setBlocking) {
167350
+ process.stderr._handle.setBlocking(true);
167386
167351
  }
167387
167352
  if (source) {
167388
167353
  console.error();
167389
167354
  console.error(source);
167390
167355
  }
167391
167356
  console.error(error2.stack);
167392
- globalProcessExit(1);
167357
+ process.exit(1);
167393
167358
  }
167394
167359
  function shimEmitUncaughtException() {
167395
167360
  var origEmit = process.emit;
@@ -167431,7 +167396,11 @@ var require_source_map_support = __commonJS({
167431
167396
  retrieveMapHandlers.unshift(options2.retrieveSourceMap);
167432
167397
  }
167433
167398
  if (options2.hookRequire && !isInBrowser()) {
167434
- var Module2 = dynamicRequire(module, "module");
167399
+ var Module2;
167400
+ try {
167401
+ Module2 = __require("module");
167402
+ } catch (err) {
167403
+ }
167435
167404
  var $compile = Module2.prototype._compile;
167436
167405
  if (!$compile.__sourceMapSupport) {
167437
167406
  Module2.prototype._compile = function(content, filename) {
@@ -167451,13 +167420,6 @@ var require_source_map_support = __commonJS({
167451
167420
  }
167452
167421
  if (!uncaughtShimInstalled) {
167453
167422
  var installHandler = "handleUncaughtExceptions" in options2 ? options2.handleUncaughtExceptions : true;
167454
- try {
167455
- var worker_threads = dynamicRequire(module, "worker_threads");
167456
- if (worker_threads.isMainThread === false) {
167457
- installHandler = false;
167458
- }
167459
- } catch (e2) {
167460
- }
167461
167423
  if (installHandler && hasGlobalProcessEventEmitter()) {
167462
167424
  uncaughtShimInstalled = true;
167463
167425
  shimEmitUncaughtException();
@@ -474819,13 +474781,46 @@ var init_shell = __esm({
474819
474781
  }
474820
474782
  });
474821
474783
 
474784
+ // src/startup/python-abi.ts
474785
+ import { existsSync as existsSync42, readdirSync as readdirSync12 } from "fs";
474786
+ function listVersionedPythonRuntimeFiles(pythonDir) {
474787
+ if (!existsSync42(pythonDir)) return /* @__PURE__ */ new Set();
474788
+ try {
474789
+ return new Set(
474790
+ readdirSync12(pythonDir).filter((entry) => VERSIONED_PYTHON_RUNTIME_FILE.test(entry)).map((entry) => entry.toLowerCase())
474791
+ );
474792
+ } catch {
474793
+ return /* @__PURE__ */ new Set();
474794
+ }
474795
+ }
474796
+ function hasVersionedPythonRuntimeFileDrift(sourcePythonDir, destPythonDir) {
474797
+ const sourceFiles = listVersionedPythonRuntimeFiles(sourcePythonDir);
474798
+ if (sourceFiles.size === 0) return false;
474799
+ const destFiles = listVersionedPythonRuntimeFiles(destPythonDir);
474800
+ if (destFiles.size === 0) return false;
474801
+ for (const file2 of destFiles) {
474802
+ if (!sourceFiles.has(file2)) return true;
474803
+ }
474804
+ for (const file2 of sourceFiles) {
474805
+ if (!destFiles.has(file2)) return true;
474806
+ }
474807
+ return false;
474808
+ }
474809
+ var VERSIONED_PYTHON_RUNTIME_FILE;
474810
+ var init_python_abi = __esm({
474811
+ "src/startup/python-abi.ts"() {
474812
+ "use strict";
474813
+ VERSIONED_PYTHON_RUNTIME_FILE = /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i;
474814
+ }
474815
+ });
474816
+
474822
474817
  // src/startup/sync-xll.ts
474823
474818
  import { createHash as createHash3 } from "crypto";
474824
474819
  import {
474825
474820
  copyFileSync,
474826
- existsSync as existsSync42,
474821
+ existsSync as existsSync43,
474827
474822
  mkdirSync as mkdirSync20,
474828
- readdirSync as readdirSync12,
474823
+ readdirSync as readdirSync13,
474829
474824
  readFileSync as readFileSync33,
474830
474825
  rmSync as rmSync4,
474831
474826
  statSync as statSync11,
@@ -474841,7 +474836,7 @@ function stripMotw(filePath) {
474841
474836
  }
474842
474837
  }
474843
474838
  function filesMatch(src, dest) {
474844
- if (!existsSync42(dest)) return false;
474839
+ if (!existsSync43(dest)) return false;
474845
474840
  const srcStat = statSync11(src);
474846
474841
  const destStat = statSync11(dest);
474847
474842
  return srcStat.size === destStat.size && srcStat.mtimeMs === destStat.mtimeMs;
@@ -474851,11 +474846,11 @@ function filesContentMatch(src, dest) {
474851
474846
  return hash2(src) === hash2(dest);
474852
474847
  }
474853
474848
  function syncDir(src, dest) {
474854
- if (!existsSync42(src)) return { updated: 0, staleLocked: false };
474849
+ if (!existsSync43(src)) return { updated: 0, staleLocked: false };
474855
474850
  mkdirSync20(dest, { recursive: true });
474856
474851
  let updated = 0;
474857
474852
  let staleLocked = false;
474858
- for (const entry of readdirSync12(src)) {
474853
+ for (const entry of readdirSync13(src)) {
474859
474854
  const srcPath = join58(src, entry);
474860
474855
  const destPath = join58(dest, entry);
474861
474856
  const stat8 = statSync11(srcPath);
@@ -474886,28 +474881,8 @@ function syncDir(src, dest) {
474886
474881
  }
474887
474882
  return { updated, staleLocked };
474888
474883
  }
474889
- function listVersionedPythonRuntimeFiles(pythonDir) {
474890
- if (!existsSync42(pythonDir)) return /* @__PURE__ */ new Set();
474891
- try {
474892
- return new Set(
474893
- readdirSync12(pythonDir).filter((entry) => /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i.test(entry)).map((entry) => entry.toLowerCase())
474894
- );
474895
- } catch {
474896
- return /* @__PURE__ */ new Set();
474897
- }
474898
- }
474899
474884
  function hasPythonAbiDrift(shippedDir, stableDir) {
474900
- const shippedRuntimeFiles = listVersionedPythonRuntimeFiles(join58(shippedDir, "python"));
474901
- if (shippedRuntimeFiles.size === 0) return false;
474902
- const stableRuntimeFiles = listVersionedPythonRuntimeFiles(join58(stableDir, "python"));
474903
- if (stableRuntimeFiles.size === 0) return false;
474904
- for (const file2 of stableRuntimeFiles) {
474905
- if (!shippedRuntimeFiles.has(file2)) return true;
474906
- }
474907
- for (const file2 of shippedRuntimeFiles) {
474908
- if (!stableRuntimeFiles.has(file2)) return true;
474909
- }
474910
- return false;
474885
+ return hasVersionedPythonRuntimeFileDrift(join58(shippedDir, "python"), join58(stableDir, "python"));
474911
474886
  }
474912
474887
  function resetStablePythonRuntime(stableDir) {
474913
474888
  try {
@@ -474937,11 +474912,11 @@ function syncXll() {
474937
474912
  mkdirSync20(stableDir, { recursive: true });
474938
474913
  const pythonRuntimeNeedsReset = hasPythonAbiDrift(shippedDir, stableDir);
474939
474914
  try {
474940
- if (existsSync42(versionFile)) {
474915
+ if (existsSync43(versionFile)) {
474941
474916
  const stamped = readFileSync33(versionFile, "utf-8").trim();
474942
474917
  if (stamped === VERSION) {
474943
- const xllReady2 = existsSync42(stableXll);
474944
- const shippedXllReady = existsSync42(shippedXll);
474918
+ const xllReady2 = existsSync43(stableXll);
474919
+ const shippedXllReady = existsSync43(shippedXll);
474945
474920
  if (!pythonRuntimeNeedsReset && (!shippedXllReady || xllReady2 && filesContentMatch(shippedXll, stableXll))) {
474946
474921
  return { updated: 0, stableDir, xllReady: xllReady2, skipped: true, staleLocked: false };
474947
474922
  }
@@ -474956,11 +474931,11 @@ function syncXll() {
474956
474931
  hasStaleLockedFiles = true;
474957
474932
  syncFailed = true;
474958
474933
  }
474959
- if (existsSync42(shippedDir)) {
474934
+ if (existsSync43(shippedDir)) {
474960
474935
  try {
474961
474936
  const { updated, staleLocked } = syncDir(shippedDir, stableDir);
474962
474937
  totalUpdated += updated;
474963
- hasStaleLockedFiles = staleLocked;
474938
+ hasStaleLockedFiles = hasStaleLockedFiles || staleLocked;
474964
474939
  if (staleLocked) {
474965
474940
  console.warn(
474966
474941
  "Warning: A ShortcutXL update is available but Excel has files locked. Please close Excel and restart the Shortcut agent to apply it."
@@ -474977,7 +474952,7 @@ function syncXll() {
474977
474952
  } catch {
474978
474953
  }
474979
474954
  }
474980
- const xllReady = existsSync42(stableXll);
474955
+ const xllReady = existsSync43(stableXll);
474981
474956
  return {
474982
474957
  updated: totalUpdated,
474983
474958
  stableDir,
@@ -474991,13 +474966,14 @@ var init_sync_xll = __esm({
474991
474966
  "src/startup/sync-xll.ts"() {
474992
474967
  "use strict";
474993
474968
  init_config();
474969
+ init_python_abi();
474994
474970
  SYNC_VERSION_FILE = ".sync-version";
474995
474971
  PIP_VERSION_FILE = ".pip-version";
474996
474972
  }
474997
474973
  });
474998
474974
 
474999
474975
  // src/startup/startup-xll.ts
475000
- import { existsSync as existsSync43 } from "fs";
474976
+ import { existsSync as existsSync44 } from "fs";
475001
474977
  import { join as join59 } from "path";
475002
474978
  function getSourceCheckoutRequiredPaths() {
475003
474979
  return [
@@ -475011,7 +474987,7 @@ function resolveStartupXll({
475011
474987
  needsSetup
475012
474988
  }) {
475013
474989
  if (sourceCheckout) {
475014
- const missingPaths = getSourceCheckoutRequiredPaths().filter((p2) => !existsSync43(p2));
474990
+ const missingPaths = getSourceCheckoutRequiredPaths().filter((p2) => !existsSync44(p2));
475015
474991
  return missingPaths.length === 0 ? { kind: "ok", updated: 0 } : { kind: "source-missing", missingPaths };
475016
474992
  }
475017
474993
  const result = syncXll();
@@ -475713,7 +475689,7 @@ var init_mog_command = __esm({
475713
475689
  });
475714
475690
 
475715
475691
  // src/app/permissions/permissions-command.ts
475716
- import { existsSync as existsSync44, statSync as statSync12 } from "fs";
475692
+ import { existsSync as existsSync45, statSync as statSync12 } from "fs";
475717
475693
  import { join as join60 } from "path";
475718
475694
  function getGlobalSettingsPath() {
475719
475695
  return join60(getAgentDir(), "settings.json");
@@ -475724,7 +475700,7 @@ function normalizeWorkspaceRoot(input, cwd) {
475724
475700
  return "Workspace path is required";
475725
475701
  }
475726
475702
  const resolved = resolveToCwd(trimmed, cwd);
475727
- if (!existsSync44(resolved)) {
475703
+ if (!existsSync45(resolved)) {
475728
475704
  return `Path does not exist: ${resolved}`;
475729
475705
  }
475730
475706
  try {
@@ -475746,7 +475722,7 @@ function normalizeFilesystemGrantPath(input, cwd, scope) {
475746
475722
  return "Grant path is required";
475747
475723
  }
475748
475724
  const resolved = resolveToCwd(trimmed, cwd);
475749
- if (!existsSync44(resolved)) {
475725
+ if (!existsSync45(resolved)) {
475750
475726
  return `Path does not exist: ${resolved}`;
475751
475727
  }
475752
475728
  try {
@@ -476527,9 +476503,9 @@ var init_remote_skill_files = __esm({
476527
476503
  // src/app/sync/skills-download.ts
476528
476504
  import { createHash as createHash4 } from "crypto";
476529
476505
  import {
476530
- existsSync as existsSync45,
476506
+ existsSync as existsSync46,
476531
476507
  mkdirSync as mkdirSync21,
476532
- readdirSync as readdirSync13,
476508
+ readdirSync as readdirSync14,
476533
476509
  readFileSync as readFileSync34,
476534
476510
  rmSync as rmSync5,
476535
476511
  statSync as statSync13,
@@ -476543,9 +476519,9 @@ function getSkillsDir() {
476543
476519
  return join61(getAgentDir(), "skills");
476544
476520
  }
476545
476521
  function collectLocalFiles(dir, rootDir = dir) {
476546
- if (!existsSync45(dir)) return [];
476522
+ if (!existsSync46(dir)) return [];
476547
476523
  const files = [];
476548
- for (const entry of readdirSync13(dir, { withFileTypes: true })) {
476524
+ for (const entry of readdirSync14(dir, { withFileTypes: true })) {
476549
476525
  const fullPath = join61(dir, entry.name);
476550
476526
  if (entry.isDirectory()) {
476551
476527
  files.push(...collectLocalFiles(fullPath, rootDir));
@@ -476847,7 +476823,7 @@ var init_skills_download = __esm({
476847
476823
 
476848
476824
  // src/app/sync/skills-status.ts
476849
476825
  import { createHash as createHash5 } from "crypto";
476850
- import { existsSync as existsSync46, readdirSync as readdirSync14, readFileSync as readFileSync35 } from "fs";
476826
+ import { existsSync as existsSync47, readdirSync as readdirSync15, readFileSync as readFileSync35 } from "fs";
476851
476827
  import { join as join62, relative as relative10 } from "path";
476852
476828
  function sha2562(data) {
476853
476829
  return createHash5("sha256").update(data).digest("hex");
@@ -476856,13 +476832,13 @@ function getSkillsDir2() {
476856
476832
  return join62(getAgentDir(), "skills");
476857
476833
  }
476858
476834
  function listLocalSkillNames(skillsDir) {
476859
- if (!existsSync46(skillsDir)) return [];
476860
- return readdirSync14(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
476835
+ if (!existsSync47(skillsDir)) return [];
476836
+ return readdirSync15(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
476861
476837
  }
476862
476838
  function collectLocalFiles2(dir, rootDir = dir) {
476863
- if (!existsSync46(dir)) return [];
476839
+ if (!existsSync47(dir)) return [];
476864
476840
  const files = [];
476865
- for (const entry of readdirSync14(dir, { withFileTypes: true })) {
476841
+ for (const entry of readdirSync15(dir, { withFileTypes: true })) {
476866
476842
  const fullPath = join62(dir, entry.name);
476867
476843
  if (entry.isDirectory()) {
476868
476844
  files.push(...collectLocalFiles2(fullPath, rootDir));
@@ -477104,7 +477080,7 @@ var init_skills_status = __esm({
477104
477080
 
477105
477081
  // src/app/sync/skills-upload.ts
477106
477082
  import { createHash as createHash6 } from "crypto";
477107
- import { existsSync as existsSync47, readdirSync as readdirSync15, readFileSync as readFileSync36, statSync as statSync14 } from "fs";
477083
+ import { existsSync as existsSync48, readdirSync as readdirSync16, readFileSync as readFileSync36, statSync as statSync14 } from "fs";
477108
477084
  import { join as join63, relative as relative11 } from "path";
477109
477085
  function sha2563(data) {
477110
477086
  return createHash6("sha256").update(data).digest("hex");
@@ -477113,8 +477089,8 @@ function getSkillsDir3() {
477113
477089
  return join63(getAgentDir(), "skills");
477114
477090
  }
477115
477091
  function listLocalSkillNames2(skillsDir) {
477116
- if (!existsSync47(skillsDir)) return [];
477117
- return readdirSync15(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
477092
+ if (!existsSync48(skillsDir)) return [];
477093
+ return readdirSync16(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
477118
477094
  }
477119
477095
  function validateSkillName2(name, parentDirName) {
477120
477096
  const errors = [];
@@ -477137,7 +477113,7 @@ function validateSkillName2(name, parentDirName) {
477137
477113
  }
477138
477114
  function validateLocalSkill(skillName, skillDir) {
477139
477115
  const skillPath = join63(skillDir, "SKILL.md");
477140
- if (!existsSync47(skillPath)) {
477116
+ if (!existsSync48(skillPath)) {
477141
477117
  return "missing SKILL.md";
477142
477118
  }
477143
477119
  try {
@@ -477158,7 +477134,7 @@ function validateLocalSkill(skillName, skillDir) {
477158
477134
  }
477159
477135
  function collectLocalFiles3(dir, rootDir = dir) {
477160
477136
  const files = [];
477161
- for (const entry of readdirSync15(dir, { withFileTypes: true })) {
477137
+ for (const entry of readdirSync16(dir, { withFileTypes: true })) {
477162
477138
  const fullPath = join63(dir, entry.name);
477163
477139
  if (entry.isDirectory()) {
477164
477140
  files.push(...collectLocalFiles3(fullPath, rootDir));
@@ -477409,7 +477385,7 @@ var init_skills_upload = __esm({
477409
477385
  });
477410
477386
 
477411
477387
  // src/startup/interactive-commands.ts
477412
- import { existsSync as existsSync48 } from "fs";
477388
+ import { existsSync as existsSync49 } from "fs";
477413
477389
  async function fetchAuthorizedSkillTeamIds(accessToken) {
477414
477390
  const userInfo = await fetchUserInfo(accessToken);
477415
477391
  if (!userInfo) return null;
@@ -477443,7 +477419,7 @@ function createInteractiveCommandHandler(context) {
477443
477419
  }
477444
477420
  if (command === "/docs") {
477445
477421
  const docsFile = getUserDocsPath();
477446
- if (existsSync48(docsFile)) {
477422
+ if (existsSync49(docsFile)) {
477447
477423
  void openInEditor(docsFile).catch(
477448
477424
  (error2) => showStatus(`Could not open documentation: ${formatOpenError(error2)}`)
477449
477425
  );
@@ -477781,12 +477757,12 @@ var init_install_utils = __esm({
477781
477757
  });
477782
477758
 
477783
477759
  // src/startup/preflight/shared.ts
477784
- import { chmodSync as chmodSync3, existsSync as existsSync49, mkdirSync as mkdirSync22, readFileSync as readFileSync37, unlinkSync as unlinkSync8, writeFileSync as writeFileSync20 } from "fs";
477760
+ import { chmodSync as chmodSync3, existsSync as existsSync50, mkdirSync as mkdirSync22, readFileSync as readFileSync37, unlinkSync as unlinkSync8, writeFileSync as writeFileSync20 } from "fs";
477785
477761
  import { dirname as dirname34, join as join64 } from "path";
477786
477762
  async function ensureUv(platformTarget) {
477787
477763
  header("Python Toolchain");
477788
477764
  const uvPath = getUvBinPath();
477789
- if (existsSync49(uvPath)) {
477765
+ if (existsSync50(uvPath)) {
477790
477766
  const check2 = run(`"${uvPath}" --version`, { timeout: 5e3 });
477791
477767
  if (check2.ok) {
477792
477768
  ok(`uv ${check2.stdout.replace("uv ", "").trim()} installed.`);
@@ -477842,7 +477818,7 @@ async function ensureUvPython() {
477842
477818
  const uvPath = getUvBinPath();
477843
477819
  const venvDir = getUvVenvDir();
477844
477820
  const venvPython = join64(venvDir, "bin", "python3");
477845
- if (existsSync49(venvPython)) {
477821
+ if (existsSync50(venvPython)) {
477846
477822
  const check2 = run(`"${venvPython}" --version`, { timeout: 5e3 });
477847
477823
  if (check2.ok) {
477848
477824
  ok(`${check2.stdout.trim()} ready.`);
@@ -477877,7 +477853,7 @@ async function ensureUvPackages() {
477877
477853
  const uvPath = getUvBinPath();
477878
477854
  const venvDir = getUvVenvDir();
477879
477855
  const venvPython = join64(venvDir, "bin", "python3");
477880
- if (!existsSync49(venvPython)) {
477856
+ if (!existsSync50(venvPython)) {
477881
477857
  log2(
477882
477858
  "Shortcut agent will install packages after Python is ready.",
477883
477859
  "Will finish setup automatically."
@@ -477887,7 +477863,7 @@ async function ensureUvPackages() {
477887
477863
  const stampFile = join64(venvDir, ".packages-stamp");
477888
477864
  const stampValue = `${VERSION}:${UV_PIP_PACKAGES.join(",")}`;
477889
477865
  try {
477890
- if (existsSync49(stampFile) && readFileSync37(stampFile, "utf-8").trim() === stampValue) {
477866
+ if (existsSync50(stampFile) && readFileSync37(stampFile, "utf-8").trim() === stampValue) {
477891
477867
  ok("Packages already installed.", "Ready.");
477892
477868
  return;
477893
477869
  }
@@ -477971,13 +477947,13 @@ __export(mac_exports, {
477971
477947
  ensureUvPython: () => ensureUvPython,
477972
477948
  smokeTestSheetEngine: () => smokeTestSheetEngine
477973
477949
  });
477974
- import { existsSync as existsSync50 } from "fs";
477950
+ import { existsSync as existsSync51 } from "fs";
477975
477951
  async function ensureUv2() {
477976
477952
  return ensureUv("apple-darwin");
477977
477953
  }
477978
477954
  function ensureChromeMac() {
477979
477955
  header("Chrome");
477980
- if (existsSync50("/Applications/Google Chrome.app")) {
477956
+ if (existsSync51("/Applications/Google Chrome.app")) {
477981
477957
  ok("Chrome found \u2014 SEC filing support ready.");
477982
477958
  } else {
477983
477959
  log2("Chrome not installed \u2014 Shortcut agent can set this up for SEC filing support.");
@@ -478001,13 +477977,13 @@ __export(linux_exports, {
478001
477977
  ensureUvPython: () => ensureUvPython,
478002
477978
  smokeTestSheetEngine: () => smokeTestSheetEngine
478003
477979
  });
478004
- import { existsSync as existsSync51 } from "fs";
477980
+ import { existsSync as existsSync52 } from "fs";
478005
477981
  async function ensureUv3() {
478006
477982
  return ensureUv("unknown-linux-gnu");
478007
477983
  }
478008
477984
  function ensureChromeLinux() {
478009
477985
  header("Chrome");
478010
- const found = LINUX_CHROME_PATHS.find((p2) => existsSync51(p2));
477986
+ const found = LINUX_CHROME_PATHS.find((p2) => existsSync52(p2));
478011
477987
  if (found) {
478012
477988
  ok("Chrome found \u2014 SEC filing support ready.");
478013
477989
  } else {
@@ -478039,12 +478015,12 @@ __export(windows_exports, {
478039
478015
  ensureXllRegistry: () => ensureXllRegistry,
478040
478016
  smokeTestExcel: () => smokeTestExcel
478041
478017
  });
478042
- import { existsSync as existsSync52, readFileSync as readFileSync38, writeFileSync as writeFileSync21 } from "fs";
478018
+ import { existsSync as existsSync53, readFileSync as readFileSync38, writeFileSync as writeFileSync21 } from "fs";
478043
478019
  import { join as join65, resolve as resolve18 } from "path";
478044
478020
  async function ensurePipPackages() {
478045
478021
  header("Python Packages", "Calculation Engine");
478046
478022
  const pythonExe = getEmbeddedPythonExe();
478047
- if (!existsSync52(pythonExe)) {
478023
+ if (!existsSync53(pythonExe)) {
478048
478024
  log2("Shortcut agent will set up Python.", "Will finish setup automatically.");
478049
478025
  return;
478050
478026
  }
@@ -478063,7 +478039,7 @@ async function ensurePipPackages() {
478063
478039
  const pipStampFile = join65(getStableXllDir(), ".pip-version");
478064
478040
  let pipUpToDate = false;
478065
478041
  try {
478066
- pipUpToDate = existsSync52(pipStampFile) && readFileSync38(pipStampFile, "utf-8").trim() === VERSION;
478042
+ pipUpToDate = existsSync53(pipStampFile) && readFileSync38(pipStampFile, "utf-8").trim() === VERSION;
478067
478043
  } catch {
478068
478044
  }
478069
478045
  if (pipUpToDate) {
@@ -478093,7 +478069,7 @@ async function ensureChromeWindows() {
478093
478069
  return;
478094
478070
  }
478095
478071
  const pythonExe = getEmbeddedPythonExe();
478096
- if (!existsSync52(pythonExe)) {
478072
+ if (!existsSync53(pythonExe)) {
478097
478073
  log2("Shortcut agent will set up Chrome.", "Will finish setup automatically.");
478098
478074
  return;
478099
478075
  }
@@ -478177,7 +478153,7 @@ async function ensureGitBash() {
478177
478153
  function ensureXllRegistry() {
478178
478154
  header("Excel Add-in", "Excel Integration");
478179
478155
  const xllPath = resolve18(getXllPath()).replace(/\//g, "\\");
478180
- if (!existsSync52(xllPath)) {
478156
+ if (!existsSync53(xllPath)) {
478181
478157
  log2("Shortcut agent will set up the Excel add-in.", "Will finish setup automatically.");
478182
478158
  return;
478183
478159
  }
@@ -478373,7 +478349,7 @@ var diagnostics_exports = {};
478373
478349
  __export(diagnostics_exports, {
478374
478350
  collectPreflightDiagnostics: () => collectPreflightDiagnostics
478375
478351
  });
478376
- import { closeSync as closeSync2, existsSync as existsSync53, openSync as openSync2, readSync as readSync2, statSync as statSync15 } from "fs";
478352
+ import { closeSync as closeSync2, existsSync as existsSync54, openSync as openSync2, readSync as readSync2, statSync as statSync15 } from "fs";
478377
478353
  import { resolve as resolve19 } from "path";
478378
478354
  function safeRun(command, timeout = DIAGNOSTIC_TIMEOUT_MS) {
478379
478355
  try {
@@ -478498,7 +478474,7 @@ function collectXllLog() {
478498
478474
  `${process.env.TEMP ?? ""}\\shortcutxl.log`
478499
478475
  ];
478500
478476
  for (const path21 of logPaths) {
478501
- if (path21 && existsSync53(path21)) {
478477
+ if (path21 && existsSync54(path21)) {
478502
478478
  return readLogTail(path21);
478503
478479
  }
478504
478480
  }
@@ -478550,7 +478526,7 @@ function collectPreflightDiagnostics() {
478550
478526
  setCurrentStep("diagnostics");
478551
478527
  const xllPath = resolve19(getXllPath()).replace(/\//g, "\\");
478552
478528
  const diagnostics = {
478553
- xllExists: existsSync53(xllPath),
478529
+ xllExists: existsSync54(xllPath),
478554
478530
  xllPath,
478555
478531
  motw: collectMotw(xllPath),
478556
478532
  xllArch: collectXllArch(xllPath),
@@ -478634,7 +478610,7 @@ var init_preflight = __esm({
478634
478610
  });
478635
478611
 
478636
478612
  // src/startup/prune-sessions.ts
478637
- import { readdirSync as readdirSync16, rmSync as rmSync6, statSync as statSync16 } from "node:fs";
478613
+ import { readdirSync as readdirSync17, rmSync as rmSync6, statSync as statSync16 } from "node:fs";
478638
478614
  import { join as join66 } from "node:path";
478639
478615
  function pruneStaleFiles() {
478640
478616
  const cutoff = Date.now() - FOURTEEN_DAYS_MS;
@@ -478643,7 +478619,7 @@ function pruneStaleFiles() {
478643
478619
  function pruneSessionFiles(cutoff) {
478644
478620
  try {
478645
478621
  const sessionsRoot = getSessionsDir();
478646
- for (const projectDir of readdirSync16(sessionsRoot)) {
478622
+ for (const projectDir of readdirSync17(sessionsRoot)) {
478647
478623
  pruneJsonlFilesRecursively(join66(sessionsRoot, projectDir), cutoff);
478648
478624
  }
478649
478625
  } catch {
@@ -478651,7 +478627,7 @@ function pruneSessionFiles(cutoff) {
478651
478627
  }
478652
478628
  function pruneJsonlFilesRecursively(dir, cutoff) {
478653
478629
  try {
478654
- for (const entry of readdirSync16(dir, { withFileTypes: true })) {
478630
+ for (const entry of readdirSync17(dir, { withFileTypes: true })) {
478655
478631
  const entryPath = join66(dir, entry.name);
478656
478632
  if (entry.isDirectory()) {
478657
478633
  pruneJsonlFilesRecursively(entryPath, cutoff);
@@ -478751,19 +478727,38 @@ var init_session_selection = __esm({
478751
478727
 
478752
478728
  // src/startup/update-action.ts
478753
478729
  import { spawn as spawn12 } from "child_process";
478730
+ import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync23 } from "fs";
478731
+ import { dirname as dirname35 } from "path";
478754
478732
  async function runForegroundUpdate(request, deps = {}) {
478755
478733
  const resolveShell = deps.resolveShell ?? getShellConfig;
478756
478734
  const resolveEnv = deps.resolveEnv ?? getShellEnv;
478757
478735
  const spawnProcess = deps.spawnProcess ?? spawn12;
478736
+ const resolveLogPath = deps.resolveLogPath ?? getDebugLogPath;
478758
478737
  const shellConfig = resolveShell();
478759
478738
  return await new Promise((resolve22, reject) => {
478739
+ const output = [];
478740
+ let outputBytes = 0;
478741
+ let outputTruncated = false;
478760
478742
  const child = spawnProcess(shellConfig.shell, [...shellConfig.args, request.command], {
478761
478743
  cwd: request.cwd ?? process.cwd(),
478762
478744
  env: resolveEnv(),
478763
- stdio: "inherit"
478745
+ stdio: ["ignore", "pipe", "pipe"]
478764
478746
  });
478747
+ const capture = (chunk) => {
478748
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
478749
+ const remaining = MAX_CAPTURED_OUTPUT_BYTES - outputBytes;
478750
+ if (remaining > 0) {
478751
+ output.push(buffer.subarray(0, remaining));
478752
+ outputBytes += Math.min(buffer.length, remaining);
478753
+ }
478754
+ if (buffer.length > remaining) {
478755
+ outputTruncated = true;
478756
+ }
478757
+ };
478758
+ child.stdout?.on("data", capture);
478759
+ child.stderr?.on("data", capture);
478765
478760
  child.once("error", reject);
478766
- child.once("exit", (exitCode, signal) => {
478761
+ child.once("close", (exitCode, signal) => {
478767
478762
  if (exitCode === 0) {
478768
478763
  resolve22({
478769
478764
  kind: "success",
@@ -478772,24 +478767,83 @@ async function runForegroundUpdate(request, deps = {}) {
478772
478767
  });
478773
478768
  return;
478774
478769
  }
478770
+ const capturedOutput = Buffer.concat(output).toString("utf-8");
478771
+ const logPath = writeUpdateFailureLog({
478772
+ command: request.command,
478773
+ exitCode,
478774
+ signal,
478775
+ output: capturedOutput,
478776
+ truncated: outputTruncated,
478777
+ resolveLogPath
478778
+ });
478775
478779
  resolve22({
478776
478780
  kind: "failed",
478777
478781
  exitCode,
478778
- signal
478782
+ signal,
478783
+ message: summarizeUpdateFailure(capturedOutput, request.command),
478784
+ logPath
478779
478785
  });
478780
478786
  });
478781
478787
  });
478782
478788
  }
478789
+ function summarizeUpdateFailure(output, updateCommand) {
478790
+ if (/EEXIST[\s\S]*shortcut\.cmd/i.test(output) || /File exists:[\s\S]*shortcut\.cmd/i.test(output)) {
478791
+ return [
478792
+ "ShortcutXL could not update because an existing global `shortcut` command blocked npm from replacing it.",
478793
+ `Close any running ShortcutXL terminals, remove the existing global ShortcutXL package, then retry \`${updateCommand}\`.`
478794
+ ].join(" ");
478795
+ }
478796
+ if (/EPERM|ENOTEMPTY|ENOENT/i.test(output) && /AppData[\\/]+Roaming[\\/]+npm[\\/]+node_modules[\\/]+shortcutxl/i.test(output)) {
478797
+ return [
478798
+ "ShortcutXL could not update because npm could not clean up the previous global install.",
478799
+ `Close ShortcutXL terminals and retry \`${updateCommand}\`.`
478800
+ ].join(" ");
478801
+ }
478802
+ return `ShortcutXL could not update automatically. Retry with \`${updateCommand}\`.`;
478803
+ }
478804
+ function writeUpdateFailureLog({
478805
+ command,
478806
+ exitCode,
478807
+ signal,
478808
+ output,
478809
+ truncated,
478810
+ resolveLogPath
478811
+ }) {
478812
+ try {
478813
+ const logPath = resolveLogPath();
478814
+ mkdirSync23(dirname35(logPath), { recursive: true });
478815
+ appendFileSync6(
478816
+ logPath,
478817
+ [
478818
+ "",
478819
+ "--- ShortcutXL update failure ---",
478820
+ `time=${(/* @__PURE__ */ new Date()).toISOString()}`,
478821
+ `command=${command}`,
478822
+ `exitCode=${String(exitCode)}`,
478823
+ `signal=${String(signal)}`,
478824
+ truncated ? `outputTruncatedAfterBytes=${MAX_CAPTURED_OUTPUT_BYTES}` : void 0,
478825
+ output
478826
+ ].filter((line) => line !== void 0).join("\n"),
478827
+ "utf-8"
478828
+ );
478829
+ return logPath;
478830
+ } catch {
478831
+ return void 0;
478832
+ }
478833
+ }
478834
+ var MAX_CAPTURED_OUTPUT_BYTES;
478783
478835
  var init_update_action = __esm({
478784
478836
  "src/startup/update-action.ts"() {
478785
478837
  "use strict";
478786
478838
  init_bash_runtime();
478839
+ init_config();
478840
+ MAX_CAPTURED_OUTPUT_BYTES = 1024 * 1024;
478787
478841
  }
478788
478842
  });
478789
478843
 
478790
478844
  // src/startup/update-manager.ts
478791
- import { mkdirSync as mkdirSync23, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "fs";
478792
- import { dirname as dirname35 } from "path";
478845
+ import { mkdirSync as mkdirSync24, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "fs";
478846
+ import { dirname as dirname36 } from "path";
478793
478847
  async function resolveStartupUpdateStatus(options2 = {}, deps = {}) {
478794
478848
  const cachePath = options2.cachePath ?? getUpdateCachePath();
478795
478849
  const currentVersion = options2.currentVersion ?? VERSION;
@@ -478902,7 +478956,7 @@ function readUpdateCache(cachePath) {
478902
478956
  }
478903
478957
  }
478904
478958
  function writeUpdateCache(cachePath, cache) {
478905
- mkdirSync23(dirname35(cachePath), { recursive: true });
478959
+ mkdirSync24(dirname36(cachePath), { recursive: true });
478906
478960
  writeFileSync22(cachePath, `${JSON.stringify(cache, null, 2)}
478907
478961
  `, "utf-8");
478908
478962
  }
@@ -480350,7 +480404,7 @@ __export(uninstall_exports, {
480350
480404
  removeOwnedInstallArtifacts: () => removeOwnedInstallArtifacts,
480351
480405
  runUninstall: () => runUninstall
480352
480406
  });
480353
- import { existsSync as existsSync54, readdirSync as readdirSync17, rmSync as rmSync7, rmdirSync } from "fs";
480407
+ import { existsSync as existsSync55, readdirSync as readdirSync18, rmSync as rmSync7, rmdirSync } from "fs";
480354
480408
  import { isAbsolute as isAbsolute10, join as join67, relative as relative12, resolve as resolve20 } from "path";
480355
480409
  function isWithinDir(parentDir, targetPath) {
480356
480410
  const parent = resolve20(parentDir);
@@ -480360,7 +480414,7 @@ function isWithinDir(parentDir, targetPath) {
480360
480414
  }
480361
480415
  function removeIfInsideAgentDir(agentDir, relativePath) {
480362
480416
  const target = join67(agentDir, relativePath);
480363
- if (!isWithinDir(agentDir, target) || !existsSync54(target)) {
480417
+ if (!isWithinDir(agentDir, target) || !existsSync55(target)) {
480364
480418
  return false;
480365
480419
  }
480366
480420
  rmSync7(target, { recursive: true, force: true });
@@ -480368,10 +480422,10 @@ function removeIfInsideAgentDir(agentDir, relativePath) {
480368
480422
  }
480369
480423
  function removeEmptyDirIfInsideAgentDir(agentDir, relativePath) {
480370
480424
  const target = join67(agentDir, relativePath);
480371
- if (!isWithinDir(agentDir, target) || !existsSync54(target)) {
480425
+ if (!isWithinDir(agentDir, target) || !existsSync55(target)) {
480372
480426
  return false;
480373
480427
  }
480374
- if (readdirSync17(target).length > 0) {
480428
+ if (readdirSync18(target).length > 0) {
480375
480429
  return false;
480376
480430
  }
480377
480431
  rmdirSync(target);
@@ -480398,7 +480452,7 @@ function removeOwnedInstallArtifacts(agentDir) {
480398
480452
  }
480399
480453
  }
480400
480454
  try {
480401
- if (existsSync54(agentDir) && readdirSync17(agentDir).length === 0) {
480455
+ if (existsSync55(agentDir) && readdirSync18(agentDir).length === 0) {
480402
480456
  rmdirSync(agentDir);
480403
480457
  result.removed.push(".");
480404
480458
  }
@@ -480474,7 +480528,7 @@ function runUninstall() {
480474
480528
  }
480475
480529
  header2("Playwright Browsers");
480476
480530
  const pythonExe = getEmbeddedPythonExe();
480477
- if (existsSync54(pythonExe)) {
480531
+ if (existsSync55(pythonExe)) {
480478
480532
  const pwResult = run(`"${pythonExe}" -m playwright uninstall --all`, { timeout: 3e4 });
480479
480533
  if (pwResult.ok) {
480480
480534
  ok2("Removed Playwright browsers.");
@@ -480490,7 +480544,7 @@ function runUninstall() {
480490
480544
  }
480491
480545
  header2("Files");
480492
480546
  const agentDir = getAgentDir();
480493
- if (existsSync54(agentDir)) {
480547
+ if (existsSync55(agentDir)) {
480494
480548
  const cleanup = removeOwnedInstallArtifacts(agentDir);
480495
480549
  if (cleanup.removed.length > 0) {
480496
480550
  ok2(`Removed generated install files from ${agentDir}`);
@@ -480543,7 +480597,7 @@ __export(main_exports, {
480543
480597
  isMogRuntimeEnabledForUser: () => isMogRuntimeEnabledForUser,
480544
480598
  main: () => main
480545
480599
  });
480546
- import { existsSync as existsSync55 } from "fs";
480600
+ import { existsSync as existsSync56 } from "fs";
480547
480601
  async function resolveShortcutUserInfo(authStorage) {
480548
480602
  const accessToken = await authStorage.getApiKey(SHORTCUT_PROVIDER_ID);
480549
480603
  return accessToken ? fetchUserInfo(accessToken) : null;
@@ -480968,7 +481022,7 @@ async function main(args) {
480968
481022
  if (sandboxMode === "enabled" && sandboxAvailable) {
480969
481023
  const sandboxFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
480970
481024
  let sandboxFrame = 0;
480971
- const needsDownload = !existsSync55(getAlpineRootfsPath());
481025
+ const needsDownload = !existsSync56(getAlpineRootfsPath());
480972
481026
  const spinnerMsg = needsDownload ? "Downloading sandbox (~80 MB)..." : "Checking sandbox...";
480973
481027
  const sandboxSpinner = setInterval(() => {
480974
481028
  process.stderr.write(
@@ -481109,10 +481163,18 @@ async function main(args) {
481109
481163
  if (updateResult.kind === "failed") {
481110
481164
  const exitDetail = updateResult.exitCode !== null ? `exit code ${updateResult.exitCode}` : updateResult.signal ? `signal ${updateResult.signal}` : "unknown error";
481111
481165
  console.error(source_default.red(`Update failed (${exitDetail}). ShortcutXL will not launch.`));
481166
+ if (updateResult.message) {
481167
+ console.error(source_default.yellow(updateResult.message));
481168
+ }
481169
+ if (updateResult.logPath) {
481170
+ console.error(source_default.dim(`Details were written to ${updateResult.logPath}`));
481171
+ }
481112
481172
  process.exit(updateResult.exitCode ?? 1);
481113
481173
  }
481114
481174
  console.log(source_default.green(`Update to v${updatePromptOutcome.update.latestVersion} succeeded.`));
481115
- console.log(source_default.green("Launching ShortcutXL..."));
481175
+ console.log(source_default.green("Restart ShortcutXL to finish applying the update:"));
481176
+ console.log(source_default.bold(" shortcut"));
481177
+ process.exit(0);
481116
481178
  }
481117
481179
  initTheme(settingsManager.getTheme(), isInteractive);
481118
481180
  sessionManager = await createSessionManager2(parsed, cwd);
@@ -481571,9 +481633,9 @@ ${loop.prompt}`;
481571
481633
 
481572
481634
  // src/cli.ts
481573
481635
  var import_dotenv = __toESM(require_main(), 1);
481574
- import { dirname as dirname36, resolve as resolve21 } from "path";
481636
+ import { dirname as dirname37, resolve as resolve21 } from "path";
481575
481637
  import { fileURLToPath as fileURLToPath4 } from "url";
481576
- var __dirname3 = dirname36(fileURLToPath4(import.meta.url));
481638
+ var __dirname3 = dirname37(fileURLToPath4(import.meta.url));
481577
481639
  var agentRoot = resolve21(__dirname3, "..");
481578
481640
  import_dotenv.default.config({ path: resolve21(agentRoot, ".env.development") });
481579
481641
  process.title = "shortcut";
package/dist/main.js CHANGED
@@ -669,10 +669,18 @@ export async function main(args) {
669
669
  ? `signal ${updateResult.signal}`
670
670
  : 'unknown error';
671
671
  console.error(chalk.red(`Update failed (${exitDetail}). ShortcutXL will not launch.`));
672
+ if (updateResult.message) {
673
+ console.error(chalk.yellow(updateResult.message));
674
+ }
675
+ if (updateResult.logPath) {
676
+ console.error(chalk.dim(`Details were written to ${updateResult.logPath}`));
677
+ }
672
678
  process.exit(updateResult.exitCode ?? 1);
673
679
  }
674
680
  console.log(chalk.green(`Update to v${updatePromptOutcome.update.latestVersion} succeeded.`));
675
- console.log(chalk.green('Launching ShortcutXL...'));
681
+ console.log(chalk.green('Restart ShortcutXL to finish applying the update:'));
682
+ console.log(chalk.bold(' shortcut'));
683
+ process.exit(0);
676
684
  }
677
685
  initTheme(settingsManager.getTheme(), isInteractive);
678
686
  // Create session manager based on CLI flags
@@ -0,0 +1,3 @@
1
+ export declare function listVersionedPythonRuntimeFiles(pythonDir: string): Set<string>;
2
+ export declare function hasVersionedPythonRuntimeFileDrift(sourcePythonDir: string, destPythonDir: string): boolean;
3
+ //# sourceMappingURL=python-abi.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { existsSync, readdirSync } from 'fs';
2
+ const VERSIONED_PYTHON_RUNTIME_FILE = /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i;
3
+ export function listVersionedPythonRuntimeFiles(pythonDir) {
4
+ if (!existsSync(pythonDir))
5
+ return new Set();
6
+ try {
7
+ return new Set(readdirSync(pythonDir)
8
+ .filter((entry) => VERSIONED_PYTHON_RUNTIME_FILE.test(entry))
9
+ .map((entry) => entry.toLowerCase()));
10
+ }
11
+ catch {
12
+ return new Set();
13
+ }
14
+ }
15
+ export function hasVersionedPythonRuntimeFileDrift(sourcePythonDir, destPythonDir) {
16
+ const sourceFiles = listVersionedPythonRuntimeFiles(sourcePythonDir);
17
+ if (sourceFiles.size === 0)
18
+ return false;
19
+ const destFiles = listVersionedPythonRuntimeFiles(destPythonDir);
20
+ if (destFiles.size === 0)
21
+ return false;
22
+ for (const file of destFiles) {
23
+ if (!sourceFiles.has(file))
24
+ return true;
25
+ }
26
+ for (const file of sourceFiles) {
27
+ if (!destFiles.has(file))
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ //# sourceMappingURL=python-abi.js.map
@@ -22,6 +22,7 @@ import { createHash } from 'crypto';
22
22
  import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, utimesSync, writeFileSync } from 'fs';
23
23
  import { join } from 'path';
24
24
  import { getPackageDir, getStableXllDir, VERSION } from '../config.js';
25
+ import { hasVersionedPythonRuntimeFileDrift } from './python-abi.js';
25
26
  /** Strip the NTFS Zone.Identifier alternate data stream (Mark of the Web). */
26
27
  function stripMotw(filePath) {
27
28
  try {
@@ -101,34 +102,8 @@ function syncDir(src, dest) {
101
102
  }
102
103
  const SYNC_VERSION_FILE = '.sync-version';
103
104
  const PIP_VERSION_FILE = '.pip-version';
104
- function listVersionedPythonRuntimeFiles(pythonDir) {
105
- if (!existsSync(pythonDir))
106
- return new Set();
107
- try {
108
- return new Set(readdirSync(pythonDir)
109
- .filter((entry) => /^python\d{2,}(?:\.dll|\.zip|\._pth)$/i.test(entry))
110
- .map((entry) => entry.toLowerCase()));
111
- }
112
- catch {
113
- return new Set();
114
- }
115
- }
116
105
  export function hasPythonAbiDrift(shippedDir, stableDir) {
117
- const shippedRuntimeFiles = listVersionedPythonRuntimeFiles(join(shippedDir, 'python'));
118
- if (shippedRuntimeFiles.size === 0)
119
- return false;
120
- const stableRuntimeFiles = listVersionedPythonRuntimeFiles(join(stableDir, 'python'));
121
- if (stableRuntimeFiles.size === 0)
122
- return false;
123
- for (const file of stableRuntimeFiles) {
124
- if (!shippedRuntimeFiles.has(file))
125
- return true;
126
- }
127
- for (const file of shippedRuntimeFiles) {
128
- if (!stableRuntimeFiles.has(file))
129
- return true;
130
- }
131
- return false;
106
+ return hasVersionedPythonRuntimeFileDrift(join(shippedDir, 'python'), join(stableDir, 'python'));
132
107
  }
133
108
  function resetStablePythonRuntime(stableDir) {
134
109
  try {
@@ -203,7 +178,7 @@ export function syncXll() {
203
178
  try {
204
179
  const { updated, staleLocked } = syncDir(shippedDir, stableDir);
205
180
  totalUpdated += updated;
206
- hasStaleLockedFiles = staleLocked;
181
+ hasStaleLockedFiles = hasStaleLockedFiles || staleLocked;
207
182
  if (staleLocked) {
208
183
  console.warn('Warning: A ShortcutXL update is available but Excel has files locked. ' +
209
184
  'Please close Excel and restart the Shortcut agent to apply it.');
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Foreground update execution path.
3
3
  *
4
- * Runs the updater command in the startup flow, surfaces real output to the
5
- * terminal, and returns a structured success/failure result so startup can
6
- * decide whether to continue into the shell.
4
+ * Runs the updater command in the startup flow. Updaters such as npm can emit
5
+ * huge cleanup dumps on Windows when a global install tree is locked or
6
+ * half-deleted, so stdout/stderr are captured and summarized instead of being
7
+ * streamed directly into the user's terminal.
7
8
  */
8
9
  import { spawn } from 'child_process';
9
10
  export interface UpdateLaunchRequest {
@@ -14,6 +15,8 @@ export interface ForegroundUpdateResult {
14
15
  kind: 'success' | 'failed';
15
16
  exitCode: number | null;
16
17
  signal: NodeJS.Signals | null;
18
+ message?: string;
19
+ logPath?: string;
17
20
  }
18
21
  interface UpdateActionDeps {
19
22
  spawnProcess?: typeof spawn;
@@ -22,6 +25,7 @@ interface UpdateActionDeps {
22
25
  args: string[];
23
26
  };
24
27
  resolveEnv?: () => NodeJS.ProcessEnv;
28
+ resolveLogPath?: () => string;
25
29
  }
26
30
  export declare function runForegroundUpdate(request: UpdateLaunchRequest, deps?: UpdateActionDeps): Promise<ForegroundUpdateResult>;
27
31
  export {};
@@ -1,25 +1,47 @@
1
1
  /**
2
2
  * Foreground update execution path.
3
3
  *
4
- * Runs the updater command in the startup flow, surfaces real output to the
5
- * terminal, and returns a structured success/failure result so startup can
6
- * decide whether to continue into the shell.
4
+ * Runs the updater command in the startup flow. Updaters such as npm can emit
5
+ * huge cleanup dumps on Windows when a global install tree is locked or
6
+ * half-deleted, so stdout/stderr are captured and summarized instead of being
7
+ * streamed directly into the user's terminal.
7
8
  */
8
9
  import { spawn } from 'child_process';
10
+ import { appendFileSync, mkdirSync } from 'fs';
11
+ import { dirname } from 'path';
9
12
  import { getShellConfig, getShellEnv } from '../app/tools/bash-runtime.js';
13
+ import { getDebugLogPath } from '../config.js';
14
+ const MAX_CAPTURED_OUTPUT_BYTES = 1024 * 1024;
10
15
  export async function runForegroundUpdate(request, deps = {}) {
11
16
  const resolveShell = deps.resolveShell ?? getShellConfig;
12
17
  const resolveEnv = deps.resolveEnv ?? getShellEnv;
13
18
  const spawnProcess = deps.spawnProcess ?? spawn;
19
+ const resolveLogPath = deps.resolveLogPath ?? getDebugLogPath;
14
20
  const shellConfig = resolveShell();
15
21
  return await new Promise((resolve, reject) => {
22
+ const output = [];
23
+ let outputBytes = 0;
24
+ let outputTruncated = false;
16
25
  const child = spawnProcess(shellConfig.shell, [...shellConfig.args, request.command], {
17
26
  cwd: request.cwd ?? process.cwd(),
18
27
  env: resolveEnv(),
19
- stdio: 'inherit'
28
+ stdio: ['ignore', 'pipe', 'pipe']
20
29
  });
30
+ const capture = (chunk) => {
31
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
32
+ const remaining = MAX_CAPTURED_OUTPUT_BYTES - outputBytes;
33
+ if (remaining > 0) {
34
+ output.push(buffer.subarray(0, remaining));
35
+ outputBytes += Math.min(buffer.length, remaining);
36
+ }
37
+ if (buffer.length > remaining) {
38
+ outputTruncated = true;
39
+ }
40
+ };
41
+ child.stdout?.on('data', capture);
42
+ child.stderr?.on('data', capture);
21
43
  child.once('error', reject);
22
- child.once('exit', (exitCode, signal) => {
44
+ child.once('close', (exitCode, signal) => {
23
45
  if (exitCode === 0) {
24
46
  resolve({
25
47
  kind: 'success',
@@ -28,12 +50,62 @@ export async function runForegroundUpdate(request, deps = {}) {
28
50
  });
29
51
  return;
30
52
  }
53
+ const capturedOutput = Buffer.concat(output).toString('utf-8');
54
+ const logPath = writeUpdateFailureLog({
55
+ command: request.command,
56
+ exitCode,
57
+ signal,
58
+ output: capturedOutput,
59
+ truncated: outputTruncated,
60
+ resolveLogPath
61
+ });
31
62
  resolve({
32
63
  kind: 'failed',
33
64
  exitCode,
34
- signal
65
+ signal,
66
+ message: summarizeUpdateFailure(capturedOutput, request.command),
67
+ logPath
35
68
  });
36
69
  });
37
70
  });
38
71
  }
72
+ function summarizeUpdateFailure(output, updateCommand) {
73
+ if (/EEXIST[\s\S]*shortcut\.cmd/i.test(output) ||
74
+ /File exists:[\s\S]*shortcut\.cmd/i.test(output)) {
75
+ return [
76
+ 'ShortcutXL could not update because an existing global `shortcut` command blocked npm from replacing it.',
77
+ `Close any running ShortcutXL terminals, remove the existing global ShortcutXL package, then retry \`${updateCommand}\`.`
78
+ ].join(' ');
79
+ }
80
+ if (/EPERM|ENOTEMPTY|ENOENT/i.test(output) &&
81
+ /AppData[\\/]+Roaming[\\/]+npm[\\/]+node_modules[\\/]+shortcutxl/i.test(output)) {
82
+ return [
83
+ 'ShortcutXL could not update because npm could not clean up the previous global install.',
84
+ `Close ShortcutXL terminals and retry \`${updateCommand}\`.`
85
+ ].join(' ');
86
+ }
87
+ return `ShortcutXL could not update automatically. Retry with \`${updateCommand}\`.`;
88
+ }
89
+ function writeUpdateFailureLog({ command, exitCode, signal, output, truncated, resolveLogPath }) {
90
+ try {
91
+ const logPath = resolveLogPath();
92
+ mkdirSync(dirname(logPath), { recursive: true });
93
+ appendFileSync(logPath, [
94
+ '',
95
+ '--- ShortcutXL update failure ---',
96
+ `time=${new Date().toISOString()}`,
97
+ `command=${command}`,
98
+ `exitCode=${String(exitCode)}`,
99
+ `signal=${String(signal)}`,
100
+ truncated ? `outputTruncatedAfterBytes=${MAX_CAPTURED_OUTPUT_BYTES}` : undefined,
101
+ output
102
+ ]
103
+ .filter((line) => line !== undefined)
104
+ .join('\n'), 'utf-8');
105
+ return logPath;
106
+ }
107
+ catch {
108
+ return undefined;
109
+ }
110
+ }
39
111
  //# sourceMappingURL=update-action.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shortcutxl",
3
- "version": "0.3.57",
3
+ "version": "0.3.59",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
Binary file
Binary file
@@ -1,4 +1,4 @@
1
- ../../Scripts/httpx.exe,sha256=Xs5vpwDSMtcSuQsucFQbH1H83T6dPckefxxE830wpcM,108375
1
+ ../../Scripts/httpx.exe,sha256=hKWaVi_N2EraqZWrqTZz-IOtGz3AZwj8smjqMnX4Oc0,108375
2
2
  httpx-0.28.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
3
  httpx-0.28.1.dist-info/METADATA,sha256=_rubD48-gNV8gZnDBPNcQzboWB0dGNeYPJJ2a4J5OyU,7052
4
4
  httpx-0.28.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
- ../../Scripts/idna.exe,sha256=q9bVAELEskDPQqWn1ggaDLedFTWMGGQbqFa2AyKyt0w,108378
1
+ ../../Scripts/idna.exe,sha256=pb0G50CoxK8b2NeIPvx60WsLIVGyqeWe9tZCgsFkpWs,108378
2
2
  idna-3.18.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
3
  idna-3.18.dist-info/METADATA,sha256=Rt_m5axGLQ9oDs2avPZugptqIzSCS02eOXmzETXK8oE,6119
4
4
  idna-3.18.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
- ../../Scripts/pip.exe,sha256=76IkwBuAcClYbPvOPEDX-1Q9kFtSz3ZdiY6dPbESPyg,108392
2
- ../../Scripts/pip3.13.exe,sha256=76IkwBuAcClYbPvOPEDX-1Q9kFtSz3ZdiY6dPbESPyg,108392
3
- ../../Scripts/pip3.exe,sha256=76IkwBuAcClYbPvOPEDX-1Q9kFtSz3ZdiY6dPbESPyg,108392
1
+ ../../Scripts/pip.exe,sha256=LkvtNOocma7gMlnPefH9mlapkAyv1-y2DIGMReESVTM,108392
2
+ ../../Scripts/pip3.13.exe,sha256=LkvtNOocma7gMlnPefH9mlapkAyv1-y2DIGMReESVTM,108392
3
+ ../../Scripts/pip3.exe,sha256=LkvtNOocma7gMlnPefH9mlapkAyv1-y2DIGMReESVTM,108392
4
4
  pip-26.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
5
5
  pip-26.1.2.dist-info/METADATA,sha256=F4Mt5Htdwj5GJTuhZGJSADbyjZon1cMBV3hJUspOabI,4566
6
6
  pip-26.1.2.dist-info/RECORD,,
@@ -1,8 +1,8 @@
1
1
  ../../Scripts/__pycache__/pywin32_postinstall.cpython-313.pyc,,
2
2
  ../../Scripts/__pycache__/pywin32_testall.cpython-313.pyc,,
3
- ../../Scripts/pywin32_postinstall.exe,sha256=hPCjF-MBbClWm-PxAdstNVeRTYTEzKaTpweGbRK0X9s,108403
3
+ ../../Scripts/pywin32_postinstall.exe,sha256=fAJ3WBDQg_HnCgJsLqgOoIY3O9qsgRqaTxwZ2tnlTm8,108403
4
4
  ../../Scripts/pywin32_postinstall.py,sha256=mx4WVp1hD_8xgkSXttNtto1BVDECZOc3FCClrR1SjFM,25736
5
- ../../Scripts/pywin32_testall.exe,sha256=O24n_kazGQSox2DTxwUY1e6w2VtYXgLa-TaC--LCkPE,108399
5
+ ../../Scripts/pywin32_testall.exe,sha256=uhRZHHwouOWKKRBA1wsKz_eXWYZFq_xPnrTi87bgucc,108399
6
6
  ../../Scripts/pywin32_testall.py,sha256=hyGLMKILn3_W0q-McP1VJnNFW_z5yk_gesTMQDlUocM,3847
7
7
  PyWin32.chm,sha256=S3fN4iKEUFyW4IIm3Zf5jtdrlTJLh93MzIrT9wX3z9o,2646518
8
8
  __pycache__/pythoncom.cpython-313.pyc,,
Binary file
Binary file
Binary file
Binary file
Binary file