@indigoai-us/hq-cli 5.345.33 → 5.345.35

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.
@@ -626,8 +626,10 @@ export function buildPrefixedInstallArgv(prefix) {
626
626
  export function buildPnpmInstallArgv(install) {
627
627
  const args = ["add", "-g", LATEST_PACKAGE_SPEC];
628
628
  const dirs = install ? pnpmInstallDirs(install) : null;
629
- if (dirs)
630
- args.push("--store-dir", dirs.storeDir, "--virtual-store-dir", dirs.virtualStoreDir);
629
+ if (dirs) {
630
+ args.push("--global-dir", dirs.globalDir, "--store-dir", dirs.storeDir);
631
+ args.push("--virtual-store-dir", dirs.virtualStoreDir);
632
+ }
631
633
  return args;
632
634
  }
633
635
  /**
@@ -655,7 +657,10 @@ export function pnpmInstallDirs(install) {
655
657
  const virtualStoreDir = path.isAbsolute(configuredVirtualStore)
656
658
  ? configuredVirtualStore
657
659
  : path.resolve(globalNodeModules, configuredVirtualStore);
658
- return { storeDir, virtualStoreDir };
660
+ const pnpmHome = derivePnpmHome(install);
661
+ if (!pnpmHome)
662
+ return null;
663
+ return { globalDir: path.join(pnpmHome, "global"), storeDir, virtualStoreDir };
659
664
  }
660
665
  catch {
661
666
  // The metadata file is absent in older pnpm global layouts. The global
@@ -664,11 +669,93 @@ export function pnpmInstallDirs(install) {
664
669
  if (!pnpmHome)
665
670
  return null;
666
671
  return {
672
+ globalDir: path.join(pnpmHome, "global"),
667
673
  storeDir: path.join(pnpmHome, "store", "v11"),
668
674
  virtualStoreDir: path.join(globalNodeModules, ".pnpm"),
669
675
  };
670
676
  }
671
677
  }
678
+ /** Version recorded by pnpm for the global install that owns this package. */
679
+ export function pnpmInstalledVersion(install) {
680
+ if (install.manager !== "pnpm" || !install.packageRoot)
681
+ return null;
682
+ const normalized = install.packageRoot.replace(/\\/g, "/");
683
+ const match = normalized.match(/^(.*\/global\/\d+\/node_modules)(?:\/|$)|^(.*\/global\/v\d+\/[^/]+\/node_modules)(?:\/|$)/);
684
+ const globalNodeModules = match?.[1] ?? match?.[2];
685
+ if (!globalNodeModules)
686
+ return null;
687
+ try {
688
+ const metadata = yaml.load(readFileSync(path.join(globalNodeModules, ".modules.yaml"), "utf8"));
689
+ const value = typeof metadata?.packageManager === "string" ? metadata.packageManager : "";
690
+ const version = value.match(/^pnpm@(.+)$/)?.[1];
691
+ return version && semver.valid(version) ? version : null;
692
+ }
693
+ catch {
694
+ return null;
695
+ }
696
+ }
697
+ /**
698
+ * Resolve a bounded pnpm executable probe. pnpm 10 and older reject
699
+ * --virtual-store-dir for global installs; pnpm 11+ accepts the flag.
700
+ */
701
+ export function probePnpmVersion(cmd, args, env, cwd) {
702
+ try {
703
+ const plan = buildSpawnPlan(cmd, args);
704
+ const result = spawnSync(plan.cmd, plan.args, {
705
+ encoding: "utf8",
706
+ timeout: 10_000,
707
+ cwd,
708
+ env,
709
+ shell: plan.shell,
710
+ windowsHide: true,
711
+ });
712
+ if (result.error || result.status !== 0)
713
+ return null;
714
+ const match = (result.stdout ?? "").trim().match(/(?:^|\s)(\d+\.\d+\.\d+(?:-[\w.-]+)?)(?:\s|$)/);
715
+ return match?.[1] && semver.valid(match[1]) ? match[1] : null;
716
+ }
717
+ catch (error) {
718
+ console.error(chalk.dim(`hq: pnpm version probe failed (${error instanceof Error ? error.name : "unknown"}).`));
719
+ return null;
720
+ }
721
+ }
722
+ /** Run the install-owned pnpm first; use PATH only if that version cannot run. */
723
+ export function performPnpmUpdate(install, runner = runUpdateCommand, env = process.env, probe = probePnpmVersion) {
724
+ const cwd = mkdtempSync(path.join(os.tmpdir(), "hq-cli-pnpm-update-"));
725
+ const updateEnv = {
726
+ ...env,
727
+ COREPACK_ENABLE_PROJECT_SPEC: "0",
728
+ COREPACK_ENABLE_STRICT: "0",
729
+ COREPACK_DEFAULT_TO_LATEST: "0",
730
+ npm_config_manage_package_manager_versions: "false",
731
+ };
732
+ try {
733
+ const installedVersion = pnpmInstalledVersion(install);
734
+ if (installedVersion) {
735
+ const command = `pnpm@${installedVersion}`;
736
+ if (probe("corepack", [command, "--version"], updateEnv, cwd) === installedVersion) {
737
+ return runner("corepack", [command, ...buildPnpmInstallArgv(install)], updateEnv, cwd);
738
+ }
739
+ }
740
+ const fallbackVersion = probe("pnpm", ["--version"], updateEnv, cwd);
741
+ if (!fallbackVersion) {
742
+ return { ok: false, code: "PNPM_UNAVAILABLE", detail: "pnpm not found on PATH; install-owned Corepack pnpm could not run" };
743
+ }
744
+ // pnpm 10.28.1 and earlier reject --virtual-store-dir with -g. Keep the
745
+ // existing global directory and store, but omit only that unsupported flag.
746
+ const supportsGlobalVirtualStoreDir = semver.gte(fallbackVersion, "11.0.0");
747
+ const args = buildPnpmInstallArgv(install);
748
+ if (!supportsGlobalVirtualStoreDir) {
749
+ const index = args.indexOf("--virtual-store-dir");
750
+ if (index !== -1)
751
+ args.splice(index, 2);
752
+ }
753
+ return runner("pnpm", args, updateEnv, cwd);
754
+ }
755
+ finally {
756
+ rmSync(cwd, { recursive: true, force: true });
757
+ }
758
+ }
672
759
  /** Argv for updating a Bun-managed global install. */
673
760
  export function buildBunInstallArgv() {
674
761
  return ["add", "-g", LATEST_PACKAGE_SPEC];
@@ -1037,7 +1124,7 @@ export function openInstallOutput(verbose, isTty = process.stdout.isTTY === true
1037
1124
  },
1038
1125
  };
1039
1126
  }
1040
- export function runSupervisedUpdateCommand(cmd, args, output, env) {
1127
+ export function runSupervisedUpdateCommand(cmd, args, output, env, cwd) {
1041
1128
  try {
1042
1129
  const plan = buildSpawnPlan(cmd, args);
1043
1130
  const timeoutMs = updateTimeoutMs(env);
@@ -1061,6 +1148,7 @@ export function runSupervisedUpdateCommand(cmd, args, output, env) {
1061
1148
  ], inOwnProcessGroup({
1062
1149
  stdio: output.stdio,
1063
1150
  ...(env ? { env } : {}),
1151
+ ...(cwd ? { cwd } : {}),
1064
1152
  }));
1065
1153
  // spawnSync reports a missing executable via `error`, not a throw.
1066
1154
  if (result.error) {
@@ -1108,20 +1196,22 @@ export function runSupervisedUpdateCommand(cmd, args, output, env) {
1108
1196
  };
1109
1197
  }
1110
1198
  }
1111
- export function runUpdateCommand(cmd, args, env) {
1199
+ export function runUpdateCommand(cmd, args, env, cwd) {
1112
1200
  const output = openInstallOutput(true);
1113
1201
  try {
1114
- return runSupervisedUpdateCommand(cmd, args, output, env);
1202
+ return runSupervisedUpdateCommand(cmd, args, output, env, cwd);
1115
1203
  }
1116
1204
  finally {
1117
1205
  output.dispose();
1118
1206
  }
1119
1207
  }
1120
- function performUpdateCommand(cmd, args, runner = runUpdateCommand, env) {
1208
+ function performUpdateCommand(cmd, args, runner = runUpdateCommand, env, cwd) {
1121
1209
  // Forward env only when there is one to forward, so the common path keeps the
1122
1210
  // two-argument runner call it has always made (and existing runner spies see
1123
1211
  // no phantom `undefined` third argument).
1124
- return env === undefined ? runner(cmd, args) : runner(cmd, args, env);
1212
+ return env === undefined && cwd === undefined
1213
+ ? runner(cmd, args)
1214
+ : runner(cmd, args, env, cwd);
1125
1215
  }
1126
1216
  function performUpdate(command, runner = runUpdateCommand) {
1127
1217
  const parts = command.split(/\s+/).filter(Boolean);
@@ -1141,8 +1231,13 @@ function performUpdate(command, runner = runUpdateCommand) {
1141
1231
  * speak the running manager's language.
1142
1232
  */
1143
1233
  function manualUpdateCommand(install, decision) {
1144
- if (install.manager === "pnpm")
1145
- return `pnpm ${buildPnpmInstallArgv(install).join(" ")}`;
1234
+ if (install.manager === "pnpm") {
1235
+ const version = pnpmInstalledVersion(install);
1236
+ const plan = version
1237
+ ? `corepack pnpm@${version} ${buildPnpmInstallArgv(install).join(" ")}`
1238
+ : `pnpm ${buildPnpmInstallArgv(install).join(" ")}`;
1239
+ return plan;
1240
+ }
1146
1241
  if (install.manager === "bun")
1147
1242
  return `bun ${buildBunInstallArgv().join(" ")}`;
1148
1243
  if (install.prefix)
@@ -1522,7 +1617,9 @@ function attemptRequiredUpdate(decision, deps, install) {
1522
1617
  if (isManagedOutsideNpm) {
1523
1618
  console.error(chalk.dim(` Detected a ${install.manager}-managed global install; updating with ${install.manager}`));
1524
1619
  console.error(chalk.dim(` Running: ${primaryCmd} ${primaryArgs.join(" ")}`));
1525
- result = performUpdateCommand(primaryCmd, primaryArgs, runner, updateEnv);
1620
+ result = install.manager === "pnpm"
1621
+ ? performPnpmUpdate(install, runner, updateEnv ?? process.env, deps.probePnpmVersion)
1622
+ : performUpdateCommand(primaryCmd, primaryArgs, runner, updateEnv);
1526
1623
  }
1527
1624
  else if (prefix) {
1528
1625
  console.error(chalk.dim(` Installing into npm prefix: ${prefix}`));
@@ -1719,6 +1816,10 @@ export const __test__ = {
1719
1816
  enforceUpdateRequired,
1720
1817
  isBunManagedPackageDir,
1721
1818
  pnpmUpdateEnv,
1819
+ pnpmInstallDirs,
1820
+ pnpmInstalledVersion,
1821
+ performPnpmUpdate,
1822
+ probePnpmVersion,
1722
1823
  isLocalDependencyInstall,
1723
1824
  isLocalDependencyPackageDir,
1724
1825
  isNewerVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.33",
3
+ "version": "5.345.35",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {