repowisestage 0.0.67 → 0.0.69

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.
Files changed (2) hide show
  1. package/dist/bin/repowise.js +1148 -986
  2. package/package.json +1 -1
@@ -620,325 +620,6 @@ var init_process_manager = __esm({
620
620
  }
621
621
  });
622
622
 
623
- // ../listener/dist/service-installer.js
624
- var service_installer_exports = {};
625
- __export(service_installer_exports, {
626
- install: () => install,
627
- isInstalled: () => isInstalled,
628
- isServiceRunning: () => isServiceRunning,
629
- stopService: () => stopService,
630
- uninstall: () => uninstall
631
- });
632
- import { execFile as execFile4 } from "child_process";
633
- import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
634
- import { homedir as homedir3 } from "os";
635
- import { join as join13 } from "path";
636
- function exec(cmd, args) {
637
- return new Promise((resolve5, reject) => {
638
- execFile4(cmd, args, (err, stdout) => {
639
- if (err) {
640
- reject(err);
641
- return;
642
- }
643
- resolve5(String(stdout ?? ""));
644
- });
645
- });
646
- }
647
- function plistPath() {
648
- return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
649
- }
650
- function logDir() {
651
- return join13(getConfigDir(), "logs");
652
- }
653
- function buildPlist() {
654
- const cmd = resolveListenerCommand();
655
- const logs = logDir();
656
- const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${a}</string>`).join("\n");
657
- return `<?xml version="1.0" encoding="UTF-8"?>
658
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
659
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
660
- <plist version="1.0">
661
- <dict>
662
- <key>Label</key>
663
- <string>${PLIST_LABEL}</string>
664
- <key>ProgramArguments</key>
665
- <array>
666
- ${programArgs}
667
- </array>
668
- <key>RunAtLoad</key>
669
- <true/>
670
- <key>KeepAlive</key>
671
- <true/>
672
- <key>StandardOutPath</key>
673
- <string>${join13(logs, "listener-stdout.log")}</string>
674
- <key>StandardErrorPath</key>
675
- <string>${join13(logs, "listener-stderr.log")}</string>
676
- <key>ProcessType</key>
677
- <string>Background</string>
678
- <!--
679
- SoftResourceLimits: raise the open-file descriptor limit from the macOS
680
- default of 256 to 4096. When multiple LSP child processes (rust-analyzer,
681
- jdtls, etc.) share the listener's fd budget they can exhaust the default
682
- very quickly, causing EMFILE errors. 4096 matches the system-wide default
683
- set by /etc/launchd.conf on modern macOS and gives enough headroom for
684
- ~10 concurrent LSP servers each opening ~300 fds.
685
-
686
- MemoryLimit: intentionally NOT set here. The launchd MemoryLimit key
687
- (available since macOS 12.3) terminates the entire LaunchAgent when RSS
688
- exceeds the threshold \u2014 it cannot be scoped to individual child processes.
689
- Killing the listener to evict a memory-hungry rust-analyzer instance is
690
- undesirable. Per-child memory pressure is better handled by the LSP
691
- process manager (process-manager.ts) or by OS-level memory pressure
692
- signals (SIGTERM on macOS, OOM killer on Linux).
693
- -->
694
- <key>SoftResourceLimits</key>
695
- <dict>
696
- <key>NumberOfFiles</key>
697
- <integer>4096</integer>
698
- </dict>
699
- </dict>
700
- </plist>`;
701
- }
702
- async function darwinInstall() {
703
- await mkdir8(logDir(), { recursive: true });
704
- await mkdir8(join13(homedir3(), "Library", "LaunchAgents"), { recursive: true });
705
- try {
706
- await exec("launchctl", ["unload", plistPath()]);
707
- } catch {
708
- }
709
- await writeFile8(plistPath(), buildPlist());
710
- await exec("launchctl", ["load", plistPath()]);
711
- }
712
- async function darwinUninstall() {
713
- try {
714
- await exec("launchctl", ["unload", plistPath()]);
715
- } catch {
716
- }
717
- try {
718
- await unlink6(plistPath());
719
- } catch {
720
- }
721
- }
722
- async function darwinIsInstalled() {
723
- try {
724
- const stdout = await exec("launchctl", ["list"]);
725
- return stdout.includes(PLIST_LABEL);
726
- } catch {
727
- return false;
728
- }
729
- }
730
- function unitPath() {
731
- return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
732
- }
733
- function buildUnit() {
734
- const cmd = resolveListenerCommand();
735
- const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
736
- const logs = logDir();
737
- return `[Unit]
738
- Description=RepoWise Listener
739
- After=network-online.target
740
- Wants=network-online.target
741
-
742
- [Service]
743
- Type=simple
744
- ExecStart=${execStart}
745
- Restart=always
746
- RestartSec=10
747
- StandardOutput=append:${join13(logs, "listener-stdout.log")}
748
- StandardError=append:${join13(logs, "listener-stderr.log")}
749
-
750
- [Install]
751
- WantedBy=default.target`;
752
- }
753
- async function linuxInstall() {
754
- await mkdir8(logDir(), { recursive: true });
755
- await mkdir8(join13(homedir3(), ".config", "systemd", "user"), { recursive: true });
756
- await writeFile8(unitPath(), buildUnit());
757
- await exec("systemctl", ["--user", "daemon-reload"]);
758
- await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
759
- await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
760
- }
761
- async function linuxUninstall() {
762
- try {
763
- await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
764
- } catch {
765
- }
766
- try {
767
- await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
768
- } catch {
769
- }
770
- try {
771
- await unlink6(unitPath());
772
- } catch {
773
- }
774
- try {
775
- await exec("systemctl", ["--user", "daemon-reload"]);
776
- } catch {
777
- }
778
- }
779
- async function linuxIsInstalled() {
780
- try {
781
- const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
782
- return stdout.trim() === "enabled";
783
- } catch {
784
- return false;
785
- }
786
- }
787
- async function win32Install() {
788
- await mkdir8(logDir(), { recursive: true });
789
- const cmd = resolveListenerCommand();
790
- const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
791
- await exec("schtasks", [
792
- "/create",
793
- "/tn",
794
- TASK_NAME,
795
- "/tr",
796
- taskCmd,
797
- "/sc",
798
- "onlogon",
799
- "/ru",
800
- process.env.USERNAME ?? "",
801
- "/f"
802
- ]);
803
- await exec("schtasks", ["/run", "/tn", TASK_NAME]);
804
- }
805
- async function win32Uninstall() {
806
- try {
807
- await exec("schtasks", ["/end", "/tn", TASK_NAME]);
808
- } catch {
809
- }
810
- try {
811
- await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
812
- } catch {
813
- }
814
- }
815
- async function win32IsInstalled() {
816
- try {
817
- await exec("schtasks", ["/query", "/tn", TASK_NAME]);
818
- return true;
819
- } catch {
820
- return false;
821
- }
822
- }
823
- async function darwinStop() {
824
- try {
825
- await exec("launchctl", ["unload", plistPath()]);
826
- } catch {
827
- }
828
- }
829
- async function linuxStop() {
830
- await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
831
- }
832
- async function win32Stop() {
833
- await exec("schtasks", ["/end", "/tn", TASK_NAME]);
834
- }
835
- async function darwinIsServiceRunning() {
836
- try {
837
- const stdout = await exec("launchctl", ["list", PLIST_LABEL]);
838
- const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
839
- return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
840
- } catch {
841
- return false;
842
- }
843
- }
844
- async function linuxIsServiceRunning() {
845
- try {
846
- const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
847
- return stdout.trim() === "active";
848
- } catch {
849
- return false;
850
- }
851
- }
852
- async function win32IsServiceRunning() {
853
- try {
854
- const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
855
- return stdout.includes("Running");
856
- } catch {
857
- return false;
858
- }
859
- }
860
- async function stopService() {
861
- switch (process.platform) {
862
- case "darwin":
863
- await darwinStop();
864
- break;
865
- case "linux":
866
- await linuxStop();
867
- break;
868
- case "win32":
869
- await win32Stop();
870
- break;
871
- default:
872
- throw new Error(`Unsupported platform: ${process.platform}`);
873
- }
874
- }
875
- async function install() {
876
- switch (process.platform) {
877
- case "darwin":
878
- await darwinInstall();
879
- break;
880
- case "linux":
881
- await linuxInstall();
882
- break;
883
- case "win32":
884
- await win32Install();
885
- break;
886
- default:
887
- throw new Error(`Unsupported platform: ${process.platform}`);
888
- }
889
- }
890
- async function uninstall() {
891
- switch (process.platform) {
892
- case "darwin":
893
- await darwinUninstall();
894
- break;
895
- case "linux":
896
- await linuxUninstall();
897
- break;
898
- case "win32":
899
- await win32Uninstall();
900
- break;
901
- default:
902
- throw new Error(`Unsupported platform: ${process.platform}`);
903
- }
904
- }
905
- async function isInstalled() {
906
- switch (process.platform) {
907
- case "darwin":
908
- return darwinIsInstalled();
909
- case "linux":
910
- return linuxIsInstalled();
911
- case "win32":
912
- return win32IsInstalled();
913
- default:
914
- return false;
915
- }
916
- }
917
- async function isServiceRunning() {
918
- switch (process.platform) {
919
- case "darwin":
920
- return darwinIsServiceRunning();
921
- case "linux":
922
- return linuxIsServiceRunning();
923
- case "win32":
924
- return win32IsServiceRunning();
925
- default:
926
- return false;
927
- }
928
- }
929
- var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
930
- var init_service_installer = __esm({
931
- "../listener/dist/service-installer.js"() {
932
- "use strict";
933
- init_config_dir();
934
- init_process_manager();
935
- IS_STAGING = true ? true : false;
936
- PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
937
- SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
938
- TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
939
- }
940
- });
941
-
942
623
  // ../listener/dist/lsp/registry.js
943
624
  function resolvePhpLspOverride(env = process.env, configOverrides) {
944
625
  const envChoice = env["PHP_LSP"];
@@ -1415,7 +1096,7 @@ var init_registry = __esm({
1415
1096
  });
1416
1097
 
1417
1098
  // ../listener/dist/lsp/jdk-probe.js
1418
- import { spawn as spawn3 } from "child_process";
1099
+ import { spawn as spawn2 } from "child_process";
1419
1100
  function parseJavaMajorVersion(stderr) {
1420
1101
  const modernMatch = /\b(?:openjdk|java) version "(\d+)(?:\.\d+)*(?:[._]\w+)?"/.exec(stderr);
1421
1102
  if (!modernMatch?.[1])
@@ -1437,7 +1118,7 @@ async function probeJdk(options = {}) {
1437
1118
  return new Promise((resolve5) => {
1438
1119
  let stderr = "";
1439
1120
  let settled = false;
1440
- const child = spawn3("java", ["-version"], { stdio: ["ignore", "pipe", "pipe"] });
1121
+ const child = spawn2("java", ["-version"], { stdio: ["ignore", "pipe", "pipe"] });
1441
1122
  const timer = setTimeout(() => {
1442
1123
  if (settled)
1443
1124
  return;
@@ -1486,15 +1167,15 @@ var init_jdk_probe = __esm({
1486
1167
  import { createWriteStream } from "fs";
1487
1168
  import { promises as fs } from "fs";
1488
1169
  import { createGunzip } from "zlib";
1489
- import { spawn as spawn4 } from "child_process";
1170
+ import { spawn as spawn3 } from "child_process";
1490
1171
  import { pipeline } from "stream/promises";
1491
1172
  import { createHash as createHash3 } from "crypto";
1492
- import { join as join15, dirname as dirname5 } from "path";
1173
+ import { join as join13, dirname as dirname5 } from "path";
1493
1174
  function getNativeLspDir() {
1494
- return join15(getLspInstallDir(), "native");
1175
+ return join13(getLspInstallDir(), "native");
1495
1176
  }
1496
1177
  function getNativeLspBinDir() {
1497
- return join15(getNativeLspDir(), "bin");
1178
+ return join13(getNativeLspDir(), "bin");
1498
1179
  }
1499
1180
  async function ensureNativeLspInstalled(lspKey) {
1500
1181
  const entry = NATIVE_LSP_VERSIONS[lspKey];
@@ -1520,15 +1201,15 @@ async function ensureNativeLspInstalled(lspKey) {
1520
1201
  }
1521
1202
  }
1522
1203
  const usesWrapper = Boolean(asset.launcherJarGlob && asset.wrapperBinaryName);
1523
- const versionDir = join15(getNativeLspDir(), lspKey, entry.version);
1204
+ const versionDir = join13(getNativeLspDir(), lspKey, entry.version);
1524
1205
  if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
1525
1206
  const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
1526
- const shimPath = join15(getNativeLspBinDir(), asset.wrapperBinaryName);
1207
+ const shimPath = join13(getNativeLspBinDir(), asset.wrapperBinaryName);
1527
1208
  if (jarPath && await pathExists(shimPath)) {
1528
1209
  return { installed: false, alreadyPresent: true, binaryPath: shimPath };
1529
1210
  }
1530
1211
  } else {
1531
- const installedBinary = join15(versionDir, asset.binaryPath);
1212
+ const installedBinary = join13(versionDir, asset.binaryPath);
1532
1213
  if (await pathExists(installedBinary)) {
1533
1214
  await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
1534
1215
  return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
@@ -1536,7 +1217,7 @@ async function ensureNativeLspInstalled(lspKey) {
1536
1217
  }
1537
1218
  try {
1538
1219
  await fs.mkdir(versionDir, { recursive: true });
1539
- const tmpDownload = join15(versionDir, `.${asset.filename}.download`);
1220
+ const tmpDownload = join13(versionDir, `.${asset.filename}.download`);
1540
1221
  const downloadUrl = entry.source.kind === "github-release" ? `https://github.com/${entry.source.repo}/releases/download/${entry.version}/${asset.filename}` : asset.url ?? "";
1541
1222
  if (!downloadUrl) {
1542
1223
  return {
@@ -1578,7 +1259,7 @@ async function ensureNativeLspInstalled(lspKey) {
1578
1259
  await pruneStaleVersions(lspKey, entry.version);
1579
1260
  return { installed: true, alreadyPresent: false, binaryPath: shimPath };
1580
1261
  }
1581
- const installedBinary = join15(versionDir, asset.binaryPath);
1262
+ const installedBinary = join13(versionDir, asset.binaryPath);
1582
1263
  if (!await pathExists(installedBinary)) {
1583
1264
  return {
1584
1265
  installed: false,
@@ -1602,7 +1283,7 @@ async function resolveGlobInsideDir(versionDir, glob) {
1602
1283
  const parts = glob.split("/");
1603
1284
  let dir = versionDir;
1604
1285
  for (let i = 0; i < parts.length - 1; i += 1) {
1605
- dir = join15(dir, parts[i]);
1286
+ dir = join13(dir, parts[i]);
1606
1287
  }
1607
1288
  const pattern = parts[parts.length - 1];
1608
1289
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
@@ -1615,10 +1296,10 @@ async function resolveGlobInsideDir(versionDir, glob) {
1615
1296
  }
1616
1297
  const matches = entries.filter((e) => re.test(e)).sort();
1617
1298
  const last = matches[matches.length - 1];
1618
- return last ? join15(dir, last) : null;
1299
+ return last ? join13(dir, last) : null;
1619
1300
  }
1620
1301
  async function pruneStaleVersions(lspKey, currentVersion) {
1621
- const lspDir = join15(getNativeLspDir(), lspKey);
1302
+ const lspDir = join13(getNativeLspDir(), lspKey);
1622
1303
  let entries;
1623
1304
  try {
1624
1305
  entries = await fs.readdir(lspDir);
@@ -1627,7 +1308,7 @@ async function pruneStaleVersions(lspKey, currentVersion) {
1627
1308
  }
1628
1309
  await Promise.all(entries.filter((name) => name !== currentVersion).map(async (name) => {
1629
1310
  try {
1630
- await fs.rm(join15(lspDir, name), { recursive: true, force: true });
1311
+ await fs.rm(join13(lspDir, name), { recursive: true, force: true });
1631
1312
  } catch {
1632
1313
  }
1633
1314
  }));
@@ -1641,9 +1322,9 @@ function cmdQuote(value) {
1641
1322
  async function writeJdtlsWrapper(input) {
1642
1323
  const binDir = getNativeLspBinDir();
1643
1324
  await fs.mkdir(binDir, { recursive: true });
1644
- const shimPath = join15(binDir, input.binaryName);
1325
+ const shimPath = join13(binDir, input.binaryName);
1645
1326
  const platformCfgDir = process.platform === "darwin" ? "config_mac" : process.platform === "win32" ? "config_win" : "config_linux";
1646
- const workspaceBase = join15(input.versionDir, "workspaces");
1327
+ const workspaceBase = join13(input.versionDir, "workspaces");
1647
1328
  const qLauncher = shQuote(input.launcherJarPath);
1648
1329
  const qConfig = shQuote(`${input.versionDir}/${platformCfgDir}`);
1649
1330
  const qWorkspaceBase = shQuote(workspaceBase);
@@ -1703,7 +1384,7 @@ async function ensureSymlinkInBin(lspKey, target, sourceAssetPath) {
1703
1384
  await fs.mkdir(binDir, { recursive: true });
1704
1385
  const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
1705
1386
  const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
1706
- const linkPath = join15(binDir, linkName);
1387
+ const linkPath = join13(binDir, linkName);
1707
1388
  try {
1708
1389
  await fs.unlink(linkPath);
1709
1390
  } catch {
@@ -1752,16 +1433,16 @@ async function extractAsset(source, destDir, asset) {
1752
1433
  return;
1753
1434
  }
1754
1435
  if (filename.endsWith(".gz")) {
1755
- const outPath = join15(destDir, asset.binaryPath);
1436
+ const outPath = join13(destDir, asset.binaryPath);
1756
1437
  await fs.mkdir(dirname5(outPath), { recursive: true });
1757
1438
  await pipeline((await fs.open(source)).createReadStream(), createGunzip(), createWriteStream(outPath));
1758
1439
  return;
1759
1440
  }
1760
- await fs.copyFile(source, join15(destDir, asset.binaryPath));
1441
+ await fs.copyFile(source, join13(destDir, asset.binaryPath));
1761
1442
  }
1762
1443
  async function runExternal(cmd, args) {
1763
1444
  return new Promise((resolve5, reject) => {
1764
- const child = spawn4(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1445
+ const child = spawn3(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1765
1446
  let stderr = "";
1766
1447
  child.stderr.on("data", (chunk) => {
1767
1448
  stderr += chunk.toString();
@@ -1794,10 +1475,10 @@ var init_native_installer = __esm({
1794
1475
 
1795
1476
  // ../listener/dist/lsp/coursier-installer.js
1796
1477
  import { promises as fs2 } from "fs";
1797
- import { spawn as spawn5 } from "child_process";
1798
- import { join as join16 } from "path";
1478
+ import { spawn as spawn4 } from "child_process";
1479
+ import { join as join14 } from "path";
1799
1480
  function getCoursierBinDir() {
1800
- return join16(getLspInstallDir(), "coursier-bin");
1481
+ return join14(getLspInstallDir(), "coursier-bin");
1801
1482
  }
1802
1483
  async function ensureCoursierLspInstalled(key) {
1803
1484
  const entry = COURSIER_LSPS[key];
@@ -1805,8 +1486,8 @@ async function ensureCoursierLspInstalled(key) {
1805
1486
  return { installed: false, alreadyPresent: false, skipped: "no-coursier-entry" };
1806
1487
  }
1807
1488
  const binDir = getCoursierBinDir();
1808
- const binaryPath = join16(binDir, entry.appName);
1809
- const binaryPathExe = join16(binDir, `${entry.appName}.bat`);
1489
+ const binaryPath = join14(binDir, entry.appName);
1490
+ const binaryPathExe = join14(binDir, `${entry.appName}.bat`);
1810
1491
  if (await pathExists2(binaryPath) || await pathExists2(binaryPathExe)) {
1811
1492
  return { installed: false, alreadyPresent: true, binaryPath };
1812
1493
  }
@@ -1819,492 +1500,881 @@ async function ensureCoursierLspInstalled(key) {
1819
1500
  error: csBootstrap.error ?? csBootstrap.skipped
1820
1501
  };
1821
1502
  }
1822
- const csPath = join16(getNativeLspBinDir(), "coursier");
1823
- const csPathExe = join16(getNativeLspBinDir(), "coursier.exe");
1824
- const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
1825
- if (!cs) {
1826
- return {
1827
- installed: false,
1828
- alreadyPresent: false,
1829
- skipped: "coursier-bootstrap-failed",
1830
- error: `coursier launcher not found at ${csPath} after bootstrap`
1831
- };
1503
+ const csPath = join14(getNativeLspBinDir(), "coursier");
1504
+ const csPathExe = join14(getNativeLspBinDir(), "coursier.exe");
1505
+ const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
1506
+ if (!cs) {
1507
+ return {
1508
+ installed: false,
1509
+ alreadyPresent: false,
1510
+ skipped: "coursier-bootstrap-failed",
1511
+ error: `coursier launcher not found at ${csPath} after bootstrap`
1512
+ };
1513
+ }
1514
+ try {
1515
+ await fs2.mkdir(binDir, { recursive: true });
1516
+ await runCs(cs, ["install", "--install-dir", binDir, entry.appName]);
1517
+ if (!await pathExists2(binaryPath) && !await pathExists2(binaryPathExe)) {
1518
+ return {
1519
+ installed: false,
1520
+ alreadyPresent: false,
1521
+ error: `cs install ${entry.appName} completed but launcher missing at ${binaryPath}`
1522
+ };
1523
+ }
1524
+ return { installed: true, alreadyPresent: false, binaryPath };
1525
+ } catch (err) {
1526
+ return {
1527
+ installed: false,
1528
+ alreadyPresent: false,
1529
+ error: err instanceof Error ? err.message : String(err)
1530
+ };
1531
+ }
1532
+ }
1533
+ async function runCs(cs, args) {
1534
+ return new Promise((resolve5, reject) => {
1535
+ const child = spawn4(cs, args, { stdio: ["ignore", "pipe", "pipe"] });
1536
+ let stderr = "";
1537
+ child.stderr.on("data", (chunk) => {
1538
+ stderr += chunk.toString();
1539
+ });
1540
+ child.on("error", reject);
1541
+ child.on("close", (code) => {
1542
+ if (code === 0)
1543
+ resolve5();
1544
+ else
1545
+ reject(new Error(`cs ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
1546
+ });
1547
+ });
1548
+ }
1549
+ async function pathExists2(p) {
1550
+ try {
1551
+ await fs2.access(p);
1552
+ return true;
1553
+ } catch {
1554
+ return false;
1555
+ }
1556
+ }
1557
+ var init_coursier_installer = __esm({
1558
+ "../listener/dist/lsp/coursier-installer.js"() {
1559
+ "use strict";
1560
+ init_src();
1561
+ init_installer();
1562
+ init_native_installer();
1563
+ }
1564
+ });
1565
+
1566
+ // ../listener/dist/lsp/toolchain-installer.js
1567
+ import { spawn as spawn5 } from "child_process";
1568
+ import { promises as fs3 } from "fs";
1569
+ import { homedir as homedir3 } from "os";
1570
+ import { join as join15 } from "path";
1571
+ async function ensureToolchainLspInstalled(key) {
1572
+ const entry = TOOLCHAIN_LSPS[key];
1573
+ if (!entry) {
1574
+ return { installed: false, alreadyPresent: false, skipped: "no-toolchain-entry" };
1575
+ }
1576
+ if (entry.bundled) {
1577
+ const found = await isCommandOnPath(entry.binaryName);
1578
+ if (found)
1579
+ return { installed: false, alreadyPresent: true };
1580
+ return { installed: false, alreadyPresent: false, skipped: "bundled-not-installable" };
1581
+ }
1582
+ if (await isCommandOnPath(entry.binaryName)) {
1583
+ return { installed: false, alreadyPresent: true, skipped: "already-on-path" };
1584
+ }
1585
+ if (!entry.toolchain) {
1586
+ return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
1587
+ }
1588
+ if (!await isCommandOnPath(entry.toolchain)) {
1589
+ return {
1590
+ installed: false,
1591
+ alreadyPresent: false,
1592
+ skipped: "toolchain-missing",
1593
+ error: `${entry.toolchain} toolchain not on PATH`
1594
+ };
1595
+ }
1596
+ let binDir;
1597
+ try {
1598
+ binDir = await resolveToolchainBinDir(entry);
1599
+ } catch (err) {
1600
+ return {
1601
+ installed: false,
1602
+ alreadyPresent: false,
1603
+ error: err instanceof Error ? err.message : String(err)
1604
+ };
1605
+ }
1606
+ try {
1607
+ if (!entry.installArgs) {
1608
+ return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
1609
+ }
1610
+ await runToolchain(entry.toolchain, entry.installArgs);
1611
+ } catch (err) {
1612
+ return {
1613
+ installed: false,
1614
+ alreadyPresent: false,
1615
+ error: err instanceof Error ? err.message : String(err)
1616
+ };
1617
+ }
1618
+ const binaryPath = join15(binDir, entry.binaryName);
1619
+ if (!await pathExists3(binaryPath)) {
1620
+ return {
1621
+ installed: false,
1622
+ alreadyPresent: false,
1623
+ error: `${entry.toolchain} install of ${entry.binaryName} completed but binary missing at ${binaryPath}`,
1624
+ binDir
1625
+ };
1626
+ }
1627
+ return { installed: true, alreadyPresent: false, binaryPath, binDir };
1628
+ }
1629
+ async function resolveToolchainBinDir(entry) {
1630
+ if (!entry.toolchain) {
1631
+ throw new Error("cannot resolve bin dir for entry without toolchain");
1632
+ }
1633
+ if (entry.toolchainBinDir) {
1634
+ if (entry.toolchain === "go") {
1635
+ const gopath = process.env["GOPATH"] ?? join15(homedir3(), "go");
1636
+ return join15(gopath, "bin");
1637
+ }
1638
+ return join15(homedir3(), entry.toolchainBinDir);
1639
+ }
1640
+ if (entry.toolchain === "gem") {
1641
+ const userdir = await captureStdout("gem", ["env", "userdir"]);
1642
+ return join15(userdir.trim(), "bin");
1643
+ }
1644
+ throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
1645
+ }
1646
+ function getToolchainBinDirs() {
1647
+ const dirs = /* @__PURE__ */ new Set();
1648
+ for (const entry of Object.values(TOOLCHAIN_LSPS)) {
1649
+ if (entry.bundled || !entry.toolchain || !entry.toolchainBinDir)
1650
+ continue;
1651
+ if (entry.toolchain === "go") {
1652
+ const gopath = process.env["GOPATH"] ?? join15(homedir3(), "go");
1653
+ dirs.add(join15(gopath, "bin"));
1654
+ } else {
1655
+ dirs.add(join15(homedir3(), entry.toolchainBinDir));
1656
+ }
1657
+ }
1658
+ return [...dirs];
1659
+ }
1660
+ function runToolchain(cmd, args) {
1661
+ return new Promise((resolve5, reject) => {
1662
+ const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1663
+ let stderr = "";
1664
+ child.stderr.on("data", (chunk) => {
1665
+ stderr += chunk.toString();
1666
+ });
1667
+ child.on("error", reject);
1668
+ child.on("close", (code) => {
1669
+ if (code === 0)
1670
+ resolve5();
1671
+ else
1672
+ reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
1673
+ });
1674
+ });
1675
+ }
1676
+ function captureStdout(cmd, args) {
1677
+ return new Promise((resolve5, reject) => {
1678
+ const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1679
+ let stdout = "";
1680
+ let stderr = "";
1681
+ child.stdout.on("data", (chunk) => {
1682
+ stdout += chunk.toString();
1683
+ });
1684
+ child.stderr.on("data", (chunk) => {
1685
+ stderr += chunk.toString();
1686
+ });
1687
+ child.on("error", reject);
1688
+ child.on("close", (code) => {
1689
+ if (code === 0)
1690
+ resolve5(stdout);
1691
+ else
1692
+ reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr}`));
1693
+ });
1694
+ });
1695
+ }
1696
+ async function isCommandOnPath(command) {
1697
+ if (/[^\w./+-]/.test(command))
1698
+ return false;
1699
+ const probeCmd = process.platform === "win32" ? "where" : "which";
1700
+ return new Promise((resolve5) => {
1701
+ const child = spawn5(probeCmd, [command], { stdio: "ignore", timeout: 5e3 });
1702
+ child.on("error", () => resolve5(false));
1703
+ child.on("close", (code) => resolve5(code === 0));
1704
+ });
1705
+ }
1706
+ async function pathExists3(p) {
1707
+ try {
1708
+ await fs3.access(p);
1709
+ return true;
1710
+ } catch {
1711
+ return false;
1712
+ }
1713
+ }
1714
+ var init_toolchain_installer = __esm({
1715
+ "../listener/dist/lsp/toolchain-installer.js"() {
1716
+ "use strict";
1717
+ init_src();
1718
+ }
1719
+ });
1720
+
1721
+ // ../listener/dist/lsp/installer.js
1722
+ import { promises as fs4 } from "fs";
1723
+ import { join as join16, dirname as dirname6 } from "path";
1724
+ import { spawn as spawn6 } from "child_process";
1725
+ import lockfile3 from "proper-lockfile";
1726
+ function getLspInstallDir() {
1727
+ return join16(getConfigDir(), "lsp-servers");
1728
+ }
1729
+ function getLspBinDir() {
1730
+ return join16(getLspInstallDir(), "node_modules", ".bin");
1731
+ }
1732
+ async function ensureNpmLspInstalled(config2) {
1733
+ if (!config2.npmPackage)
1734
+ return { installed: false, skipped: "no-npm-package" };
1735
+ const installDir = getLspInstallDir();
1736
+ const binPath = join16(installDir, "node_modules", ".bin", config2.command);
1737
+ if (await pathExists4(binPath)) {
1738
+ return { installed: false, skipped: "already-present" };
1832
1739
  }
1833
1740
  try {
1834
- await fs2.mkdir(binDir, { recursive: true });
1835
- await runCs(cs, ["install", "--install-dir", binDir, entry.appName]);
1836
- if (!await pathExists2(binaryPath) && !await pathExists2(binaryPathExe)) {
1837
- return {
1838
- installed: false,
1839
- alreadyPresent: false,
1840
- error: `cs install ${entry.appName} completed but launcher missing at ${binaryPath}`
1841
- };
1741
+ await fs4.mkdir(installDir, { recursive: true });
1742
+ let release = null;
1743
+ try {
1744
+ release = await lockfile3.lock(installDir, {
1745
+ stale: 18e4,
1746
+ retries: { retries: 20, factor: 1.5, minTimeout: 500, maxTimeout: 5e3 },
1747
+ realpath: false
1748
+ });
1749
+ } catch {
1750
+ if (await pathExists4(binPath)) {
1751
+ return { installed: false, skipped: "already-present" };
1752
+ }
1753
+ }
1754
+ try {
1755
+ if (await pathExists4(binPath)) {
1756
+ return { installed: false, skipped: "already-present" };
1757
+ }
1758
+ const pkgJsonPath = join16(installDir, "package.json");
1759
+ if (!await pathExists4(pkgJsonPath)) {
1760
+ await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
1761
+ }
1762
+ await runNpmInstall(installDir, config2.npmPackage);
1763
+ if (!await pathExists4(binPath)) {
1764
+ return {
1765
+ installed: false,
1766
+ error: `npm install completed but ${config2.command} still not at ${binPath}`
1767
+ };
1768
+ }
1769
+ return { installed: true };
1770
+ } finally {
1771
+ if (release) {
1772
+ try {
1773
+ await release();
1774
+ } catch {
1775
+ }
1776
+ }
1842
1777
  }
1843
- return { installed: true, alreadyPresent: false, binaryPath };
1844
1778
  } catch (err) {
1845
1779
  return {
1846
1780
  installed: false,
1847
- alreadyPresent: false,
1848
1781
  error: err instanceof Error ? err.message : String(err)
1849
1782
  };
1850
1783
  }
1851
1784
  }
1852
- async function runCs(cs, args) {
1785
+ async function resolveNpmCommand() {
1786
+ const nodeDir = dirname6(process.execPath);
1787
+ for (const candidate of [join16(nodeDir, "npm"), join16(nodeDir, "npm.cmd")]) {
1788
+ try {
1789
+ await fs4.access(candidate);
1790
+ return candidate;
1791
+ } catch {
1792
+ }
1793
+ }
1794
+ for (const candidate of ["/opt/homebrew/bin/npm", "/usr/local/bin/npm", "/usr/bin/npm"]) {
1795
+ try {
1796
+ await fs4.access(candidate);
1797
+ return candidate;
1798
+ } catch {
1799
+ }
1800
+ }
1801
+ return "npm";
1802
+ }
1803
+ async function runNpmInstall(cwd, pkg2) {
1804
+ const npmCmd = await resolveNpmCommand();
1805
+ const nodeDir = dirname6(process.execPath);
1806
+ const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
1853
1807
  return new Promise((resolve5, reject) => {
1854
- const child = spawn5(cs, args, { stdio: ["ignore", "pipe", "pipe"] });
1808
+ const child = spawn6(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
1809
+ cwd,
1810
+ stdio: ["ignore", "pipe", "pipe"],
1811
+ env: { ...process.env, PATH: augmentedPath }
1812
+ });
1855
1813
  let stderr = "";
1856
1814
  child.stderr.on("data", (chunk) => {
1857
1815
  stderr += chunk.toString();
1858
1816
  });
1859
- child.on("error", reject);
1817
+ child.on("error", (err) => reject(err));
1860
1818
  child.on("close", (code) => {
1861
- if (code === 0)
1819
+ if (code === 0) {
1862
1820
  resolve5();
1863
- else
1864
- reject(new Error(`cs ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
1821
+ } else {
1822
+ reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
1823
+ }
1865
1824
  });
1866
1825
  });
1867
1826
  }
1868
- async function pathExists2(p) {
1827
+ function sanitizeNpmStderr(raw) {
1828
+ const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
1829
+ return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
1830
+ }
1831
+ async function pathExists4(p) {
1869
1832
  try {
1870
- await fs2.access(p);
1833
+ await fs4.access(p);
1871
1834
  return true;
1872
1835
  } catch {
1873
1836
  return false;
1874
1837
  }
1875
1838
  }
1876
- var init_coursier_installer = __esm({
1877
- "../listener/dist/lsp/coursier-installer.js"() {
1839
+ async function detectRepoLanguages(repoRoot) {
1840
+ const found = /* @__PURE__ */ new Set();
1841
+ let inspected = 0;
1842
+ const MAX_INSPECT = 200;
1843
+ async function inspect(dir) {
1844
+ if (inspected >= MAX_INSPECT)
1845
+ return;
1846
+ let entries;
1847
+ try {
1848
+ entries = await fs4.readdir(dir);
1849
+ } catch {
1850
+ return;
1851
+ }
1852
+ for (const name of entries) {
1853
+ if (inspected >= MAX_INSPECT)
1854
+ return;
1855
+ if (name.startsWith("."))
1856
+ continue;
1857
+ if (name === "node_modules" || name === "dist" || name === "build")
1858
+ continue;
1859
+ const lang = detectLanguage(name);
1860
+ if (lang) {
1861
+ found.add(lang);
1862
+ inspected += 1;
1863
+ }
1864
+ }
1865
+ }
1866
+ await inspect(repoRoot);
1867
+ let topEntries = [];
1868
+ try {
1869
+ topEntries = await fs4.readdir(repoRoot);
1870
+ } catch {
1871
+ return found;
1872
+ }
1873
+ for (const name of topEntries) {
1874
+ if (name.startsWith("."))
1875
+ continue;
1876
+ if (name === "node_modules" || name === "dist" || name === "build")
1877
+ continue;
1878
+ const sub = join16(repoRoot, name);
1879
+ try {
1880
+ const stat8 = await fs4.stat(sub);
1881
+ if (stat8.isDirectory())
1882
+ await inspect(sub);
1883
+ } catch {
1884
+ }
1885
+ }
1886
+ return found;
1887
+ }
1888
+ async function prepareLspServersForRepos(repos, options = {}) {
1889
+ const detected = /* @__PURE__ */ new Set();
1890
+ for (const r of repos) {
1891
+ if (!r.localPath)
1892
+ continue;
1893
+ try {
1894
+ const stat8 = await fs4.stat(r.localPath);
1895
+ if (!stat8.isDirectory())
1896
+ continue;
1897
+ } catch {
1898
+ continue;
1899
+ }
1900
+ try {
1901
+ const langs = await detectRepoLanguages(r.localPath);
1902
+ for (const l of langs)
1903
+ detected.add(l);
1904
+ } catch {
1905
+ }
1906
+ }
1907
+ const results = [];
1908
+ for (const language of detected) {
1909
+ const configs = getEffectiveConfigList(language, process.env, options.lspOverrides);
1910
+ const npmConfig = configs.find((c) => c.npmPackage);
1911
+ if (npmConfig) {
1912
+ const outcome = await ensureNpmLspInstalled(npmConfig);
1913
+ results.push({
1914
+ language,
1915
+ installed: outcome.installed,
1916
+ alreadyPresent: outcome.skipped === "already-present",
1917
+ skippedNoNpmPackage: false,
1918
+ installMethod: "npm",
1919
+ error: outcome.error
1920
+ });
1921
+ continue;
1922
+ }
1923
+ const nativeConfig = configs.find((c) => c.nativeInstallKey);
1924
+ if (nativeConfig?.nativeInstallKey) {
1925
+ const outcome = await ensureNativeLspInstalled(nativeConfig.nativeInstallKey);
1926
+ results.push({
1927
+ language,
1928
+ installed: outcome.installed,
1929
+ alreadyPresent: outcome.alreadyPresent,
1930
+ skippedNoNpmPackage: false,
1931
+ installMethod: "native",
1932
+ skipped: outcome.skipped,
1933
+ hint: outcome.skipped ? nativeConfig.installHint : void 0,
1934
+ error: outcome.error
1935
+ });
1936
+ continue;
1937
+ }
1938
+ const coursierConfig = configs.find((c) => c.coursierInstallKey);
1939
+ if (coursierConfig?.coursierInstallKey) {
1940
+ const outcome = await ensureCoursierLspInstalled(coursierConfig.coursierInstallKey);
1941
+ results.push({
1942
+ language,
1943
+ installed: outcome.installed,
1944
+ alreadyPresent: outcome.alreadyPresent,
1945
+ skippedNoNpmPackage: false,
1946
+ installMethod: "coursier",
1947
+ skipped: outcome.skipped,
1948
+ hint: outcome.skipped ? coursierConfig.installHint : void 0,
1949
+ error: outcome.error
1950
+ });
1951
+ continue;
1952
+ }
1953
+ const toolchainConfig = configs.find((c) => c.toolchainInstallKey);
1954
+ if (toolchainConfig?.toolchainInstallKey) {
1955
+ const outcome = await ensureToolchainLspInstalled(toolchainConfig.toolchainInstallKey);
1956
+ results.push({
1957
+ language,
1958
+ installed: outcome.installed,
1959
+ alreadyPresent: outcome.alreadyPresent,
1960
+ skippedNoNpmPackage: false,
1961
+ installMethod: "toolchain",
1962
+ skipped: outcome.skipped,
1963
+ // Surface the install hint for any skip reason — toolchain
1964
+ // missing means user needs to install it; bundled SDK means
1965
+ // user needs the SDK.
1966
+ hint: outcome.skipped ? toolchainConfig.installHint : void 0,
1967
+ error: outcome.error
1968
+ });
1969
+ continue;
1970
+ }
1971
+ results.push({
1972
+ language,
1973
+ installed: false,
1974
+ alreadyPresent: false,
1975
+ skippedNoNpmPackage: true,
1976
+ hint: configs[0]?.installHint
1977
+ });
1978
+ }
1979
+ return results;
1980
+ }
1981
+ var init_installer = __esm({
1982
+ "../listener/dist/lsp/installer.js"() {
1878
1983
  "use strict";
1879
- init_src();
1880
- init_installer();
1984
+ init_config_dir();
1985
+ init_registry();
1881
1986
  init_native_installer();
1987
+ init_coursier_installer();
1988
+ init_toolchain_installer();
1882
1989
  }
1883
1990
  });
1884
1991
 
1885
- // ../listener/dist/lsp/toolchain-installer.js
1886
- import { spawn as spawn6 } from "child_process";
1887
- import { promises as fs3 } from "fs";
1992
+ // ../listener/dist/lsp/daemon-path.js
1888
1993
  import { homedir as homedir4 } from "os";
1889
- import { join as join17 } from "path";
1890
- async function ensureToolchainLspInstalled(key) {
1891
- const entry = TOOLCHAIN_LSPS[key];
1892
- if (!entry) {
1893
- return { installed: false, alreadyPresent: false, skipped: "no-toolchain-entry" };
1894
- }
1895
- if (entry.bundled) {
1896
- const found = await isCommandOnPath(entry.binaryName);
1897
- if (found)
1898
- return { installed: false, alreadyPresent: true };
1899
- return { installed: false, alreadyPresent: false, skipped: "bundled-not-installable" };
1900
- }
1901
- if (await isCommandOnPath(entry.binaryName)) {
1902
- return { installed: false, alreadyPresent: true, skipped: "already-on-path" };
1903
- }
1904
- if (!entry.toolchain) {
1905
- return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
1906
- }
1907
- if (!await isCommandOnPath(entry.toolchain)) {
1908
- return {
1909
- installed: false,
1910
- alreadyPresent: false,
1911
- skipped: "toolchain-missing",
1912
- error: `${entry.toolchain} toolchain not on PATH`
1913
- };
1914
- }
1915
- let binDir;
1916
- try {
1917
- binDir = await resolveToolchainBinDir(entry);
1918
- } catch (err) {
1919
- return {
1920
- installed: false,
1921
- alreadyPresent: false,
1922
- error: err instanceof Error ? err.message : String(err)
1923
- };
1994
+ import { delimiter, dirname as dirname7, join as join17 } from "path";
1995
+ function commonBinDirs() {
1996
+ const home = homedir4();
1997
+ return [
1998
+ "/opt/homebrew/bin",
1999
+ // Apple Silicon Homebrew
2000
+ "/opt/homebrew/sbin",
2001
+ "/usr/local/bin",
2002
+ // Intel Homebrew / common installs
2003
+ "/usr/bin",
2004
+ "/bin",
2005
+ "/usr/sbin",
2006
+ "/sbin",
2007
+ join17(home, ".local", "bin"),
2008
+ // pipx / pip --user / uv
2009
+ join17(home, ".npm-global", "bin"),
2010
+ // common npm global prefix
2011
+ join17(home, "go", "bin"),
2012
+ // gopls
2013
+ join17(home, ".cargo", "bin")
2014
+ // rust-analyzer via cargo
2015
+ ];
2016
+ }
2017
+ function managedBinDirs() {
2018
+ return [getLspBinDir(), getNativeLspBinDir(), getCoursierBinDir(), ...getToolchainBinDirs()];
2019
+ }
2020
+ function dedup(dirs) {
2021
+ return [...new Set(dirs.filter((d) => d.length > 0))];
2022
+ }
2023
+ function daemonPathDirs() {
2024
+ return dedup([dirname7(process.execPath), ...commonBinDirs(), ...managedBinDirs()]);
2025
+ }
2026
+ function buildDaemonPath() {
2027
+ return daemonPathDirs().join(delimiter);
2028
+ }
2029
+ var init_daemon_path = __esm({
2030
+ "../listener/dist/lsp/daemon-path.js"() {
2031
+ "use strict";
2032
+ init_installer();
2033
+ init_native_installer();
2034
+ init_coursier_installer();
2035
+ init_toolchain_installer();
1924
2036
  }
2037
+ });
2038
+
2039
+ // ../listener/dist/service-installer.js
2040
+ var service_installer_exports = {};
2041
+ __export(service_installer_exports, {
2042
+ install: () => install,
2043
+ isInstalled: () => isInstalled,
2044
+ isServiceRunning: () => isServiceRunning,
2045
+ stopService: () => stopService,
2046
+ uninstall: () => uninstall
2047
+ });
2048
+ import { execFile as execFile4 } from "child_process";
2049
+ import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
2050
+ import { homedir as homedir5 } from "os";
2051
+ import { join as join18 } from "path";
2052
+ function exec(cmd, args) {
2053
+ return new Promise((resolve5, reject) => {
2054
+ execFile4(cmd, args, (err, stdout) => {
2055
+ if (err) {
2056
+ reject(err);
2057
+ return;
2058
+ }
2059
+ resolve5(String(stdout ?? ""));
2060
+ });
2061
+ });
2062
+ }
2063
+ function xmlEscape(value) {
2064
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2065
+ }
2066
+ function sanitizeEnvValue(value) {
2067
+ return Array.from(value).filter((ch) => ch.charCodeAt(0) >= 32).join("");
2068
+ }
2069
+ function plistPath() {
2070
+ return join18(homedir5(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
2071
+ }
2072
+ function logDir() {
2073
+ return join18(getConfigDir(), "logs");
2074
+ }
2075
+ function buildPlist() {
2076
+ const cmd = resolveListenerCommand();
2077
+ const logs = logDir();
2078
+ const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${xmlEscape(a)}</string>`).join("\n");
2079
+ return `<?xml version="1.0" encoding="UTF-8"?>
2080
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
2081
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2082
+ <plist version="1.0">
2083
+ <dict>
2084
+ <key>Label</key>
2085
+ <string>${xmlEscape(PLIST_LABEL)}</string>
2086
+ <key>ProgramArguments</key>
2087
+ <array>
2088
+ ${programArgs}
2089
+ </array>
2090
+ <key>RunAtLoad</key>
2091
+ <true/>
2092
+ <key>KeepAlive</key>
2093
+ <true/>
2094
+ <!--
2095
+ FIX-8: launchd inherits a minimal PATH (/usr/bin:/bin:\u2026) that excludes
2096
+ /opt/homebrew/bin + the npm-global bin, so user-installed LSP servers
2097
+ (pyright, rust-analyzer, \u2026) cannot be spawned and the typed-resolution
2098
+ which-probe fails. Inject a rich PATH (single source of truth:
2099
+ buildDaemonPath in lsp/daemon-path.ts) so the daemon resolves them.
2100
+ -->
2101
+ <key>EnvironmentVariables</key>
2102
+ <dict>
2103
+ <key>PATH</key>
2104
+ <string>${xmlEscape(buildDaemonPath())}</string>
2105
+ </dict>
2106
+ <key>StandardOutPath</key>
2107
+ <string>${xmlEscape(join18(logs, "listener-stdout.log"))}</string>
2108
+ <key>StandardErrorPath</key>
2109
+ <string>${xmlEscape(join18(logs, "listener-stderr.log"))}</string>
2110
+ <key>ProcessType</key>
2111
+ <string>Background</string>
2112
+ <!--
2113
+ SoftResourceLimits: raise the open-file descriptor limit from the macOS
2114
+ default of 256 to 4096. When multiple LSP child processes (rust-analyzer,
2115
+ jdtls, etc.) share the listener's fd budget they can exhaust the default
2116
+ very quickly, causing EMFILE errors. 4096 matches the system-wide default
2117
+ set by /etc/launchd.conf on modern macOS and gives enough headroom for
2118
+ ~10 concurrent LSP servers each opening ~300 fds.
2119
+
2120
+ MemoryLimit: intentionally NOT set here. The launchd MemoryLimit key
2121
+ (available since macOS 12.3) terminates the entire LaunchAgent when RSS
2122
+ exceeds the threshold \u2014 it cannot be scoped to individual child processes.
2123
+ Killing the listener to evict a memory-hungry rust-analyzer instance is
2124
+ undesirable. Per-child memory pressure is better handled by the LSP
2125
+ process manager (process-manager.ts) or by OS-level memory pressure
2126
+ signals (SIGTERM on macOS, OOM killer on Linux).
2127
+ -->
2128
+ <key>SoftResourceLimits</key>
2129
+ <dict>
2130
+ <key>NumberOfFiles</key>
2131
+ <integer>4096</integer>
2132
+ </dict>
2133
+ </dict>
2134
+ </plist>`;
2135
+ }
2136
+ async function darwinInstall() {
2137
+ await mkdir8(logDir(), { recursive: true });
2138
+ await mkdir8(join18(homedir5(), "Library", "LaunchAgents"), { recursive: true });
1925
2139
  try {
1926
- if (!entry.installArgs) {
1927
- return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
1928
- }
1929
- await runToolchain(entry.toolchain, entry.installArgs);
1930
- } catch (err) {
1931
- return {
1932
- installed: false,
1933
- alreadyPresent: false,
1934
- error: err instanceof Error ? err.message : String(err)
1935
- };
1936
- }
1937
- const binaryPath = join17(binDir, entry.binaryName);
1938
- if (!await pathExists3(binaryPath)) {
1939
- return {
1940
- installed: false,
1941
- alreadyPresent: false,
1942
- error: `${entry.toolchain} install of ${entry.binaryName} completed but binary missing at ${binaryPath}`,
1943
- binDir
1944
- };
2140
+ await exec("launchctl", ["unload", plistPath()]);
2141
+ } catch {
1945
2142
  }
1946
- return { installed: true, alreadyPresent: false, binaryPath, binDir };
2143
+ await writeFile8(plistPath(), buildPlist());
2144
+ await exec("launchctl", ["load", plistPath()]);
1947
2145
  }
1948
- async function resolveToolchainBinDir(entry) {
1949
- if (!entry.toolchain) {
1950
- throw new Error("cannot resolve bin dir for entry without toolchain");
1951
- }
1952
- if (entry.toolchainBinDir) {
1953
- if (entry.toolchain === "go") {
1954
- const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
1955
- return join17(gopath, "bin");
1956
- }
1957
- return join17(homedir4(), entry.toolchainBinDir);
2146
+ async function darwinUninstall() {
2147
+ try {
2148
+ await exec("launchctl", ["unload", plistPath()]);
2149
+ } catch {
1958
2150
  }
1959
- if (entry.toolchain === "gem") {
1960
- const userdir = await captureStdout("gem", ["env", "userdir"]);
1961
- return join17(userdir.trim(), "bin");
2151
+ try {
2152
+ await unlink6(plistPath());
2153
+ } catch {
1962
2154
  }
1963
- throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
1964
2155
  }
1965
- function getToolchainBinDirs() {
1966
- const dirs = /* @__PURE__ */ new Set();
1967
- for (const entry of Object.values(TOOLCHAIN_LSPS)) {
1968
- if (entry.bundled || !entry.toolchain || !entry.toolchainBinDir)
1969
- continue;
1970
- if (entry.toolchain === "go") {
1971
- const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
1972
- dirs.add(join17(gopath, "bin"));
1973
- } else {
1974
- dirs.add(join17(homedir4(), entry.toolchainBinDir));
1975
- }
2156
+ async function darwinIsInstalled() {
2157
+ try {
2158
+ const stdout = await exec("launchctl", ["list"]);
2159
+ return stdout.includes(PLIST_LABEL);
2160
+ } catch {
2161
+ return false;
1976
2162
  }
1977
- return [...dirs];
1978
- }
1979
- function runToolchain(cmd, args) {
1980
- return new Promise((resolve5, reject) => {
1981
- const child = spawn6(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1982
- let stderr = "";
1983
- child.stderr.on("data", (chunk) => {
1984
- stderr += chunk.toString();
1985
- });
1986
- child.on("error", reject);
1987
- child.on("close", (code) => {
1988
- if (code === 0)
1989
- resolve5();
1990
- else
1991
- reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
1992
- });
1993
- });
1994
2163
  }
1995
- function captureStdout(cmd, args) {
1996
- return new Promise((resolve5, reject) => {
1997
- const child = spawn6(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1998
- let stdout = "";
1999
- let stderr = "";
2000
- child.stdout.on("data", (chunk) => {
2001
- stdout += chunk.toString();
2002
- });
2003
- child.stderr.on("data", (chunk) => {
2004
- stderr += chunk.toString();
2005
- });
2006
- child.on("error", reject);
2007
- child.on("close", (code) => {
2008
- if (code === 0)
2009
- resolve5(stdout);
2010
- else
2011
- reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr}`));
2012
- });
2013
- });
2164
+ function unitPath() {
2165
+ return join18(homedir5(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
2014
2166
  }
2015
- async function isCommandOnPath(command) {
2016
- if (/[^\w./+-]/.test(command))
2017
- return false;
2018
- const probeCmd = process.platform === "win32" ? "where" : "which";
2019
- return new Promise((resolve5) => {
2020
- const child = spawn6(probeCmd, [command], { stdio: "ignore", timeout: 5e3 });
2021
- child.on("error", () => resolve5(false));
2022
- child.on("close", (code) => resolve5(code === 0));
2023
- });
2167
+ function buildUnit() {
2168
+ const cmd = resolveListenerCommand();
2169
+ const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
2170
+ const logs = logDir();
2171
+ return `[Unit]
2172
+ Description=RepoWise Listener
2173
+ After=network-online.target
2174
+ Wants=network-online.target
2175
+
2176
+ [Service]
2177
+ Type=simple
2178
+ # FIX-8: systemd user services run with a minimal PATH and don't load shell
2179
+ # profiles. Inject a rich PATH so LSP servers + the typed-resolution probe
2180
+ # resolve (single source of truth: buildDaemonPath() in lsp/daemon-path.ts).
2181
+ Environment=PATH=${sanitizeEnvValue(buildDaemonPath())}
2182
+ ExecStart=${execStart}
2183
+ Restart=always
2184
+ RestartSec=10
2185
+ StandardOutput=append:${join18(logs, "listener-stdout.log")}
2186
+ StandardError=append:${join18(logs, "listener-stderr.log")}
2187
+
2188
+ [Install]
2189
+ WantedBy=default.target`;
2024
2190
  }
2025
- async function pathExists3(p) {
2191
+ async function linuxInstall() {
2192
+ await mkdir8(logDir(), { recursive: true });
2193
+ await mkdir8(join18(homedir5(), ".config", "systemd", "user"), { recursive: true });
2194
+ await writeFile8(unitPath(), buildUnit());
2195
+ await exec("systemctl", ["--user", "daemon-reload"]);
2196
+ await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
2197
+ await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
2198
+ }
2199
+ async function linuxUninstall() {
2026
2200
  try {
2027
- await fs3.access(p);
2028
- return true;
2201
+ await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
2202
+ } catch {
2203
+ }
2204
+ try {
2205
+ await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
2206
+ } catch {
2207
+ }
2208
+ try {
2209
+ await unlink6(unitPath());
2210
+ } catch {
2211
+ }
2212
+ try {
2213
+ await exec("systemctl", ["--user", "daemon-reload"]);
2029
2214
  } catch {
2030
- return false;
2031
2215
  }
2032
2216
  }
2033
- var init_toolchain_installer = __esm({
2034
- "../listener/dist/lsp/toolchain-installer.js"() {
2035
- "use strict";
2036
- init_src();
2217
+ async function linuxIsInstalled() {
2218
+ try {
2219
+ const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
2220
+ return stdout.trim() === "enabled";
2221
+ } catch {
2222
+ return false;
2037
2223
  }
2038
- });
2039
-
2040
- // ../listener/dist/lsp/installer.js
2041
- import { promises as fs4 } from "fs";
2042
- import { join as join18, dirname as dirname6 } from "path";
2043
- import { spawn as spawn7 } from "child_process";
2044
- import lockfile3 from "proper-lockfile";
2045
- function getLspInstallDir() {
2046
- return join18(getConfigDir(), "lsp-servers");
2047
2224
  }
2048
- function getLspBinDir() {
2049
- return join18(getLspInstallDir(), "node_modules", ".bin");
2225
+ async function win32Install() {
2226
+ await mkdir8(logDir(), { recursive: true });
2227
+ const cmd = resolveListenerCommand();
2228
+ const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
2229
+ await exec("schtasks", [
2230
+ "/create",
2231
+ "/tn",
2232
+ TASK_NAME,
2233
+ "/tr",
2234
+ taskCmd,
2235
+ "/sc",
2236
+ "onlogon",
2237
+ "/ru",
2238
+ process.env.USERNAME ?? "",
2239
+ "/f"
2240
+ ]);
2241
+ await exec("schtasks", ["/run", "/tn", TASK_NAME]);
2050
2242
  }
2051
- async function ensureNpmLspInstalled(config2) {
2052
- if (!config2.npmPackage)
2053
- return { installed: false, skipped: "no-npm-package" };
2054
- const installDir = getLspInstallDir();
2055
- const binPath = join18(installDir, "node_modules", ".bin", config2.command);
2056
- if (await pathExists4(binPath)) {
2057
- return { installed: false, skipped: "already-present" };
2243
+ async function win32Uninstall() {
2244
+ try {
2245
+ await exec("schtasks", ["/end", "/tn", TASK_NAME]);
2246
+ } catch {
2058
2247
  }
2059
2248
  try {
2060
- await fs4.mkdir(installDir, { recursive: true });
2061
- let release = null;
2062
- try {
2063
- release = await lockfile3.lock(installDir, {
2064
- stale: 18e4,
2065
- retries: { retries: 20, factor: 1.5, minTimeout: 500, maxTimeout: 5e3 },
2066
- realpath: false
2067
- });
2068
- } catch {
2069
- if (await pathExists4(binPath)) {
2070
- return { installed: false, skipped: "already-present" };
2071
- }
2072
- }
2073
- try {
2074
- if (await pathExists4(binPath)) {
2075
- return { installed: false, skipped: "already-present" };
2076
- }
2077
- const pkgJsonPath = join18(installDir, "package.json");
2078
- if (!await pathExists4(pkgJsonPath)) {
2079
- await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
2080
- }
2081
- await runNpmInstall(installDir, config2.npmPackage);
2082
- if (!await pathExists4(binPath)) {
2083
- return {
2084
- installed: false,
2085
- error: `npm install completed but ${config2.command} still not at ${binPath}`
2086
- };
2087
- }
2088
- return { installed: true };
2089
- } finally {
2090
- if (release) {
2091
- try {
2092
- await release();
2093
- } catch {
2094
- }
2095
- }
2096
- }
2097
- } catch (err) {
2098
- return {
2099
- installed: false,
2100
- error: err instanceof Error ? err.message : String(err)
2101
- };
2249
+ await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
2250
+ } catch {
2102
2251
  }
2103
2252
  }
2104
- async function resolveNpmCommand() {
2105
- const nodeDir = dirname6(process.execPath);
2106
- for (const candidate of [join18(nodeDir, "npm"), join18(nodeDir, "npm.cmd")]) {
2107
- try {
2108
- await fs4.access(candidate);
2109
- return candidate;
2110
- } catch {
2111
- }
2253
+ async function win32IsInstalled() {
2254
+ try {
2255
+ await exec("schtasks", ["/query", "/tn", TASK_NAME]);
2256
+ return true;
2257
+ } catch {
2258
+ return false;
2112
2259
  }
2113
- for (const candidate of ["/opt/homebrew/bin/npm", "/usr/local/bin/npm", "/usr/bin/npm"]) {
2114
- try {
2115
- await fs4.access(candidate);
2116
- return candidate;
2117
- } catch {
2118
- }
2260
+ }
2261
+ async function darwinStop() {
2262
+ try {
2263
+ await exec("launchctl", ["unload", plistPath()]);
2264
+ } catch {
2119
2265
  }
2120
- return "npm";
2121
2266
  }
2122
- async function runNpmInstall(cwd, pkg2) {
2123
- const npmCmd = await resolveNpmCommand();
2124
- const nodeDir = dirname6(process.execPath);
2125
- const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
2126
- return new Promise((resolve5, reject) => {
2127
- const child = spawn7(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
2128
- cwd,
2129
- stdio: ["ignore", "pipe", "pipe"],
2130
- env: { ...process.env, PATH: augmentedPath }
2131
- });
2132
- let stderr = "";
2133
- child.stderr.on("data", (chunk) => {
2134
- stderr += chunk.toString();
2135
- });
2136
- child.on("error", (err) => reject(err));
2137
- child.on("close", (code) => {
2138
- if (code === 0) {
2139
- resolve5();
2140
- } else {
2141
- reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
2142
- }
2143
- });
2144
- });
2267
+ async function linuxStop() {
2268
+ await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
2145
2269
  }
2146
- function sanitizeNpmStderr(raw) {
2147
- const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
2148
- return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
2270
+ async function win32Stop() {
2271
+ await exec("schtasks", ["/end", "/tn", TASK_NAME]);
2149
2272
  }
2150
- async function pathExists4(p) {
2273
+ async function darwinIsServiceRunning() {
2151
2274
  try {
2152
- await fs4.access(p);
2153
- return true;
2275
+ const stdout = await exec("launchctl", ["list", PLIST_LABEL]);
2276
+ const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
2277
+ return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
2154
2278
  } catch {
2155
2279
  return false;
2156
2280
  }
2157
2281
  }
2158
- async function detectRepoLanguages(repoRoot) {
2159
- const found = /* @__PURE__ */ new Set();
2160
- let inspected = 0;
2161
- const MAX_INSPECT = 200;
2162
- async function inspect(dir) {
2163
- if (inspected >= MAX_INSPECT)
2164
- return;
2165
- let entries;
2166
- try {
2167
- entries = await fs4.readdir(dir);
2168
- } catch {
2169
- return;
2170
- }
2171
- for (const name of entries) {
2172
- if (inspected >= MAX_INSPECT)
2173
- return;
2174
- if (name.startsWith("."))
2175
- continue;
2176
- if (name === "node_modules" || name === "dist" || name === "build")
2177
- continue;
2178
- const lang = detectLanguage(name);
2179
- if (lang) {
2180
- found.add(lang);
2181
- inspected += 1;
2182
- }
2183
- }
2282
+ async function linuxIsServiceRunning() {
2283
+ try {
2284
+ const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
2285
+ return stdout.trim() === "active";
2286
+ } catch {
2287
+ return false;
2184
2288
  }
2185
- await inspect(repoRoot);
2186
- let topEntries = [];
2289
+ }
2290
+ async function win32IsServiceRunning() {
2187
2291
  try {
2188
- topEntries = await fs4.readdir(repoRoot);
2292
+ const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
2293
+ return stdout.includes("Running");
2189
2294
  } catch {
2190
- return found;
2295
+ return false;
2191
2296
  }
2192
- for (const name of topEntries) {
2193
- if (name.startsWith("."))
2194
- continue;
2195
- if (name === "node_modules" || name === "dist" || name === "build")
2196
- continue;
2197
- const sub = join18(repoRoot, name);
2198
- try {
2199
- const stat8 = await fs4.stat(sub);
2200
- if (stat8.isDirectory())
2201
- await inspect(sub);
2202
- } catch {
2203
- }
2297
+ }
2298
+ async function stopService() {
2299
+ switch (process.platform) {
2300
+ case "darwin":
2301
+ await darwinStop();
2302
+ break;
2303
+ case "linux":
2304
+ await linuxStop();
2305
+ break;
2306
+ case "win32":
2307
+ await win32Stop();
2308
+ break;
2309
+ default:
2310
+ throw new Error(`Unsupported platform: ${process.platform}`);
2311
+ }
2312
+ }
2313
+ async function install() {
2314
+ switch (process.platform) {
2315
+ case "darwin":
2316
+ await darwinInstall();
2317
+ break;
2318
+ case "linux":
2319
+ await linuxInstall();
2320
+ break;
2321
+ case "win32":
2322
+ await win32Install();
2323
+ break;
2324
+ default:
2325
+ throw new Error(`Unsupported platform: ${process.platform}`);
2204
2326
  }
2205
- return found;
2206
2327
  }
2207
- async function prepareLspServersForRepos(repos, options = {}) {
2208
- const detected = /* @__PURE__ */ new Set();
2209
- for (const r of repos) {
2210
- if (!r.localPath)
2211
- continue;
2212
- try {
2213
- const stat8 = await fs4.stat(r.localPath);
2214
- if (!stat8.isDirectory())
2215
- continue;
2216
- } catch {
2217
- continue;
2218
- }
2219
- try {
2220
- const langs = await detectRepoLanguages(r.localPath);
2221
- for (const l of langs)
2222
- detected.add(l);
2223
- } catch {
2224
- }
2328
+ async function uninstall() {
2329
+ switch (process.platform) {
2330
+ case "darwin":
2331
+ await darwinUninstall();
2332
+ break;
2333
+ case "linux":
2334
+ await linuxUninstall();
2335
+ break;
2336
+ case "win32":
2337
+ await win32Uninstall();
2338
+ break;
2339
+ default:
2340
+ throw new Error(`Unsupported platform: ${process.platform}`);
2225
2341
  }
2226
- const results = [];
2227
- for (const language of detected) {
2228
- const configs = getEffectiveConfigList(language, process.env, options.lspOverrides);
2229
- const npmConfig = configs.find((c) => c.npmPackage);
2230
- if (npmConfig) {
2231
- const outcome = await ensureNpmLspInstalled(npmConfig);
2232
- results.push({
2233
- language,
2234
- installed: outcome.installed,
2235
- alreadyPresent: outcome.skipped === "already-present",
2236
- skippedNoNpmPackage: false,
2237
- installMethod: "npm",
2238
- error: outcome.error
2239
- });
2240
- continue;
2241
- }
2242
- const nativeConfig = configs.find((c) => c.nativeInstallKey);
2243
- if (nativeConfig?.nativeInstallKey) {
2244
- const outcome = await ensureNativeLspInstalled(nativeConfig.nativeInstallKey);
2245
- results.push({
2246
- language,
2247
- installed: outcome.installed,
2248
- alreadyPresent: outcome.alreadyPresent,
2249
- skippedNoNpmPackage: false,
2250
- installMethod: "native",
2251
- skipped: outcome.skipped,
2252
- hint: outcome.skipped ? nativeConfig.installHint : void 0,
2253
- error: outcome.error
2254
- });
2255
- continue;
2256
- }
2257
- const coursierConfig = configs.find((c) => c.coursierInstallKey);
2258
- if (coursierConfig?.coursierInstallKey) {
2259
- const outcome = await ensureCoursierLspInstalled(coursierConfig.coursierInstallKey);
2260
- results.push({
2261
- language,
2262
- installed: outcome.installed,
2263
- alreadyPresent: outcome.alreadyPresent,
2264
- skippedNoNpmPackage: false,
2265
- installMethod: "coursier",
2266
- skipped: outcome.skipped,
2267
- hint: outcome.skipped ? coursierConfig.installHint : void 0,
2268
- error: outcome.error
2269
- });
2270
- continue;
2271
- }
2272
- const toolchainConfig = configs.find((c) => c.toolchainInstallKey);
2273
- if (toolchainConfig?.toolchainInstallKey) {
2274
- const outcome = await ensureToolchainLspInstalled(toolchainConfig.toolchainInstallKey);
2275
- results.push({
2276
- language,
2277
- installed: outcome.installed,
2278
- alreadyPresent: outcome.alreadyPresent,
2279
- skippedNoNpmPackage: false,
2280
- installMethod: "toolchain",
2281
- skipped: outcome.skipped,
2282
- // Surface the install hint for any skip reason — toolchain
2283
- // missing means user needs to install it; bundled SDK means
2284
- // user needs the SDK.
2285
- hint: outcome.skipped ? toolchainConfig.installHint : void 0,
2286
- error: outcome.error
2287
- });
2288
- continue;
2289
- }
2290
- results.push({
2291
- language,
2292
- installed: false,
2293
- alreadyPresent: false,
2294
- skippedNoNpmPackage: true,
2295
- hint: configs[0]?.installHint
2296
- });
2342
+ }
2343
+ async function isInstalled() {
2344
+ switch (process.platform) {
2345
+ case "darwin":
2346
+ return darwinIsInstalled();
2347
+ case "linux":
2348
+ return linuxIsInstalled();
2349
+ case "win32":
2350
+ return win32IsInstalled();
2351
+ default:
2352
+ return false;
2297
2353
  }
2298
- return results;
2299
2354
  }
2300
- var init_installer = __esm({
2301
- "../listener/dist/lsp/installer.js"() {
2355
+ async function isServiceRunning() {
2356
+ switch (process.platform) {
2357
+ case "darwin":
2358
+ return darwinIsServiceRunning();
2359
+ case "linux":
2360
+ return linuxIsServiceRunning();
2361
+ case "win32":
2362
+ return win32IsServiceRunning();
2363
+ default:
2364
+ return false;
2365
+ }
2366
+ }
2367
+ var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
2368
+ var init_service_installer = __esm({
2369
+ "../listener/dist/service-installer.js"() {
2302
2370
  "use strict";
2303
2371
  init_config_dir();
2304
- init_registry();
2305
- init_native_installer();
2306
- init_coursier_installer();
2307
- init_toolchain_installer();
2372
+ init_process_manager();
2373
+ init_daemon_path();
2374
+ IS_STAGING = true ? true : false;
2375
+ PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
2376
+ SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
2377
+ TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
2308
2378
  }
2309
2379
  });
2310
2380
 
@@ -2316,7 +2386,7 @@ __export(sidecar_cache_exports, {
2316
2386
  sidecarCachePaths: () => sidecarCachePaths
2317
2387
  });
2318
2388
  import { mkdir as mkdir9, readFile as readFile8, readdir as readdir3, stat as stat2, unlink as unlink8, writeFile as writeFile9 } from "fs/promises";
2319
- import { dirname as dirname8, join as join19 } from "path";
2389
+ import { dirname as dirname9, join as join20 } from "path";
2320
2390
  function repoIdSafe(repoId) {
2321
2391
  return /^[A-Za-z0-9_.-]{1,128}$/.test(repoId) && !repoId.startsWith(".");
2322
2392
  }
@@ -2330,8 +2400,8 @@ function sidecarCachePaths(repoId, commitSha) {
2330
2400
  if (!commitShaSafe(commitSha)) {
2331
2401
  throw new Error(`unsafe commitSha: ${commitSha}`);
2332
2402
  }
2333
- const repoDir = join19(getConfigDir(), "typed-resolution", repoId);
2334
- const fullPath = join19(repoDir, `${commitSha}.jsonl`);
2403
+ const repoDir = join20(getConfigDir(), "typed-resolution", repoId);
2404
+ const fullPath = join20(repoDir, `${commitSha}.jsonl`);
2335
2405
  return { fullPath, repoDir };
2336
2406
  }
2337
2407
  async function persistMergedSidecar(repoId, commitSha, sidecar) {
@@ -2354,7 +2424,7 @@ async function sweepSidecarDir(repoDir) {
2354
2424
  const withMtime = [];
2355
2425
  for (const name of jsonlFiles) {
2356
2426
  try {
2357
- const s = await stat2(join19(repoDir, name));
2427
+ const s = await stat2(join20(repoDir, name));
2358
2428
  withMtime.push({ name, mtimeMs: s.mtimeMs });
2359
2429
  } catch {
2360
2430
  }
@@ -2365,7 +2435,7 @@ async function sweepSidecarDir(repoDir) {
2365
2435
  for (const { name, mtimeMs } of candidates) {
2366
2436
  if (now - mtimeMs >= SWEEP_MAX_AGE_MS) {
2367
2437
  try {
2368
- await unlink8(join19(repoDir, name));
2438
+ await unlink8(join20(repoDir, name));
2369
2439
  } catch {
2370
2440
  }
2371
2441
  }
@@ -2469,7 +2539,10 @@ async function uploadSidecar(req, fetchImpl = fetch) {
2469
2539
  throw new Error("upload-url response missing `data.url`");
2470
2540
  const putRes = await fetchImpl(presignedUrl, {
2471
2541
  method: "PUT",
2472
- headers: { "content-type": "application/json" },
2542
+ headers: {
2543
+ "content-type": "application/json",
2544
+ "x-amz-server-side-encryption": "aws:kms"
2545
+ },
2473
2546
  body
2474
2547
  });
2475
2548
  if (!putRes.ok) {
@@ -3060,13 +3133,13 @@ var init_telemetry = __esm({
3060
3133
  // bin/repowise.ts
3061
3134
  import { readFileSync as readFileSync3 } from "fs";
3062
3135
  import { fileURLToPath as fileURLToPath4 } from "url";
3063
- import { dirname as dirname21, join as join60 } from "path";
3136
+ import { dirname as dirname23, join as join61 } from "path";
3064
3137
  import { Command } from "commander";
3065
3138
 
3066
3139
  // ../listener/dist/main.js
3067
3140
  init_config_dir();
3068
3141
  import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir16, stat as fsStat } from "fs/promises";
3069
- import { join as join40, dirname as dirname15 } from "path";
3142
+ import { join as join41, dirname as dirname17 } from "path";
3070
3143
  import { fileURLToPath as fileURLToPath3 } from "url";
3071
3144
  import lockfile4 from "proper-lockfile";
3072
3145
 
@@ -4877,7 +4950,7 @@ init_config_dir();
4877
4950
  init_process_manager();
4878
4951
  init_service_installer();
4879
4952
  import { unlink as unlink7 } from "fs/promises";
4880
- import { join as join14 } from "path";
4953
+ import { join as join19 } from "path";
4881
4954
  async function getListenerStatus() {
4882
4955
  const pid = await readPid();
4883
4956
  if (pid !== null) {
@@ -4886,7 +4959,7 @@ async function getListenerStatus() {
4886
4959
  return { running: true, method: "pid", pid, serviceInstalled: serviceInstalled2 };
4887
4960
  }
4888
4961
  try {
4889
- await unlink7(join14(getConfigDir(), "listener.pid"));
4962
+ await unlink7(join19(getConfigDir(), "listener.pid"));
4890
4963
  } catch {
4891
4964
  }
4892
4965
  }
@@ -4931,15 +5004,15 @@ async function stopListener() {
4931
5004
 
4932
5005
  // ../listener/dist/mcp/bootstrap.js
4933
5006
  init_config_dir();
4934
- import { join as join22 } from "path";
5007
+ import { join as join23 } from "path";
4935
5008
 
4936
5009
  // ../listener/dist/lsp/workspace-session.js
4937
5010
  import { pathToFileURL } from "url";
4938
5011
  import { readFile as readFile7 } from "fs/promises";
4939
- import { resolve as pathResolve2, sep as pathSep, dirname as dirname7 } from "path";
5012
+ import { resolve as pathResolve2, sep as pathSep, dirname as dirname8, delimiter as delimiter2 } from "path";
4940
5013
 
4941
5014
  // ../listener/dist/lsp/lsp-client.js
4942
- import { spawn as spawn2 } from "child_process";
5015
+ import { spawn as spawn7 } from "child_process";
4943
5016
  import { EventEmitter, once } from "events";
4944
5017
  import { Buffer as Buffer2 } from "buffer";
4945
5018
  var LspTimeoutError = class extends Error {
@@ -4984,7 +5057,7 @@ var LspClient = class extends EventEmitter {
4984
5057
  closed = false;
4985
5058
  constructor(options) {
4986
5059
  super();
4987
- this.child = options.spawn ? options.spawn() : spawn2(options.command, [...options.args ?? []], {
5060
+ this.child = options.spawn ? options.spawn() : spawn7(options.command, [...options.args ?? []], {
4988
5061
  cwd: options.cwd,
4989
5062
  env: options.env ?? process.env,
4990
5063
  stdio: ["pipe", "pipe", "pipe"]
@@ -5207,10 +5280,7 @@ var LspClient = class extends EventEmitter {
5207
5280
  };
5208
5281
 
5209
5282
  // ../listener/dist/lsp/workspace-session.js
5210
- init_installer();
5211
- init_native_installer();
5212
- init_coursier_installer();
5213
- init_toolchain_installer();
5283
+ init_daemon_path();
5214
5284
 
5215
5285
  // ../listener/dist/lsp/bloop-lifecycle.js
5216
5286
  import { spawn as spawn8 } from "child_process";
@@ -5270,13 +5340,7 @@ var WorkspaceManager = class {
5270
5340
  this.rssSampler = opts.rssSampler ?? (() => process.memoryUsage().rss);
5271
5341
  this.clientFactory = opts.clientFactory ?? ((config2, cwd) => {
5272
5342
  const env = { ...process.env };
5273
- const lspBinDirs = [
5274
- getLspBinDir(),
5275
- getNativeLspBinDir(),
5276
- getCoursierBinDir(),
5277
- ...getToolchainBinDirs()
5278
- ];
5279
- env.PATH = buildLspSpawnPath(env.PATH, dirname7(process.execPath), lspBinDirs);
5343
+ env.PATH = buildLspSpawnPath(env.PATH, dirname8(process.execPath), managedBinDirs());
5280
5344
  const client = new LspClient({
5281
5345
  command: config2.command,
5282
5346
  args: config2.args,
@@ -5645,14 +5709,19 @@ var PathEscapeError = class extends Error {
5645
5709
  }
5646
5710
  };
5647
5711
  function buildLspSpawnPath(basePath, nodeDir, lspBinDirs) {
5648
- const extra = lspBinDirs.join(":");
5649
- return basePath ? `${nodeDir}:${basePath}:${extra}` : `${nodeDir}:${extra}`;
5712
+ const parts = [
5713
+ nodeDir,
5714
+ ...basePath ? basePath.split(delimiter2) : [],
5715
+ ...commonBinDirs(),
5716
+ ...lspBinDirs
5717
+ ];
5718
+ return [...new Set(parts.filter((d) => d.length > 0))].join(delimiter2);
5650
5719
  }
5651
5720
 
5652
5721
  // ../listener/dist/mcp/graph-cache.js
5653
5722
  init_config_dir();
5654
5723
  import { readFile as readFile9, stat as stat3 } from "fs/promises";
5655
- import { join as join20 } from "path";
5724
+ import { join as join21 } from "path";
5656
5725
  var EVICT_DEBOUNCE_MS = 6e4;
5657
5726
  function assertSafeRepoId(repoId) {
5658
5727
  if (!repoId || typeof repoId !== "string") {
@@ -5667,7 +5736,7 @@ function assertSafeRepoId(repoId) {
5667
5736
  }
5668
5737
  function createGraphCache(options = {}) {
5669
5738
  const evictMs = options.evictDebounceMs ?? EVICT_DEBOUNCE_MS;
5670
- const resolvePath = options.resolveGraphPath ?? ((repoId) => join20(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5739
+ const resolvePath = options.resolveGraphPath ?? ((repoId) => join21(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5671
5740
  const entries = /* @__PURE__ */ new Map();
5672
5741
  const inFlight = /* @__PURE__ */ new Map();
5673
5742
  async function loadFromDisk(repoId) {
@@ -5761,7 +5830,7 @@ function defaultRepoWiseHome() {
5761
5830
 
5762
5831
  // ../listener/dist/mcp/graph-downloader.js
5763
5832
  import { mkdir as mkdir10, rename as rename3, writeFile as writeFile10 } from "fs/promises";
5764
- import { dirname as dirname9 } from "path";
5833
+ import { dirname as dirname10 } from "path";
5765
5834
  function createGraphDownloader(options) {
5766
5835
  const fetchFn = options.fetchImpl ?? fetch;
5767
5836
  let lastSha = null;
@@ -5812,7 +5881,7 @@ function createGraphDownloader(options) {
5812
5881
  message: `graph parse: ${err instanceof Error ? err.message : String(err)}`
5813
5882
  };
5814
5883
  }
5815
- await mkdir10(dirname9(options.targetPath), { recursive: true });
5884
+ await mkdir10(dirname10(options.targetPath), { recursive: true });
5816
5885
  const tmpPath = `${options.targetPath}.${Date.now()}.tmp`;
5817
5886
  await writeFile10(tmpPath, body, { encoding: "utf-8", mode: 384 });
5818
5887
  await rename3(tmpPath, options.targetPath);
@@ -5838,7 +5907,7 @@ function createGraphDownloader(options) {
5838
5907
  // ../listener/dist/mcp/log-encryption.js
5839
5908
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
5840
5909
  import { mkdir as mkdir11, readFile as readFile10, stat as stat4, writeFile as writeFile11 } from "fs/promises";
5841
- import { dirname as dirname10 } from "path";
5910
+ import { dirname as dirname11 } from "path";
5842
5911
  var KEY_BYTES = 32;
5843
5912
  var IV_BYTES = 12;
5844
5913
  var TAG_BYTES = 16;
@@ -5885,7 +5954,7 @@ function createFileKeyStore(keyPath) {
5885
5954
  return parsed;
5886
5955
  }
5887
5956
  const fresh = randomBytes(KEY_BYTES);
5888
- await mkdir11(dirname10(keyPath), { recursive: true });
5957
+ await mkdir11(dirname11(keyPath), { recursive: true });
5889
5958
  const tmp = `${keyPath}.tmp`;
5890
5959
  await writeFile11(tmp, fresh.toString("base64") + "\n", {
5891
5960
  encoding: "utf-8",
@@ -5927,7 +5996,7 @@ function decryptLine(encoded, key) {
5927
5996
 
5928
5997
  // ../listener/dist/mcp/mcp-logger.js
5929
5998
  import { appendFile, mkdir as mkdir12, stat as stat5, unlink as unlink9, writeFile as writeFile12, readFile as readFile11 } from "fs/promises";
5930
- import { dirname as dirname11 } from "path";
5999
+ import { dirname as dirname12 } from "path";
5931
6000
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
5932
6001
  function createMcpLogger(options) {
5933
6002
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
@@ -5950,7 +6019,7 @@ function createMcpLogger(options) {
5950
6019
  await migrateLegacyPlaintext();
5951
6020
  const key = await options.keyStore.getKey();
5952
6021
  const encoded = encryptLine(JSON.stringify(entry), key) + "\n";
5953
- await mkdir12(dirname11(options.filePath), { recursive: true });
6022
+ await mkdir12(dirname12(options.filePath), { recursive: true });
5954
6023
  await appendFile(options.filePath, encoded, { encoding: "utf-8", mode: 384 });
5955
6024
  try {
5956
6025
  const s = await stat5(options.filePath);
@@ -5984,7 +6053,7 @@ async function rotate(path, size) {
5984
6053
  // ../listener/dist/mcp/mcp-log-uploader.js
5985
6054
  import { randomUUID } from "crypto";
5986
6055
  import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir13 } from "fs/promises";
5987
- import { dirname as dirname12 } from "path";
6056
+ import { dirname as dirname13 } from "path";
5988
6057
  var DEFAULT_MAX_ENTRIES = 5e3;
5989
6058
  var DEFAULT_MAX_BYTES2 = 1 * 1024 * 1024;
5990
6059
  function createMcpLogUploader(options) {
@@ -6134,7 +6203,7 @@ async function readPendingBatch(watermarkPath) {
6134
6203
  }
6135
6204
  }
6136
6205
  async function writePendingBatch(watermarkPath, batchId) {
6137
- await mkdir13(dirname12(watermarkPath), { recursive: true });
6206
+ await mkdir13(dirname13(watermarkPath), { recursive: true });
6138
6207
  await writeFile13(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
6139
6208
  }
6140
6209
  async function clearPendingBatch(watermarkPath) {
@@ -6156,7 +6225,7 @@ async function readWatermark(path) {
6156
6225
  }
6157
6226
  }
6158
6227
  async function writeWatermark(path, offset) {
6159
- await mkdir13(dirname12(path), { recursive: true });
6228
+ await mkdir13(dirname13(path), { recursive: true });
6160
6229
  const tmp = `${path}.tmp`;
6161
6230
  await writeFile13(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
6162
6231
  const { rename: rename5 } = await import("fs/promises");
@@ -6176,7 +6245,7 @@ async function isLocallyConsented(flagPath2) {
6176
6245
  init_config_dir();
6177
6246
  import { createServer } from "http";
6178
6247
  import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
6179
- import { dirname as dirname13, join as join21 } from "path";
6248
+ import { dirname as dirname14, join as join22 } from "path";
6180
6249
  import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
6181
6250
 
6182
6251
  // ../listener/dist/mcp/sanitize.js
@@ -7694,7 +7763,7 @@ async function readJson(req) {
7694
7763
  }
7695
7764
  }
7696
7765
  async function writeEndpointFile(path, endpoint, secret) {
7697
- await mkdir14(dirname13(path), { recursive: true });
7766
+ await mkdir14(dirname14(path), { recursive: true });
7698
7767
  await writeFile14(path, formatEndpointFile({ endpoint, secret }), {
7699
7768
  encoding: "utf-8",
7700
7769
  mode: 384
@@ -7708,7 +7777,7 @@ function extractBearer(header) {
7708
7777
  return m ? m[1] : null;
7709
7778
  }
7710
7779
  function defaultEndpointFile() {
7711
- return join21(getConfigDir(), "listener.endpoint");
7780
+ return join22(getConfigDir(), "listener.endpoint");
7712
7781
  }
7713
7782
 
7714
7783
  // ../listener/dist/mcp/bootstrap.js
@@ -7716,7 +7785,7 @@ async function startMcp(options) {
7716
7785
  const disabled = process.env.REPOWISE_MCP_DISABLED === "true";
7717
7786
  const graphsDir = options.graphsDir ?? defaultGraphsDir();
7718
7787
  const graphCache = createGraphCache({
7719
- resolveGraphPath: (repoId) => join22(graphsDir, `${repoId}.json`)
7788
+ resolveGraphPath: (repoId) => join23(graphsDir, `${repoId}.json`)
7720
7789
  });
7721
7790
  if (disabled) {
7722
7791
  return {
@@ -7739,12 +7808,12 @@ async function startMcp(options) {
7739
7808
  }
7740
7809
  const firstRepoLocal = options.repos.find((r) => r.localPath)?.localPath;
7741
7810
  const mcpHome = defaultMcpHome();
7742
- const logFilePath = join22(mcpHome, "mcp-log.jsonl.enc");
7743
- const keyStore = createFileKeyStore(join22(mcpHome, "mcp-log.key"));
7811
+ const logFilePath = join23(mcpHome, "mcp-log.jsonl.enc");
7812
+ const keyStore = createFileKeyStore(join23(mcpHome, "mcp-log.key"));
7744
7813
  const mcpLogger = createMcpLogger({ filePath: logFilePath, keyStore });
7745
- const flagFilePath = join22(mcpHome, "mcp-log.flag");
7746
- const watermarkFilePath = join22(mcpHome, "mcp-log.watermark");
7747
- const consentSentMarkerPath = join22(mcpHome, "mcp-log.consent-sent");
7814
+ const flagFilePath = join23(mcpHome, "mcp-log.flag");
7815
+ const watermarkFilePath = join23(mcpHome, "mcp-log.watermark");
7816
+ const consentSentMarkerPath = join23(mcpHome, "mcp-log.consent-sent");
7748
7817
  let uploader = null;
7749
7818
  let lastConsentState = false;
7750
7819
  let serverConsentEnsuredThisProcess = false;
@@ -7791,7 +7860,7 @@ async function startMcp(options) {
7791
7860
  apiBaseUrl: repo.apiUrl,
7792
7861
  getAuthToken: options.getAuthToken,
7793
7862
  repoId: repo.repoId,
7794
- targetPath: join22(graphsDir, `${repo.repoId}.json`),
7863
+ targetPath: join23(graphsDir, `${repo.repoId}.json`),
7795
7864
  graphCache,
7796
7865
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7797
7866
  }));
@@ -7876,7 +7945,7 @@ async function startMcp(options) {
7876
7945
  apiBaseUrl: repo.apiUrl,
7877
7946
  getAuthToken: options.getAuthToken,
7878
7947
  repoId: repo.repoId,
7879
- targetPath: join22(graphsDir, `${repo.repoId}.json`),
7948
+ targetPath: join23(graphsDir, `${repo.repoId}.json`),
7880
7949
  graphCache,
7881
7950
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7882
7951
  }));
@@ -7897,7 +7966,7 @@ async function startMcp(options) {
7897
7966
  };
7898
7967
  }
7899
7968
  function defaultGraphsDir() {
7900
- return join22(getConfigDir(), "graphs");
7969
+ return join23(getConfigDir(), "graphs");
7901
7970
  }
7902
7971
  function defaultMcpHome() {
7903
7972
  return getConfigDir();
@@ -7950,16 +8019,16 @@ async function ensureServerConsent(opts) {
7950
8019
  }
7951
8020
 
7952
8021
  // ../listener/dist/mcp/auto-config/index.js
7953
- import { homedir as homedir5 } from "os";
8022
+ import { homedir as homedir6 } from "os";
7954
8023
  import { basename as basename3 } from "path";
7955
8024
 
7956
8025
  // ../listener/dist/mcp/auto-config/writers/claude-code.js
7957
8026
  import { promises as fs6 } from "fs";
7958
- import { join as join23 } from "path";
8027
+ import { join as join24 } from "path";
7959
8028
 
7960
8029
  // ../listener/dist/mcp/auto-config/markers.js
7961
8030
  import { promises as fs5 } from "fs";
7962
- import { dirname as dirname14 } from "path";
8031
+ import { dirname as dirname15 } from "path";
7963
8032
  async function writeMergedConfig(params) {
7964
8033
  const current = await readConfig(params.path);
7965
8034
  const servers = { ...current.mcpServers ?? {} };
@@ -7969,7 +8038,7 @@ async function writeMergedConfig(params) {
7969
8038
  const existing = safeExistingContent(params.path, current);
7970
8039
  if (existing === canonical)
7971
8040
  return "unchanged";
7972
- await fs5.mkdir(dirname14(params.path), { recursive: true });
8041
+ await fs5.mkdir(dirname15(params.path), { recursive: true });
7973
8042
  await fs5.writeFile(params.path, canonical, "utf-8");
7974
8043
  return "written";
7975
8044
  }
@@ -8035,10 +8104,10 @@ function safeExistingContent(path, current) {
8035
8104
  var claudeCodeWriter = {
8036
8105
  tool: "claude-code",
8037
8106
  async detect(repoRoot, home) {
8038
- return await fileExists2(join23(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
8107
+ return await fileExists2(join24(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
8039
8108
  },
8040
8109
  async write(ctx) {
8041
- const path = join23(ctx.repoRoot, ".mcp.json");
8110
+ const path = join24(ctx.repoRoot, ".mcp.json");
8042
8111
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8043
8112
  const status2 = await writeMergedConfig({
8044
8113
  path,
@@ -8048,7 +8117,7 @@ var claudeCodeWriter = {
8048
8117
  return { status: status2, path };
8049
8118
  },
8050
8119
  async remove(ctx) {
8051
- const path = join23(ctx.repoRoot, ".mcp.json");
8120
+ const path = join24(ctx.repoRoot, ".mcp.json");
8052
8121
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8053
8122
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8054
8123
  }
@@ -8062,20 +8131,20 @@ async function fileExists2(path) {
8062
8131
  }
8063
8132
  }
8064
8133
  async function hasClaudeBinary(home) {
8065
- return fileExists2(join23(home, ".claude", "claude.json"));
8134
+ return fileExists2(join24(home, ".claude", "claude.json"));
8066
8135
  }
8067
8136
 
8068
8137
  // ../listener/dist/mcp/auto-config/writers/claude-code-hooks.js
8069
8138
  import { promises as fs7 } from "fs";
8070
- import { join as join24 } from "path";
8139
+ import { join as join25 } from "path";
8071
8140
  var claudeCodeHooksWriter = {
8072
8141
  tool: "claude-code-hooks",
8073
8142
  async detect(repoRoot, home) {
8074
- return await fileExists3(join24(repoRoot, "CLAUDE.md")) || await fileExists3(join24(home, ".claude.json"));
8143
+ return await fileExists3(join25(repoRoot, "CLAUDE.md")) || await fileExists3(join25(home, ".claude.json"));
8075
8144
  },
8076
8145
  async write(ctx) {
8077
8146
  const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context", ctx.variant);
8078
- return { status: result.status, path: join24(ctx.repoRoot, result.relPath) };
8147
+ return { status: result.status, path: join25(ctx.repoRoot, result.relPath) };
8079
8148
  },
8080
8149
  async remove(ctx) {
8081
8150
  await removeClaudeHooksFromRepo(ctx.repoRoot);
@@ -8092,14 +8161,14 @@ async function fileExists3(path) {
8092
8161
 
8093
8162
  // ../listener/dist/mcp/auto-config/writers/cline.js
8094
8163
  import { promises as fs8 } from "fs";
8095
- import { join as join25 } from "path";
8164
+ import { join as join26 } from "path";
8096
8165
  var clineWriter = {
8097
8166
  tool: "cline",
8098
8167
  async detect(repoRoot, home) {
8099
- return await fileExists4(join25(repoRoot, ".clinerules")) || await fileExists4(join25(home, ".cline"));
8168
+ return await fileExists4(join26(repoRoot, ".clinerules")) || await fileExists4(join26(home, ".cline"));
8100
8169
  },
8101
8170
  async write(ctx) {
8102
- const path = join25(ctx.home, ".cline", "mcp.json");
8171
+ const path = join26(ctx.home, ".cline", "mcp.json");
8103
8172
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8104
8173
  const status2 = await writeMergedConfig({
8105
8174
  path,
@@ -8109,7 +8178,7 @@ var clineWriter = {
8109
8178
  return { status: status2, path };
8110
8179
  },
8111
8180
  async remove(ctx) {
8112
- const path = join25(ctx.home, ".cline", "mcp.json");
8181
+ const path = join26(ctx.home, ".cline", "mcp.json");
8113
8182
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8114
8183
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8115
8184
  }
@@ -8125,13 +8194,13 @@ async function fileExists4(path) {
8125
8194
 
8126
8195
  // ../listener/dist/mcp/auto-config/writers/cline-hooks.js
8127
8196
  import { promises as fs9 } from "fs";
8128
- import { join as join26 } from "path";
8197
+ import { join as join27 } from "path";
8129
8198
  var SCRIPT_REL = ".clinerules/hooks/UserPromptSubmit";
8130
8199
  var OWNERSHIP_MARKER = "managed by RepoWise";
8131
8200
  var clineHooksWriter = {
8132
8201
  tool: "cline-hooks",
8133
8202
  async detect(repoRoot) {
8134
- return fileExists5(join26(repoRoot, ".clinerules"));
8203
+ return fileExists5(join27(repoRoot, ".clinerules"));
8135
8204
  },
8136
8205
  async write(ctx) {
8137
8206
  if (await isGitTracked(ctx.repoRoot, SCRIPT_REL)) {
@@ -8141,7 +8210,7 @@ var clineHooksWriter = {
8141
8210
  reason: `${SCRIPT_REL} is git-tracked; update it manually to opt in`
8142
8211
  };
8143
8212
  }
8144
- const path = join26(ctx.repoRoot, SCRIPT_REL);
8213
+ const path = join27(ctx.repoRoot, SCRIPT_REL);
8145
8214
  const next = buildClineHookScript({
8146
8215
  repoName: ctx.repoName,
8147
8216
  contextFolder: ctx.contextFolder ?? "repowise-context",
@@ -8158,14 +8227,14 @@ var clineHooksWriter = {
8158
8227
  await ensureExecutable(path);
8159
8228
  return { status: "unchanged", path };
8160
8229
  }
8161
- await fs9.mkdir(join26(ctx.repoRoot, ".clinerules", "hooks"), { recursive: true });
8230
+ await fs9.mkdir(join27(ctx.repoRoot, ".clinerules", "hooks"), { recursive: true });
8162
8231
  await fs9.writeFile(path, next, { encoding: "utf-8", mode: 493 });
8163
8232
  await ensureExecutable(path);
8164
8233
  await applyGitignoreChanges(ctx.repoRoot, { add: [SCRIPT_REL] });
8165
8234
  return { status: "written", path };
8166
8235
  },
8167
8236
  async remove(ctx) {
8168
- const path = join26(ctx.repoRoot, SCRIPT_REL);
8237
+ const path = join27(ctx.repoRoot, SCRIPT_REL);
8169
8238
  const existing = await readOrNull(path);
8170
8239
  if (existing !== null && existing.includes(OWNERSHIP_MARKER)) {
8171
8240
  await fs9.unlink(path);
@@ -8200,14 +8269,14 @@ async function readOrNull(path) {
8200
8269
 
8201
8270
  // ../listener/dist/mcp/auto-config/writers/codex.js
8202
8271
  import { promises as fs10 } from "fs";
8203
- import { join as join27 } from "path";
8272
+ import { join as join28 } from "path";
8204
8273
  var codexWriter = {
8205
8274
  tool: "codex",
8206
8275
  async detect(_repoRoot, home) {
8207
- return fileExists6(join27(home, ".codex"));
8276
+ return fileExists6(join28(home, ".codex"));
8208
8277
  },
8209
8278
  async write(ctx) {
8210
- const path = join27(ctx.home, ".codex", "mcp.json");
8279
+ const path = join28(ctx.home, ".codex", "mcp.json");
8211
8280
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8212
8281
  const status2 = await writeMergedConfig({
8213
8282
  path,
@@ -8217,7 +8286,7 @@ var codexWriter = {
8217
8286
  return { status: status2, path };
8218
8287
  },
8219
8288
  async remove(ctx) {
8220
- const path = join27(ctx.home, ".codex", "mcp.json");
8289
+ const path = join28(ctx.home, ".codex", "mcp.json");
8221
8290
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8222
8291
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8223
8292
  }
@@ -8233,15 +8302,15 @@ async function fileExists6(path) {
8233
8302
 
8234
8303
  // ../listener/dist/mcp/auto-config/writers/codex-hooks.js
8235
8304
  import { promises as fs11 } from "fs";
8236
- import { join as join28 } from "path";
8305
+ import { join as join29 } from "path";
8237
8306
  var HOOKS_REL = ".codex/hooks.json";
8238
8307
  var codexHooksWriter = {
8239
8308
  tool: "codex-hooks",
8240
8309
  async detect(repoRoot, home) {
8241
- return await fileExists7(join28(home, ".codex")) || await fileExists7(join28(repoRoot, ".codex"));
8310
+ return await fileExists7(join29(home, ".codex")) || await fileExists7(join29(repoRoot, ".codex"));
8242
8311
  },
8243
8312
  async write(ctx) {
8244
- const repoSignal = await fileExists7(join28(ctx.repoRoot, ".codex"));
8313
+ const repoSignal = await fileExists7(join29(ctx.repoRoot, ".codex"));
8245
8314
  const selected = ctx.selectedTools?.includes("codex") ?? false;
8246
8315
  if (!repoSignal && !selected) {
8247
8316
  return { status: "skipped", reason: "no repo-scoped Codex signal and not selected" };
@@ -8253,7 +8322,7 @@ var codexHooksWriter = {
8253
8322
  reason: `${HOOKS_REL} is git-tracked; add the RepoWise UserPromptSubmit hook manually to opt in`
8254
8323
  };
8255
8324
  }
8256
- const path = join28(ctx.repoRoot, HOOKS_REL);
8325
+ const path = join29(ctx.repoRoot, HOOKS_REL);
8257
8326
  const existing = await readOrNull2(path);
8258
8327
  const merged = mergeCodexHookSettings(existing, {
8259
8328
  repoName: ctx.repoName,
@@ -8263,13 +8332,13 @@ var codexHooksWriter = {
8263
8332
  if (merged === existing) {
8264
8333
  return { status: "unchanged", path };
8265
8334
  }
8266
- await fs11.mkdir(join28(ctx.repoRoot, ".codex"), { recursive: true });
8335
+ await fs11.mkdir(join29(ctx.repoRoot, ".codex"), { recursive: true });
8267
8336
  await fs11.writeFile(path, merged, "utf-8");
8268
8337
  await applyGitignoreChanges(ctx.repoRoot, { add: [HOOKS_REL] });
8269
8338
  return { status: "written", path };
8270
8339
  },
8271
8340
  async remove(ctx) {
8272
- const path = join28(ctx.repoRoot, HOOKS_REL);
8341
+ const path = join29(ctx.repoRoot, HOOKS_REL);
8273
8342
  const existing = await readOrNull2(path);
8274
8343
  if (existing !== null) {
8275
8344
  const next = removeCodexHookSettings(existing);
@@ -8300,14 +8369,14 @@ async function readOrNull2(path) {
8300
8369
 
8301
8370
  // ../listener/dist/mcp/auto-config/writers/copilot.js
8302
8371
  import { promises as fs12 } from "fs";
8303
- import { join as join29 } from "path";
8372
+ import { join as join30 } from "path";
8304
8373
  var copilotWriter = {
8305
8374
  tool: "copilot",
8306
8375
  async detect(repoRoot) {
8307
- return fileExists8(join29(repoRoot, ".vscode"));
8376
+ return fileExists8(join30(repoRoot, ".vscode"));
8308
8377
  },
8309
8378
  async write(ctx) {
8310
- const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
8379
+ const path = join30(ctx.repoRoot, ".vscode", "mcp.json");
8311
8380
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8312
8381
  const status2 = await writeMergedConfig({
8313
8382
  path,
@@ -8317,7 +8386,7 @@ var copilotWriter = {
8317
8386
  return { status: status2, path };
8318
8387
  },
8319
8388
  async remove(ctx) {
8320
- const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
8389
+ const path = join30(ctx.repoRoot, ".vscode", "mcp.json");
8321
8390
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8322
8391
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8323
8392
  }
@@ -8333,15 +8402,15 @@ async function fileExists8(path) {
8333
8402
 
8334
8403
  // ../listener/dist/mcp/auto-config/writers/copilot-hooks.js
8335
8404
  import { promises as fs13 } from "fs";
8336
- import { join as join30 } from "path";
8405
+ import { join as join31 } from "path";
8337
8406
  var HOOKS_REL2 = ".github/hooks/repowise.json";
8338
8407
  var copilotHooksWriter = {
8339
8408
  tool: "copilot-hooks",
8340
8409
  async detect(repoRoot) {
8341
- return fileExists9(join30(repoRoot, ".github", "copilot-instructions.md"));
8410
+ return fileExists9(join31(repoRoot, ".github", "copilot-instructions.md"));
8342
8411
  },
8343
8412
  async write(ctx) {
8344
- const path = join30(ctx.repoRoot, HOOKS_REL2);
8413
+ const path = join31(ctx.repoRoot, HOOKS_REL2);
8345
8414
  const next = buildCopilotHooksFile({
8346
8415
  repoName: ctx.repoName,
8347
8416
  contextFolder: ctx.contextFolder ?? "repowise-context",
@@ -8361,14 +8430,14 @@ var copilotHooksWriter = {
8361
8430
  if (existing === next) {
8362
8431
  return { status: "unchanged", path };
8363
8432
  }
8364
- await fs13.mkdir(join30(ctx.repoRoot, ".github", "hooks"), { recursive: true });
8433
+ await fs13.mkdir(join31(ctx.repoRoot, ".github", "hooks"), { recursive: true });
8365
8434
  await fs13.writeFile(path, next, "utf-8");
8366
8435
  const userSelected = ctx.selectedTools?.includes("copilot") ?? false;
8367
8436
  await applyGitignoreChanges(ctx.repoRoot, userSelected ? { remove: [HOOKS_REL2] } : { add: [HOOKS_REL2] });
8368
8437
  return { status: "written", path };
8369
8438
  },
8370
8439
  async remove(ctx) {
8371
- const path = join30(ctx.repoRoot, HOOKS_REL2);
8440
+ const path = join31(ctx.repoRoot, HOOKS_REL2);
8372
8441
  if (!await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
8373
8442
  await fs13.unlink(path).catch(() => {
8374
8443
  });
@@ -8394,14 +8463,14 @@ async function readOrNull3(path) {
8394
8463
 
8395
8464
  // ../listener/dist/mcp/auto-config/writers/cursor.js
8396
8465
  import { promises as fs14 } from "fs";
8397
- import { join as join31 } from "path";
8466
+ import { join as join32 } from "path";
8398
8467
  var cursorWriter = {
8399
8468
  tool: "cursor",
8400
8469
  async detect(repoRoot) {
8401
- return await fileExists10(join31(repoRoot, ".cursor")) || await fileExists10(join31(repoRoot, ".cursorrules"));
8470
+ return await fileExists10(join32(repoRoot, ".cursor")) || await fileExists10(join32(repoRoot, ".cursorrules"));
8402
8471
  },
8403
8472
  async write(ctx) {
8404
- const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8473
+ const path = join32(ctx.repoRoot, ".cursor", "mcp.json");
8405
8474
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8406
8475
  const status2 = await writeMergedConfig({
8407
8476
  path,
@@ -8411,7 +8480,7 @@ var cursorWriter = {
8411
8480
  return { status: status2, path };
8412
8481
  },
8413
8482
  async remove(ctx) {
8414
- const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8483
+ const path = join32(ctx.repoRoot, ".cursor", "mcp.json");
8415
8484
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8416
8485
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8417
8486
  }
@@ -8427,14 +8496,14 @@ async function fileExists10(path) {
8427
8496
 
8428
8497
  // ../listener/dist/mcp/auto-config/writers/gemini-cli.js
8429
8498
  import { promises as fs15 } from "fs";
8430
- import { join as join32 } from "path";
8499
+ import { join as join33 } from "path";
8431
8500
  var geminiCliWriter = {
8432
8501
  tool: "gemini-cli",
8433
8502
  async detect(_repoRoot, home) {
8434
- return fileExists11(join32(home, ".gemini"));
8503
+ return fileExists11(join33(home, ".gemini"));
8435
8504
  },
8436
8505
  async write(ctx) {
8437
- const path = join32(ctx.home, ".gemini", "settings.json");
8506
+ const path = join33(ctx.home, ".gemini", "settings.json");
8438
8507
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8439
8508
  const status2 = await writeMergedConfig({
8440
8509
  path,
@@ -8444,7 +8513,7 @@ var geminiCliWriter = {
8444
8513
  return { status: status2, path };
8445
8514
  },
8446
8515
  async remove(ctx) {
8447
- const path = join32(ctx.home, ".gemini", "settings.json");
8516
+ const path = join33(ctx.home, ".gemini", "settings.json");
8448
8517
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8449
8518
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8450
8519
  }
@@ -8460,15 +8529,15 @@ async function fileExists11(path) {
8460
8529
 
8461
8530
  // ../listener/dist/mcp/auto-config/writers/gemini-hooks.js
8462
8531
  import { promises as fs16 } from "fs";
8463
- import { join as join33 } from "path";
8532
+ import { join as join34 } from "path";
8464
8533
  var SETTINGS_REL = ".gemini/settings.json";
8465
8534
  var geminiHooksWriter = {
8466
8535
  tool: "gemini-hooks",
8467
8536
  async detect(repoRoot, home) {
8468
- return await fileExists12(join33(home, ".gemini")) || await fileExists12(join33(repoRoot, ".gemini"));
8537
+ return await fileExists12(join34(home, ".gemini")) || await fileExists12(join34(repoRoot, ".gemini"));
8469
8538
  },
8470
8539
  async write(ctx) {
8471
- const repoSignal = await fileExists12(join33(ctx.repoRoot, ".gemini")) || await fileExists12(join33(ctx.repoRoot, "GEMINI.md"));
8540
+ const repoSignal = await fileExists12(join34(ctx.repoRoot, ".gemini")) || await fileExists12(join34(ctx.repoRoot, "GEMINI.md"));
8472
8541
  const selected = ctx.selectedTools?.includes("gemini") ?? false;
8473
8542
  if (!repoSignal && !selected) {
8474
8543
  return { status: "skipped", reason: "no repo-scoped Gemini signal and not selected" };
@@ -8480,7 +8549,7 @@ var geminiHooksWriter = {
8480
8549
  reason: `${SETTINGS_REL} is git-tracked; add the RepoWise BeforeAgent hook manually to opt in`
8481
8550
  };
8482
8551
  }
8483
- const path = join33(ctx.repoRoot, SETTINGS_REL);
8552
+ const path = join34(ctx.repoRoot, SETTINGS_REL);
8484
8553
  const existing = await readOrNull4(path);
8485
8554
  const merged = mergeGeminiHookSettings(existing, {
8486
8555
  repoName: ctx.repoName,
@@ -8490,13 +8559,13 @@ var geminiHooksWriter = {
8490
8559
  if (merged === existing) {
8491
8560
  return { status: "unchanged", path };
8492
8561
  }
8493
- await fs16.mkdir(join33(ctx.repoRoot, ".gemini"), { recursive: true });
8562
+ await fs16.mkdir(join34(ctx.repoRoot, ".gemini"), { recursive: true });
8494
8563
  await fs16.writeFile(path, merged, "utf-8");
8495
8564
  await applyGitignoreChanges(ctx.repoRoot, { add: [SETTINGS_REL] });
8496
8565
  return { status: "written", path };
8497
8566
  },
8498
8567
  async remove(ctx) {
8499
- const path = join33(ctx.repoRoot, SETTINGS_REL);
8568
+ const path = join34(ctx.repoRoot, SETTINGS_REL);
8500
8569
  const existing = await readOrNull4(path);
8501
8570
  if (existing !== null) {
8502
8571
  const next = removeGeminiHookSettings(existing);
@@ -8527,14 +8596,14 @@ async function readOrNull4(path) {
8527
8596
 
8528
8597
  // ../listener/dist/mcp/auto-config/writers/roo.js
8529
8598
  import { promises as fs17 } from "fs";
8530
- import { join as join34 } from "path";
8599
+ import { join as join35 } from "path";
8531
8600
  var rooWriter = {
8532
8601
  tool: "roo",
8533
8602
  async detect(repoRoot, home) {
8534
- return await fileExists13(join34(repoRoot, ".roo")) || await fileExists13(join34(home, ".roo"));
8603
+ return await fileExists13(join35(repoRoot, ".roo")) || await fileExists13(join35(home, ".roo"));
8535
8604
  },
8536
8605
  async write(ctx) {
8537
- const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8606
+ const repoConfig = join35(ctx.repoRoot, ".roo", "mcp.json");
8538
8607
  const before = await readOrNull5(repoConfig);
8539
8608
  await this.remove(ctx);
8540
8609
  const after = await readOrNull5(repoConfig);
@@ -8544,9 +8613,9 @@ var rooWriter = {
8544
8613
  };
8545
8614
  },
8546
8615
  async remove(ctx) {
8547
- const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8548
- const homeConfig = join34(ctx.home, ".roo", "mcp.json");
8549
- const homeSettings = join34(ctx.home, ".roo", "settings.json");
8616
+ const repoConfig = join35(ctx.repoRoot, ".roo", "mcp.json");
8617
+ const homeConfig = join35(ctx.home, ".roo", "mcp.json");
8618
+ const homeSettings = join35(ctx.home, ".roo", "settings.json");
8550
8619
  for (const path of [repoConfig, homeConfig, homeSettings]) {
8551
8620
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`).catch(() => {
8552
8621
  });
@@ -8573,14 +8642,14 @@ async function readOrNull5(path) {
8573
8642
 
8574
8643
  // ../listener/dist/mcp/auto-config/writers/windsurf.js
8575
8644
  import { promises as fs18 } from "fs";
8576
- import { join as join35 } from "path";
8645
+ import { join as join36 } from "path";
8577
8646
  var windsurfWriter = {
8578
8647
  tool: "windsurf",
8579
8648
  async detect(_repoRoot, home) {
8580
- return fileExists14(join35(home, ".codeium", "windsurf"));
8649
+ return fileExists14(join36(home, ".codeium", "windsurf"));
8581
8650
  },
8582
8651
  async write(ctx) {
8583
- const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8652
+ const path = join36(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8584
8653
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8585
8654
  const status2 = await writeMergedConfig({
8586
8655
  path,
@@ -8590,7 +8659,7 @@ var windsurfWriter = {
8590
8659
  return { status: status2, path };
8591
8660
  },
8592
8661
  async remove(ctx) {
8593
- const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8662
+ const path = join36(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8594
8663
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8595
8664
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
8596
8665
  }
@@ -8623,7 +8692,7 @@ var WRITERS = [
8623
8692
 
8624
8693
  // ../listener/dist/mcp/auto-config/index.js
8625
8694
  async function runAutoConfig(opts) {
8626
- const home = opts.home ?? homedir5();
8695
+ const home = opts.home ?? homedir6();
8627
8696
  const writers = opts.writers ?? WRITERS;
8628
8697
  const results = [];
8629
8698
  for (const writer of writers) {
@@ -8672,7 +8741,7 @@ init_installer();
8672
8741
  init_registry();
8673
8742
  init_lsp_tools();
8674
8743
  import { promises as fs19 } from "fs";
8675
- import { join as join36 } from "path";
8744
+ import { join as join37 } from "path";
8676
8745
  async function collectRepresentativeFiles(repoRoot) {
8677
8746
  const reps = /* @__PURE__ */ new Map();
8678
8747
  let inspected = 0;
@@ -8713,7 +8782,7 @@ async function collectRepresentativeFiles(repoRoot) {
8713
8782
  continue;
8714
8783
  if (name === "node_modules" || name === "dist" || name === "build")
8715
8784
  continue;
8716
- const sub = join36(repoRoot, name);
8785
+ const sub = join37(repoRoot, name);
8717
8786
  try {
8718
8787
  const stat8 = await fs19.stat(sub);
8719
8788
  if (stat8.isDirectory())
@@ -8761,7 +8830,7 @@ init_config_dir();
8761
8830
  import { promises as fs20 } from "fs";
8762
8831
  import { spawn as spawn9, execFile as execFile5 } from "child_process";
8763
8832
  import { createWriteStream as createWriteStream2 } from "fs";
8764
- import { join as join37 } from "path";
8833
+ import { join as join38 } from "path";
8765
8834
  import { promisify as promisify3 } from "util";
8766
8835
  var execFileAsync3 = promisify3(execFile5);
8767
8836
  async function isCommandOnPath2(cmd) {
@@ -8825,7 +8894,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
8825
8894
  if (e.isDirectory()) {
8826
8895
  if (skip.has(e.name) || e.name.startsWith("."))
8827
8896
  continue;
8828
- await rec(join37(dir, e.name));
8897
+ await rec(join38(dir, e.name));
8829
8898
  continue;
8830
8899
  }
8831
8900
  const dot = e.name.lastIndexOf(".");
@@ -8834,7 +8903,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
8834
8903
  const ext = e.name.slice(dot);
8835
8904
  if (!C_EXTS.has(ext) && !CPP_EXTS.has(ext))
8836
8905
  continue;
8837
- sources.push({ path: join37(dir, e.name), ext });
8906
+ sources.push({ path: join38(dir, e.name), ext });
8838
8907
  }
8839
8908
  }
8840
8909
  await rec(repoRoot);
@@ -8850,9 +8919,9 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
8850
8919
  command: `${driver} ${std} -I. -I./include -c "${src}"`
8851
8920
  };
8852
8921
  });
8853
- const outDir = join37(repoRoot, ".repowise");
8922
+ const outDir = join38(repoRoot, ".repowise");
8854
8923
  await fs20.mkdir(outDir, { recursive: true });
8855
- const outPath = join37(outDir, "compile_commands.json");
8924
+ const outPath = join38(outDir, "compile_commands.json");
8856
8925
  await fs20.writeFile(outPath, JSON.stringify(entries, null, 2));
8857
8926
  const capHit = sources.length >= MAX_STUB_SOURCES;
8858
8927
  console.log(`[workspace-prep] ${repoId}: wrote ${entries.length.toString()}-entry compile_commands stub to .repowise/${capHit ? " (cap reached \u2014 additional sources truncated)" : ""}`);
@@ -8861,7 +8930,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
8861
8930
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
8862
8931
  async function fileExists15(repoRoot, name) {
8863
8932
  try {
8864
- await fs20.access(join37(repoRoot, name));
8933
+ await fs20.access(join38(repoRoot, name));
8865
8934
  return true;
8866
8935
  } catch {
8867
8936
  return false;
@@ -8872,7 +8941,7 @@ async function detectWorkspaceDeps(repoRoot) {
8872
8941
  if (await fileExists15(repoRoot, "package.json")) {
8873
8942
  let pkgRaw = "";
8874
8943
  try {
8875
- pkgRaw = await fs20.readFile(join37(repoRoot, "package.json"), "utf8");
8944
+ pkgRaw = await fs20.readFile(join38(repoRoot, "package.json"), "utf8");
8876
8945
  } catch {
8877
8946
  }
8878
8947
  if (pkgRaw) {
@@ -8975,7 +9044,7 @@ async function runWorkspacePreps(opts) {
8975
9044
  if (opts.missing.length === 0)
8976
9045
  return [];
8977
9046
  const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
8978
- const logPath2 = join37(getConfigDir(), `install-log.${safeRepoId}.txt`);
9047
+ const logPath2 = join38(getConfigDir(), `install-log.${safeRepoId}.txt`);
8979
9048
  await fs20.mkdir(getConfigDir(), { recursive: true });
8980
9049
  const stream = opts.logStream ?? createWriteStream2(logPath2, { flags: "a" });
8981
9050
  stream.on("error", () => {
@@ -9126,7 +9195,7 @@ async function prepareWorkspaceDepsForRepos(repos) {
9126
9195
  init_registry();
9127
9196
  import { execFile as execFileCb, spawn as spawn10 } from "child_process";
9128
9197
  import { access as access3, readdir as readdir4 } from "fs/promises";
9129
- import { join as join38 } from "path";
9198
+ import { join as join39 } from "path";
9130
9199
  import { promisify as promisify4 } from "util";
9131
9200
  var execFile6 = promisify4(execFileCb);
9132
9201
  var DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -9179,7 +9248,7 @@ async function ensureIndexable(indexRoot, language, options = {}) {
9179
9248
  }
9180
9249
  }
9181
9250
  try {
9182
- await access3(join38(indexRoot, spec.indexStoreProbe));
9251
+ await access3(join39(indexRoot, spec.indexStoreProbe));
9183
9252
  return { ready: true, built: false };
9184
9253
  } catch {
9185
9254
  }
@@ -9219,7 +9288,9 @@ async function ensureIndexable(indexRoot, language, options = {}) {
9219
9288
  init_src();
9220
9289
  init_registry();
9221
9290
  import { execFile as execFileCb2 } from "child_process";
9291
+ import { dirname as dirname16 } from "path";
9222
9292
  import { promisify as promisify5 } from "util";
9293
+ init_daemon_path();
9223
9294
 
9224
9295
  // ../listener/dist/typed-resolution/lsp-upgrade-strategy.js
9225
9296
  var goStrategy = {
@@ -9701,7 +9772,13 @@ async function isCommandOnPath4(command) {
9701
9772
  return false;
9702
9773
  const probeCmd = process.platform === "win32" ? "where" : "which";
9703
9774
  try {
9704
- await execFile7(probeCmd, [command], { timeout: 5e3 });
9775
+ await execFile7(probeCmd, [command], {
9776
+ timeout: 5e3,
9777
+ env: {
9778
+ ...process.env,
9779
+ PATH: buildLspSpawnPath(process.env.PATH, dirname16(process.execPath), managedBinDirs())
9780
+ }
9781
+ });
9705
9782
  return true;
9706
9783
  } catch {
9707
9784
  return false;
@@ -9905,13 +9982,13 @@ init_config_dir();
9905
9982
  import { execFile as execFileCb3 } from "child_process";
9906
9983
  import { createHash as createHash5 } from "crypto";
9907
9984
  import { mkdir as mkdir15, readdir as readdir5, rm, stat as stat6 } from "fs/promises";
9908
- import { join as join39 } from "path";
9985
+ import { join as join40 } from "path";
9909
9986
  import { promisify as promisify6 } from "util";
9910
9987
  var execFile8 = promisify6(execFileCb3);
9911
9988
  var GIT_OP_TIMEOUT_MS = 6e4;
9912
9989
  var MAX_CONCURRENT_WORKTREES = 3;
9913
9990
  function worktreeBaseDir() {
9914
- return join39(getConfigDir(), "lsp-worktrees");
9991
+ return join40(getConfigDir(), "lsp-worktrees");
9915
9992
  }
9916
9993
  async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktreeBaseDir()) {
9917
9994
  let reaped = 0;
@@ -9923,7 +10000,7 @@ async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktre
9923
10000
  }
9924
10001
  const cutoff = Date.now() - maxAgeMs;
9925
10002
  for (const entry of entries) {
9926
- const dir = join39(base, entry);
10003
+ const dir = join40(base, entry);
9927
10004
  try {
9928
10005
  const info = await stat6(dir);
9929
10006
  if (!info.isDirectory() || info.mtimeMs > cutoff)
@@ -9956,7 +10033,7 @@ async function prepareCommittedTree(repoRoot, commitSha, options = {}) {
9956
10033
  }
9957
10034
  const base = options.worktreeBase ?? worktreeBaseDir();
9958
10035
  const dirName = createHash5("sha256").update(`${repoRoot}\0${commitSha}`).digest("hex").slice(0, 16) + "-" + commitSha.slice(0, 12);
9959
- const worktreePath = join39(base, dirName);
10036
+ const worktreePath = join40(base, dirName);
9960
10037
  try {
9961
10038
  await mkdir15(base, { recursive: true });
9962
10039
  await git(["-C", repoRoot, "worktree", "prune"]).catch(() => void 0);
@@ -10292,7 +10369,7 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
10292
10369
  var CRASH_LOOP_THRESHOLD = 3;
10293
10370
  async function readRawToolConfig() {
10294
10371
  try {
10295
- const configPath = join40(getConfigDir(), "config.json");
10372
+ const configPath = join41(getConfigDir(), "config.json");
10296
10373
  const data = await readFile13(configPath, "utf-8");
10297
10374
  const raw = JSON.parse(data);
10298
10375
  return {
@@ -10350,7 +10427,7 @@ async function updateToolConfigsForRepo(localPath, config2, state, repoId) {
10350
10427
  await updateToolConfig(localPath, "codex", repoName, contextFolder, contextFiles, variant);
10351
10428
  }
10352
10429
  if (!state.repos[repoId]) {
10353
- state.repos[repoId] = { lastSyncTimestamp: "", lastSyncCommitSha: null };
10430
+ state.repos[repoId] = { lastSyncTimestamp: (/* @__PURE__ */ new Date()).toISOString(), lastSyncCommitSha: null };
10354
10431
  }
10355
10432
  state.repos[repoId].lastToolConfigHash = hash;
10356
10433
  console.log(`[ai-tools] Updated tool configs for ${repoId}`);
@@ -10367,11 +10444,11 @@ function resolveAuditRoots() {
10367
10444
  return override.split(":").map((s) => s.trim()).filter(Boolean);
10368
10445
  }
10369
10446
  const out = /* @__PURE__ */ new Set();
10370
- let dir = dirname15(process.execPath);
10447
+ let dir = dirname17(process.execPath);
10371
10448
  for (let i = 0; i < 8; i += 1) {
10372
- out.add(join40(dir, "node_modules"));
10373
- out.add(join40(dir, "lib", "node_modules"));
10374
- const parent = dirname15(dir);
10449
+ out.add(join41(dir, "node_modules"));
10450
+ out.add(join41(dir, "lib", "node_modules"));
10451
+ const parent = dirname17(dir);
10375
10452
  if (parent === dir)
10376
10453
  break;
10377
10454
  dir = parent;
@@ -10635,7 +10712,7 @@ async function checkStaleContext(repos, state, groups) {
10635
10712
  if (group?.offline.isOffline)
10636
10713
  continue;
10637
10714
  const { statSync: statSync2, readdirSync: readdirSync2 } = await import("fs");
10638
- const contextPath = join40(repo.localPath, "repowise-context");
10715
+ const contextPath = join41(repo.localPath, "repowise-context");
10639
10716
  let isMissingOrEmpty = false;
10640
10717
  try {
10641
10718
  const s = statSync2(contextPath);
@@ -10700,7 +10777,7 @@ async function reconcileAgentInstructions(repos) {
10700
10777
  const { graphOnly } = await readRawToolConfig();
10701
10778
  const variant = graphOnly ? "graph" : "full";
10702
10779
  for (const repo of repos) {
10703
- const path = join40(repo.localPath, "repowise-context", "project-overview.md");
10780
+ const path = join41(repo.localPath, "repowise-context", "project-overview.md");
10704
10781
  let content;
10705
10782
  try {
10706
10783
  content = await readFile13(path, "utf-8");
@@ -10784,7 +10861,7 @@ async function startListener() {
10784
10861
  }
10785
10862
  const configDir = getConfigDir();
10786
10863
  await mkdir16(configDir, { recursive: true });
10787
- const lockPath = join40(configDir, "listener.lock");
10864
+ const lockPath = join41(configDir, "listener.lock");
10788
10865
  await writeFile15(lockPath, "", { flag: "a" });
10789
10866
  let lockIsHeld = false;
10790
10867
  try {
@@ -10827,7 +10904,7 @@ async function startListener() {
10827
10904
  return;
10828
10905
  }
10829
10906
  if (config2.repos.length === 0 && !config2.autoDiscoverRepos) {
10830
- console.error(`No repos configured. Add repos to ${join40(configDir, "config.json")}`);
10907
+ console.error(`No repos configured. Add repos to ${join41(configDir, "config.json")}`);
10831
10908
  await releaseLockAndExit();
10832
10909
  process.exitCode = 1;
10833
10910
  return;
@@ -10894,8 +10971,8 @@ async function startListener() {
10894
10971
  const packageName = true ? "repowisestage" : "repowise";
10895
10972
  let currentVersion = "";
10896
10973
  try {
10897
- const selfDir = dirname15(fileURLToPath3(import.meta.url));
10898
- const pkgJsonPath = join40(selfDir, "..", "..", "package.json");
10974
+ const selfDir = dirname17(fileURLToPath3(import.meta.url));
10975
+ const pkgJsonPath = join41(selfDir, "..", "..", "package.json");
10899
10976
  const pkgJson = JSON.parse(await readFile13(pkgJsonPath, "utf-8"));
10900
10977
  currentVersion = pkgJson.version;
10901
10978
  } catch (err) {
@@ -11137,7 +11214,8 @@ async function startListener() {
11137
11214
  }
11138
11215
  try {
11139
11216
  const sinceTimestamp = group.repoIds.reduce((earliest, id) => {
11140
- const ts2 = state.repos[id]?.lastSyncTimestamp ?? now;
11217
+ const raw = state.repos[id]?.lastSyncTimestamp;
11218
+ const ts2 = raw && !Number.isNaN(Date.parse(raw)) ? raw : now;
11141
11219
  return ts2 < earliest ? ts2 : earliest;
11142
11220
  }, now);
11143
11221
  const response = await group.pollClient.poll(group.repoIds, sinceTimestamp, {
@@ -11397,7 +11475,7 @@ async function startListener() {
11397
11475
  } catch {
11398
11476
  }
11399
11477
  }
11400
- const credentialsPath = join40(getConfigDir(), "credentials.json");
11478
+ const credentialsPath = join41(getConfigDir(), "credentials.json");
11401
11479
  let credentialsChanged = false;
11402
11480
  let watcher = null;
11403
11481
  try {
@@ -11442,8 +11520,8 @@ if (isDirectRun) {
11442
11520
  }
11443
11521
 
11444
11522
  // src/lib/env.ts
11445
- import { homedir as homedir6 } from "os";
11446
- import { join as join41 } from "path";
11523
+ import { homedir as homedir7 } from "os";
11524
+ import { join as join42 } from "path";
11447
11525
  var IS_STAGING2 = true ? true : false;
11448
11526
  var PRODUCTION = {
11449
11527
  apiUrl: "https://api.repowise.ai",
@@ -11463,7 +11541,7 @@ function getEnvConfig() {
11463
11541
  return IS_STAGING2 ? STAGING : PRODUCTION;
11464
11542
  }
11465
11543
  function getConfigDir2() {
11466
- return join41(homedir6(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
11544
+ return join42(homedir7(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
11467
11545
  }
11468
11546
  function getPackageName() {
11469
11547
  return true ? "repowisestage" : "repowise";
@@ -11474,11 +11552,11 @@ import chalk from "chalk";
11474
11552
 
11475
11553
  // src/lib/config.ts
11476
11554
  import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir17, rename as rename4, unlink as unlink10 } from "fs/promises";
11477
- import { join as join42 } from "path";
11555
+ import { join as join43 } from "path";
11478
11556
  import lockfile5 from "proper-lockfile";
11479
11557
  async function getConfig() {
11480
11558
  try {
11481
- const data = await readFile14(join42(getConfigDir2(), "config.json"), "utf-8");
11559
+ const data = await readFile14(join43(getConfigDir2(), "config.json"), "utf-8");
11482
11560
  return JSON.parse(data);
11483
11561
  } catch {
11484
11562
  return {};
@@ -11486,7 +11564,7 @@ async function getConfig() {
11486
11564
  }
11487
11565
  async function saveConfig(config2) {
11488
11566
  const dir = getConfigDir2();
11489
- const path = join42(dir, "config.json");
11567
+ const path = join43(dir, "config.json");
11490
11568
  await mkdir17(dir, { recursive: true });
11491
11569
  const tmpPath = path + ".tmp";
11492
11570
  try {
@@ -11502,7 +11580,7 @@ async function saveConfig(config2) {
11502
11580
  }
11503
11581
  async function mergeAndSaveConfig(updates) {
11504
11582
  const dir = getConfigDir2();
11505
- const path = join42(dir, "config.json");
11583
+ const path = join43(dir, "config.json");
11506
11584
  await mkdir17(dir, { recursive: true });
11507
11585
  try {
11508
11586
  await writeFile16(path, "", { flag: "a" });
@@ -11573,7 +11651,7 @@ async function showWelcome(currentVersion) {
11573
11651
 
11574
11652
  // src/commands/create.ts
11575
11653
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
11576
- import { dirname as dirname17, join as join48 } from "path";
11654
+ import { dirname as dirname19, join as join49 } from "path";
11577
11655
  init_src();
11578
11656
  import chalk8 from "chalk";
11579
11657
  import ora from "ora";
@@ -11582,7 +11660,7 @@ import ora from "ora";
11582
11660
  import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
11583
11661
  import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir18, chmod as chmod4, unlink as unlink11 } from "fs/promises";
11584
11662
  import http from "http";
11585
- import { join as join43 } from "path";
11663
+ import { join as join44 } from "path";
11586
11664
  var CLI_CALLBACK_PORT = 19876;
11587
11665
  var CALLBACK_TIMEOUT_MS = 12e4;
11588
11666
  function getCognitoConfigForStorage() {
@@ -11748,7 +11826,7 @@ async function refreshTokens2(refreshToken) {
11748
11826
  }
11749
11827
  async function getStoredCredentials2() {
11750
11828
  try {
11751
- const credPath = join43(getConfigDir2(), "credentials.json");
11829
+ const credPath = join44(getConfigDir2(), "credentials.json");
11752
11830
  const data = await readFile15(credPath, "utf-8");
11753
11831
  return JSON.parse(data);
11754
11832
  } catch (err) {
@@ -11760,14 +11838,14 @@ async function getStoredCredentials2() {
11760
11838
  }
11761
11839
  async function storeCredentials2(credentials) {
11762
11840
  const dir = getConfigDir2();
11763
- const credPath = join43(dir, "credentials.json");
11841
+ const credPath = join44(dir, "credentials.json");
11764
11842
  await mkdir18(dir, { recursive: true, mode: 448 });
11765
11843
  await writeFile17(credPath, JSON.stringify(credentials, null, 2));
11766
11844
  await chmod4(credPath, 384);
11767
11845
  }
11768
11846
  async function clearCredentials() {
11769
11847
  try {
11770
- await unlink11(join43(getConfigDir2(), "credentials.json"));
11848
+ await unlink11(join44(getConfigDir2(), "credentials.json"));
11771
11849
  } catch (err) {
11772
11850
  if (err.code !== "ENOENT") throw err;
11773
11851
  }
@@ -11975,9 +12053,9 @@ async function selectAiTools() {
11975
12053
 
11976
12054
  // src/lib/gitignore.ts
11977
12055
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
11978
- import { join as join44 } from "path";
12056
+ import { join as join45 } from "path";
11979
12057
  function ensureGitignore(repoRoot, entry) {
11980
- const gitignorePath = join44(repoRoot, ".gitignore");
12058
+ const gitignorePath = join45(repoRoot, ".gitignore");
11981
12059
  if (existsSync2(gitignorePath)) {
11982
12060
  const content = readFileSync2(gitignorePath, "utf-8");
11983
12061
  const lines = content.split("\n").map((l) => l.trim());
@@ -11994,7 +12072,7 @@ function ensureGitignore(repoRoot, entry) {
11994
12072
  // src/lib/graph-cache.ts
11995
12073
  init_config_dir();
11996
12074
  import { mkdirSync, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
11997
- import { dirname as dirname16, join as join45 } from "path";
12075
+ import { dirname as dirname18, join as join46 } from "path";
11998
12076
  var SAFE_REPO_ID = /^[A-Za-z0-9_.-]{1,128}$/;
11999
12077
  function assertSafeRepoId2(repoId) {
12000
12078
  if (!repoId || typeof repoId !== "string" || !SAFE_REPO_ID.test(repoId) || repoId === "." || repoId === ".." || repoId.startsWith(".")) {
@@ -12003,7 +12081,7 @@ function assertSafeRepoId2(repoId) {
12003
12081
  }
12004
12082
  function graphCachePath(repoId) {
12005
12083
  assertSafeRepoId2(repoId);
12006
- return join45(getConfigDir(), "graphs", `${repoId}.json`);
12084
+ return join46(getConfigDir(), "graphs", `${repoId}.json`);
12007
12085
  }
12008
12086
  function isUsableGraph(parsed) {
12009
12087
  if (typeof parsed !== "object" || parsed === null) return false;
@@ -12021,9 +12099,27 @@ function readErrorCode(body) {
12021
12099
  return void 0;
12022
12100
  }
12023
12101
  }
12102
+ async function readBodyBounded(res, ctrl, bodyTimeoutMs) {
12103
+ const bodyPromise = res.text();
12104
+ void bodyPromise.catch(() => {
12105
+ });
12106
+ let timer;
12107
+ const timeout = new Promise((_, reject) => {
12108
+ timer = setTimeout(() => {
12109
+ ctrl.abort();
12110
+ reject(new Error("GRAPH_BODY_TIMEOUT"));
12111
+ }, bodyTimeoutMs);
12112
+ });
12113
+ try {
12114
+ return await Promise.race([bodyPromise, timeout]);
12115
+ } finally {
12116
+ if (timer) clearTimeout(timer);
12117
+ }
12118
+ }
12024
12119
  async function ensureGraphDownloaded(opts) {
12025
12120
  const fetchFn = opts.fetchFn ?? fetch;
12026
12121
  const timeoutMs = opts.timeoutMs ?? 1e4;
12122
+ const bodyTimeoutMs = opts.bodyTimeoutMs ?? 12e4;
12027
12123
  const maxRetries = opts.maxRetries ?? 3;
12028
12124
  const sleep2 = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
12029
12125
  let targetPath;
@@ -12080,7 +12176,16 @@ async function ensureGraphDownloaded(opts) {
12080
12176
  if (!res.ok) {
12081
12177
  return { status: "error", message: `HTTP ${String(res.status)}` };
12082
12178
  }
12083
- const body = await res.text();
12179
+ let body;
12180
+ try {
12181
+ body = await readBodyBounded(res, ctrl, bodyTimeoutMs);
12182
+ } catch (err) {
12183
+ if (attempt < maxRetries) {
12184
+ await sleep2(2e3 * attempt);
12185
+ continue;
12186
+ }
12187
+ return { status: "error", message: err instanceof Error ? err.message : String(err) };
12188
+ }
12084
12189
  let parsed;
12085
12190
  try {
12086
12191
  parsed = JSON.parse(body);
@@ -12095,7 +12200,7 @@ async function ensureGraphDownloaded(opts) {
12095
12200
  return { status: "not_found", message: "EMPTY_GRAPH" };
12096
12201
  }
12097
12202
  try {
12098
- mkdirSync(dirname16(targetPath), { recursive: true });
12203
+ mkdirSync(dirname18(targetPath), { recursive: true });
12099
12204
  const tmp = `${targetPath}.${String(process.pid)}.${Math.random().toString(36).slice(2)}.tmp`;
12100
12205
  try {
12101
12206
  writeFileSync2(tmp, body, { encoding: "utf-8", mode: 384 });
@@ -12246,7 +12351,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
12246
12351
  const message = err instanceof Error ? err.message : String(err);
12247
12352
  if (message.includes("not awaiting input") || message.includes("expired")) {
12248
12353
  console.log(chalk3.dim(" Pipeline has already moved on \u2014 continuing."));
12249
- return;
12354
+ return { finished: true };
12250
12355
  }
12251
12356
  try {
12252
12357
  await new Promise((r) => setTimeout(r, 1e3));
@@ -12256,7 +12361,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
12256
12361
  });
12257
12362
  } catch {
12258
12363
  console.log(chalk3.yellow(" Could not submit answer \u2014 pipeline will continue."));
12259
- return;
12364
+ return { finished: false };
12260
12365
  }
12261
12366
  }
12262
12367
  if (action === "done") {
@@ -12267,6 +12372,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
12267
12372
  console.log(chalk3.dim(" Answer recorded."));
12268
12373
  }
12269
12374
  console.log("");
12375
+ return { finished: action === "done" || questionCounter >= MAX_QUESTIONS };
12270
12376
  }
12271
12377
 
12272
12378
  // src/lib/progress-renderer.ts
@@ -12299,6 +12405,12 @@ var ProgressRenderer = class {
12299
12405
  privacyShieldShown = false;
12300
12406
  discoveryShown = false;
12301
12407
  interviewCompleteShown = false;
12408
+ // FIX-10: the scan runs in PARALLEL with the interview, so scanProgress
12409
+ // arrives mid-interview. Hold scan rendering until the interview is
12410
+ // definitively over (setInterviewComplete) so the coffee/Code-Analysis/graph
12411
+ // block never interrupts the Q&A. Buffer the latest progress meanwhile.
12412
+ interviewComplete = false;
12413
+ pendingScanProgress = null;
12302
12414
  scanHeaderShown = false;
12303
12415
  scanSummaryShown = false;
12304
12416
  graphSubtitleShown = false;
@@ -12600,6 +12712,21 @@ var ProgressRenderer = class {
12600
12712
  }
12601
12713
  return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
12602
12714
  }
12715
+ /**
12716
+ * FIX-10 — mark the interview definitively over. Unlocks scan-progress
12717
+ * rendering and flushes any progress buffered during the interview, so the
12718
+ * coffee → Code-Analysis → graph-ready block appears in order AFTER the last
12719
+ * question (renderScanProgress is idempotent via its *Shown latches).
12720
+ * Idempotent; safe to call from multiple completion paths.
12721
+ */
12722
+ setInterviewComplete(spinner) {
12723
+ if (this.interviewComplete) return;
12724
+ this.interviewComplete = true;
12725
+ if (this.pendingScanProgress) {
12726
+ this.renderScanProgress(this.pendingScanProgress, spinner);
12727
+ this.pendingScanProgress = null;
12728
+ }
12729
+ }
12603
12730
  update(syncResult, spinner) {
12604
12731
  if (syncResult.privacyShieldEnabled !== void 0) {
12605
12732
  this.renderPrivacyShield(syncResult.privacyShieldEnabled, spinner);
@@ -12608,7 +12735,11 @@ var ProgressRenderer = class {
12608
12735
  this.renderDiscovery(syncResult.discoveryResult, spinner);
12609
12736
  }
12610
12737
  if (syncResult.scanProgress) {
12611
- this.renderScanProgress(syncResult.scanProgress, spinner);
12738
+ if (this.interviewComplete) {
12739
+ this.renderScanProgress(syncResult.scanProgress, spinner);
12740
+ } else {
12741
+ this.pendingScanProgress = syncResult.scanProgress;
12742
+ }
12612
12743
  }
12613
12744
  if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
12614
12745
  this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
@@ -12668,7 +12799,7 @@ import chalk6 from "chalk";
12668
12799
 
12669
12800
  // src/lib/dep-installer.ts
12670
12801
  import { promises as fs21 } from "fs";
12671
- import { join as join46 } from "path";
12802
+ import { join as join47 } from "path";
12672
12803
  var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
12673
12804
  "typescript",
12674
12805
  "javascript",
@@ -12685,7 +12816,7 @@ var exists = async (p) => {
12685
12816
  }
12686
12817
  };
12687
12818
  async function fileExists16(repoRoot, name) {
12688
- return exists(join46(repoRoot, name));
12819
+ return exists(join47(repoRoot, name));
12689
12820
  }
12690
12821
  async function detectNodePackageManager(repoRoot) {
12691
12822
  if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
@@ -12763,11 +12894,11 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
12763
12894
  import { spawn as spawn11 } from "child_process";
12764
12895
  import { createWriteStream as createWriteStream3 } from "fs";
12765
12896
  import { promises as fs22 } from "fs";
12766
- import { join as join47 } from "path";
12897
+ import { join as join48 } from "path";
12767
12898
  var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
12768
12899
  async function runMissingDepInstalls(opts) {
12769
12900
  const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
12770
- const logPath2 = join47(getConfigDir2(), `install-log.${safeRepoId}.txt`);
12901
+ const logPath2 = join48(getConfigDir2(), `install-log.${safeRepoId}.txt`);
12771
12902
  await fs22.mkdir(getConfigDir2(), { recursive: true });
12772
12903
  const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
12773
12904
  stream.on("error", () => {
@@ -12814,7 +12945,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
12814
12945
  }
12815
12946
  async function runPipFlow(repoRoot, stream, timeoutMs) {
12816
12947
  await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
12817
- const venvPip = process.platform === "win32" ? join47(".venv", "Scripts", "pip.exe") : join47(".venv", "bin", "pip");
12948
+ const venvPip = process.platform === "win32" ? join48(".venv", "Scripts", "pip.exe") : join48(".venv", "bin", "pip");
12818
12949
  await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
12819
12950
  }
12820
12951
  function runSimple(cmd, repoRoot, stream, timeoutMs) {
@@ -12997,14 +13128,29 @@ async function create() {
12997
13128
  const spinner = ora("Checking authentication...").start();
12998
13129
  try {
12999
13130
  let credentials = await getValidCredentials2();
13000
- if (!credentials) {
13001
- spinner.info(chalk8.yellow("Not logged in. Opening browser to authenticate..."));
13131
+ let needsLogin = !credentials;
13132
+ if (credentials) {
13133
+ try {
13134
+ await apiRequest("/v1/account/usage");
13135
+ } catch (err) {
13136
+ if (err instanceof Error && /Session expired|Not logged in/i.test(err.message)) {
13137
+ needsLogin = true;
13138
+ }
13139
+ }
13140
+ }
13141
+ if (needsLogin) {
13142
+ spinner.info(
13143
+ chalk8.yellow("Not logged in or session expired. Opening browser to authenticate...")
13144
+ );
13002
13145
  credentials = await performLogin();
13003
13146
  const { email } = decodeIdToken(credentials.idToken);
13004
13147
  spinner.succeed(chalk8.green(`Authenticated as ${chalk8.bold(email)}`));
13005
13148
  } else {
13006
13149
  spinner.succeed("Authenticated");
13007
13150
  }
13151
+ if (!credentials) {
13152
+ throw new Error("Authentication failed \u2014 run `repowise login` and retry.");
13153
+ }
13008
13154
  let repoId;
13009
13155
  let repoName;
13010
13156
  let repoRoot;
@@ -13213,6 +13359,8 @@ async function create() {
13213
13359
  const progressRenderer = new ProgressRenderer();
13214
13360
  let depInstallShown = false;
13215
13361
  let seenAwaitingInput = false;
13362
+ let interviewComplete = false;
13363
+ let interviewQuestionsAnswered = 0;
13216
13364
  let lspWaitStartedAt = null;
13217
13365
  let lspFellBackNoticeShown = false;
13218
13366
  let depInstallPromise = null;
@@ -13253,6 +13401,8 @@ async function create() {
13253
13401
  try {
13254
13402
  while (true) {
13255
13403
  if (++pollAttempts > MAX_POLL_ATTEMPTS) {
13404
+ progressRenderer.setInterviewComplete(spinner);
13405
+ progressRenderer.finalize();
13256
13406
  spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
13257
13407
  process.exitCode = 1;
13258
13408
  return;
@@ -13272,6 +13422,10 @@ async function create() {
13272
13422
  if (sigintBusy) {
13273
13423
  continue;
13274
13424
  }
13425
+ if (!interviewComplete && graphOnly) {
13426
+ interviewComplete = true;
13427
+ progressRenderer.setInterviewComplete(spinner);
13428
+ }
13275
13429
  progressRenderer.update(syncResult, spinner);
13276
13430
  if (!graphOnly && syncResult.status === "in_progress") {
13277
13431
  if (syncResult.lspWaitState === "awaiting" || syncResult.lspWaitState === "analyzing") {
@@ -13291,13 +13445,19 @@ async function create() {
13291
13445
  }
13292
13446
  if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
13293
13447
  spinner.stop();
13294
- await handleInterview(
13448
+ const estimatedQ = syncResult.discoveryResult?.estimatedInterviewQuestions;
13449
+ const { finished } = await handleInterview(
13295
13450
  syncId,
13296
13451
  syncResult.questionId,
13297
13452
  syncResult.questionText,
13298
13453
  syncResult.questionContext ?? void 0,
13299
- syncResult.discoveryResult?.estimatedInterviewQuestions
13454
+ estimatedQ
13300
13455
  );
13456
+ interviewQuestionsAnswered++;
13457
+ if (!interviewComplete && (finished || estimatedQ !== void 0 && estimatedQ > 0 && interviewQuestionsAnswered >= estimatedQ)) {
13458
+ interviewComplete = true;
13459
+ progressRenderer.setInterviewComplete(spinner);
13460
+ }
13301
13461
  spinner.start("Resuming pipeline...");
13302
13462
  continue;
13303
13463
  }
@@ -13317,6 +13477,7 @@ async function create() {
13317
13477
  depInstallPromise = result.installPromise;
13318
13478
  }
13319
13479
  if (syncResult.status === "completed") {
13480
+ progressRenderer.setInterviewComplete(spinner);
13320
13481
  progressRenderer.finalize();
13321
13482
  const generatedFiles = syncResult.filesGenerated ?? [];
13322
13483
  const fileCount = generatedFiles.length;
@@ -13341,6 +13502,7 @@ async function create() {
13341
13502
  break;
13342
13503
  }
13343
13504
  if (syncResult.status === "failed") {
13505
+ progressRenderer.setInterviewComplete(spinner);
13344
13506
  progressRenderer.finalize();
13345
13507
  spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
13346
13508
  process.exitCode = 1;
@@ -13356,7 +13518,7 @@ async function create() {
13356
13518
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
13357
13519
  const files = listResult.data?.files ?? listResult.files ?? [];
13358
13520
  if (files.length > 0) {
13359
- const contextDir = join48(repoRoot, DEFAULT_CONTEXT_FOLDER);
13521
+ const contextDir = join49(repoRoot, DEFAULT_CONTEXT_FOLDER);
13360
13522
  mkdirSync2(contextDir, { recursive: true });
13361
13523
  let downloadedCount = 0;
13362
13524
  let failedCount = 0;
@@ -13370,8 +13532,8 @@ async function create() {
13370
13532
  const response = await fetch(presignedUrl);
13371
13533
  if (response.ok) {
13372
13534
  const content = await response.text();
13373
- const filePath = join48(contextDir, file.fileName);
13374
- mkdirSync2(dirname17(filePath), { recursive: true });
13535
+ const filePath = join49(contextDir, file.fileName);
13536
+ mkdirSync2(dirname19(filePath), { recursive: true });
13375
13537
  writeFileSync3(filePath, content, "utf-8");
13376
13538
  downloadedCount++;
13377
13539
  } else {
@@ -13621,7 +13783,7 @@ Files are stored on our servers (not in git). Retry when online.`
13621
13783
 
13622
13784
  // src/commands/member.ts
13623
13785
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
13624
- import { dirname as dirname18, join as join49, resolve, sep } from "path";
13786
+ import { dirname as dirname20, join as join50, resolve, sep } from "path";
13625
13787
  import chalk9 from "chalk";
13626
13788
  import ora2 from "ora";
13627
13789
  var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
@@ -13775,7 +13937,7 @@ async function member() {
13775
13937
  spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
13776
13938
  const { tools } = await selectAiTools();
13777
13939
  spinner.start("Downloading context files...");
13778
- const contextDir = join49(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13940
+ const contextDir = join50(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13779
13941
  mkdirSync3(contextDir, { recursive: true });
13780
13942
  let downloadedCount = 0;
13781
13943
  let failedCount = 0;
@@ -13796,7 +13958,7 @@ async function member() {
13796
13958
  const response = await fetch(presignedUrl);
13797
13959
  if (response.ok) {
13798
13960
  const content = await response.text();
13799
- mkdirSync3(dirname18(safePath), { recursive: true });
13961
+ mkdirSync3(dirname20(safePath), { recursive: true });
13800
13962
  writeFileSync4(safePath, content, "utf-8");
13801
13963
  downloadedCount++;
13802
13964
  } else {
@@ -13974,10 +14136,10 @@ import ora3 from "ora";
13974
14136
 
13975
14137
  // src/lib/tenant-graph-purge.ts
13976
14138
  import { promises as fs23 } from "fs";
13977
- import { homedir as homedir7 } from "os";
13978
- import { join as join50 } from "path";
13979
- async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
13980
- const graphsDir = join50(home, ".repowise", "graphs");
14139
+ import { homedir as homedir8 } from "os";
14140
+ import { join as join51 } from "path";
14141
+ async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
14142
+ const graphsDir = join51(home, ".repowise", "graphs");
13981
14143
  const result = { kept: [], removed: [] };
13982
14144
  let entries;
13983
14145
  try {
@@ -13995,7 +14157,7 @@ async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
13995
14157
  result.kept.push(entry);
13996
14158
  continue;
13997
14159
  }
13998
- const path = join50(graphsDir, entry);
14160
+ const path = join51(graphsDir, entry);
13999
14161
  try {
14000
14162
  const stat8 = await fs23.lstat(path);
14001
14163
  if (stat8.isSymbolicLink()) {
@@ -14088,11 +14250,11 @@ async function logout() {
14088
14250
 
14089
14251
  // src/commands/status.ts
14090
14252
  import { readFile as readFile16 } from "fs/promises";
14091
- import { basename as basename4, join as join51 } from "path";
14253
+ import { basename as basename4, join as join52 } from "path";
14092
14254
  async function status() {
14093
14255
  const configDir = getConfigDir2();
14094
- const STATE_PATH = join51(configDir, "listener-state.json");
14095
- const CONFIG_PATH = join51(configDir, "config.json");
14256
+ const STATE_PATH = join52(configDir, "listener-state.json");
14257
+ const CONFIG_PATH = join52(configDir, "config.json");
14096
14258
  let state = null;
14097
14259
  try {
14098
14260
  const data = await readFile16(STATE_PATH, "utf-8");
@@ -14140,7 +14302,7 @@ async function status() {
14140
14302
 
14141
14303
  // src/commands/sync.ts
14142
14304
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
14143
- import { dirname as dirname19, join as join52 } from "path";
14305
+ import { dirname as dirname21, join as join53 } from "path";
14144
14306
  import chalk12 from "chalk";
14145
14307
  import ora4 from "ora";
14146
14308
  var POLL_INTERVAL_MS2 = 3e3;
@@ -14289,7 +14451,7 @@ async function sync() {
14289
14451
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
14290
14452
  const files = listResult.data?.files ?? listResult.files ?? [];
14291
14453
  if (files.length > 0) {
14292
- const contextDir = join52(repoRoot, DEFAULT_CONTEXT_FOLDER3);
14454
+ const contextDir = join53(repoRoot, DEFAULT_CONTEXT_FOLDER3);
14293
14455
  mkdirSync4(contextDir, { recursive: true });
14294
14456
  let downloadedCount = 0;
14295
14457
  let failedCount = 0;
@@ -14303,8 +14465,8 @@ async function sync() {
14303
14465
  const response = await fetch(presignedUrl);
14304
14466
  if (response.ok) {
14305
14467
  const content = await response.text();
14306
- const filePath = join52(contextDir, file.fileName);
14307
- mkdirSync4(dirname19(filePath), { recursive: true });
14468
+ const filePath = join53(contextDir, file.fileName);
14469
+ mkdirSync4(dirname21(filePath), { recursive: true });
14308
14470
  writeFileSync5(filePath, content, "utf-8");
14309
14471
  downloadedCount++;
14310
14472
  } else {
@@ -14559,7 +14721,7 @@ async function config() {
14559
14721
  // src/commands/mcp-log.ts
14560
14722
  import { createDecipheriv as createDecipheriv2 } from "crypto";
14561
14723
  import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
14562
- import { dirname as dirname20, join as join53 } from "path";
14724
+ import { dirname as dirname22, join as join54 } from "path";
14563
14725
  var FLAG_FILE = "mcp-log.flag";
14564
14726
  var LOG_FILE = "mcp-log.jsonl.enc";
14565
14727
  var KEY_FILE = "mcp-log.key";
@@ -14567,14 +14729,14 @@ var ENDPOINT_FILE = "listener.endpoint";
14567
14729
  var IV_BYTES2 = 12;
14568
14730
  var TAG_BYTES2 = 16;
14569
14731
  function flagPath() {
14570
- return join53(getConfigDir2(), FLAG_FILE);
14732
+ return join54(getConfigDir2(), FLAG_FILE);
14571
14733
  }
14572
14734
  function logPath() {
14573
- return join53(getConfigDir2(), LOG_FILE);
14735
+ return join54(getConfigDir2(), LOG_FILE);
14574
14736
  }
14575
14737
  async function writeFlag(flag) {
14576
14738
  const path = flagPath();
14577
- await mkdir19(dirname20(path), { recursive: true });
14739
+ await mkdir19(dirname22(path), { recursive: true });
14578
14740
  await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
14579
14741
  }
14580
14742
  async function mcpLogOn() {
@@ -14611,14 +14773,14 @@ async function trySendConsentToServer() {
14611
14773
  let apiUrl = null;
14612
14774
  let token = null;
14613
14775
  try {
14614
- const body = await readFile17(join53(getConfigDir2(), "config.json"), "utf-8");
14776
+ const body = await readFile17(join54(getConfigDir2(), "config.json"), "utf-8");
14615
14777
  const parsed = JSON.parse(body);
14616
14778
  apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
14617
14779
  } catch {
14618
14780
  return false;
14619
14781
  }
14620
14782
  try {
14621
- const body = await readFile17(join53(getConfigDir2(), "credentials.json"), "utf-8");
14783
+ const body = await readFile17(join54(getConfigDir2(), "credentials.json"), "utf-8");
14622
14784
  const parsed = JSON.parse(body);
14623
14785
  token = parsed.idToken ?? null;
14624
14786
  } catch {
@@ -14688,7 +14850,7 @@ async function mcpLogStatus() {
14688
14850
  process.stderr.write("Log size: no file yet\n");
14689
14851
  }
14690
14852
  try {
14691
- const endpointBody = await readFile17(join53(getConfigDir2(), ENDPOINT_FILE), "utf-8");
14853
+ const endpointBody = await readFile17(join54(getConfigDir2(), ENDPOINT_FILE), "utf-8");
14692
14854
  const match = /endpoint=([^\n]+)/.exec(endpointBody);
14693
14855
  process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
14694
14856
  `);
@@ -14719,7 +14881,7 @@ async function mcpLogViewingFlags(flags = {}) {
14719
14881
  const key = await readKey();
14720
14882
  if (!key) {
14721
14883
  process.stderr.write(
14722
- `No encryption key at ${join53(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
14884
+ `No encryption key at ${join54(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
14723
14885
  `
14724
14886
  );
14725
14887
  return;
@@ -14795,7 +14957,7 @@ async function mcpLogViewingFlags(flags = {}) {
14795
14957
  }
14796
14958
  async function readKey() {
14797
14959
  try {
14798
- const body = await readFile17(join53(getConfigDir2(), KEY_FILE), "utf-8");
14960
+ const body = await readFile17(join54(getConfigDir2(), KEY_FILE), "utf-8");
14799
14961
  const parsed = Buffer.from(body.trim(), "base64");
14800
14962
  if (parsed.length !== 32) return null;
14801
14963
  return parsed;
@@ -14839,7 +15001,7 @@ import chalk14 from "chalk";
14839
15001
 
14840
15002
  // src/lib/graph-loader.ts
14841
15003
  import { promises as fs24 } from "fs";
14842
- import { join as join54, resolve as resolve2 } from "path";
15004
+ import { join as join55, resolve as resolve2 } from "path";
14843
15005
  import { gunzipSync } from "zlib";
14844
15006
  var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
14845
15007
  var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
@@ -14856,8 +15018,8 @@ var GraphNotFoundError = class extends Error {
14856
15018
  var cache = /* @__PURE__ */ new Map();
14857
15019
  async function loadGraph(repoRoot = process.cwd()) {
14858
15020
  const root = resolve2(repoRoot);
14859
- const gzPath = join54(root, GZIPPED_GRAPH_PATH);
14860
- const plainPath = join54(root, RELATIVE_GRAPH_PATH);
15021
+ const gzPath = join55(root, GZIPPED_GRAPH_PATH);
15022
+ const plainPath = join55(root, RELATIVE_GRAPH_PATH);
14861
15023
  const cached = cache.get(root);
14862
15024
  if (cached) {
14863
15025
  return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
@@ -15272,13 +15434,13 @@ function registerQueryCommand(program2) {
15272
15434
 
15273
15435
  // src/commands/uninstall.ts
15274
15436
  import { promises as fs28 } from "fs";
15275
- import { homedir as homedir9 } from "os";
15276
- import { join as join58 } from "path";
15437
+ import { homedir as homedir10 } from "os";
15438
+ import { join as join59 } from "path";
15277
15439
  import chalk15 from "chalk";
15278
15440
 
15279
15441
  // src/lib/cleanup/marker-blocks.ts
15280
15442
  import { promises as fs25 } from "fs";
15281
- import { join as join55 } from "path";
15443
+ import { join as join56 } from "path";
15282
15444
  var MARKER_START = "<!-- repowise-start -->";
15283
15445
  var MARKER_END = "<!-- repowise-end -->";
15284
15446
  var CONTEXT_FILES = [
@@ -15318,7 +15480,7 @@ async function stripMarkerBlock(filePath) {
15318
15480
  async function stripAllMarkerBlocks(repoRoot) {
15319
15481
  const out = [];
15320
15482
  for (const relative of CONTEXT_FILES) {
15321
- const full = join55(repoRoot, relative);
15483
+ const full = join56(repoRoot, relative);
15322
15484
  const result = await stripMarkerBlock(full).catch((err) => ({
15323
15485
  path: full,
15324
15486
  status: "untouched",
@@ -15331,18 +15493,18 @@ async function stripAllMarkerBlocks(repoRoot) {
15331
15493
 
15332
15494
  // src/lib/cleanup/mcp-configs.ts
15333
15495
  import { promises as fs26 } from "fs";
15334
- import { join as join56 } from "path";
15496
+ import { join as join57 } from "path";
15335
15497
  function mcpConfigPaths(repoRoot, home) {
15336
15498
  return [
15337
- join56(repoRoot, ".mcp.json"),
15338
- join56(repoRoot, ".cursor", "mcp.json"),
15339
- join56(repoRoot, ".vscode", "mcp.json"),
15340
- join56(repoRoot, ".roo", "mcp.json"),
15341
- join56(home, ".cline", "mcp.json"),
15342
- join56(home, ".codeium", "windsurf", "mcp_config.json"),
15343
- join56(home, ".gemini", "settings.json"),
15344
- join56(home, ".codex", "mcp.json"),
15345
- join56(home, ".roo", "mcp.json")
15499
+ join57(repoRoot, ".mcp.json"),
15500
+ join57(repoRoot, ".cursor", "mcp.json"),
15501
+ join57(repoRoot, ".vscode", "mcp.json"),
15502
+ join57(repoRoot, ".roo", "mcp.json"),
15503
+ join57(home, ".cline", "mcp.json"),
15504
+ join57(home, ".codeium", "windsurf", "mcp_config.json"),
15505
+ join57(home, ".gemini", "settings.json"),
15506
+ join57(home, ".codex", "mcp.json"),
15507
+ join57(home, ".roo", "mcp.json")
15346
15508
  ];
15347
15509
  }
15348
15510
  async function removeRepowiseFromConfig(path, serverName) {
@@ -15382,11 +15544,11 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
15382
15544
 
15383
15545
  // src/lib/cleanup/local-state.ts
15384
15546
  import { promises as fs27 } from "fs";
15385
- import { homedir as homedir8 } from "os";
15386
- import { join as join57, resolve as resolve3 } from "path";
15547
+ import { homedir as homedir9 } from "os";
15548
+ import { join as join58, resolve as resolve3 } from "path";
15387
15549
  async function clearLocalState(homeOverride) {
15388
- const home = homeOverride ?? homedir8();
15389
- const target = resolve3(join57(home, ".repowise"));
15550
+ const home = homeOverride ?? homedir9();
15551
+ const target = resolve3(join58(home, ".repowise"));
15390
15552
  if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
15391
15553
  return { path: target, status: "error", error: "refused: not under home" };
15392
15554
  }
@@ -15421,7 +15583,7 @@ async function stopAndUninstallService(uninstaller) {
15421
15583
  // src/commands/uninstall.ts
15422
15584
  async function uninstall2(opts = {}) {
15423
15585
  const tier = opts.tier ?? "uninstall";
15424
- const home = opts.home ?? homedir9();
15586
+ const home = opts.home ?? homedir10();
15425
15587
  const repoRoot = opts.repoRoot ?? process.cwd();
15426
15588
  const loadRepoIds = opts.loadRepoIds ?? defaultLoadRepoIds;
15427
15589
  const report = { tier, removed: [], preserved: [], skipped: [] };
@@ -15430,7 +15592,7 @@ async function uninstall2(opts = {}) {
15430
15592
  else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
15431
15593
  if (tier === "stop") return report;
15432
15594
  try {
15433
- await fs28.unlink(join58(home, ".repowise", "credentials.json"));
15595
+ await fs28.unlink(join59(home, ".repowise", "credentials.json"));
15434
15596
  report.removed.push("credentials");
15435
15597
  } catch (err) {
15436
15598
  if (err.code !== "ENOENT") {
@@ -15469,7 +15631,7 @@ async function uninstall2(opts = {}) {
15469
15631
  }
15470
15632
  async function defaultLoadRepoIds(home) {
15471
15633
  try {
15472
- const raw = await fs28.readFile(join58(home, ".repowise", "config.json"), "utf-8");
15634
+ const raw = await fs28.readFile(join59(home, ".repowise", "config.json"), "utf-8");
15473
15635
  const parsed = JSON.parse(raw);
15474
15636
  return (parsed.repos ?? []).map((r) => r.repoId);
15475
15637
  } catch {
@@ -15518,10 +15680,10 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
15518
15680
  init_config_dir();
15519
15681
  import { promises as fs29 } from "fs";
15520
15682
  import { createInterface as createInterface2 } from "readline";
15521
- import { join as join59 } from "path";
15683
+ import { join as join60 } from "path";
15522
15684
  var DEFAULT_MAX = 200 * 1024;
15523
15685
  async function mcpShim(opts) {
15524
- const endpointPath = opts.endpointFile ?? join59(getConfigDir(), "listener.endpoint");
15686
+ const endpointPath = opts.endpointFile ?? join60(getConfigDir(), "listener.endpoint");
15525
15687
  const stdin = opts.stdin ?? process.stdin;
15526
15688
  const stdout = opts.stdout ?? process.stdout;
15527
15689
  const stderr = opts.stderr ?? process.stderr;
@@ -16269,8 +16431,8 @@ async function lspOn() {
16269
16431
 
16270
16432
  // bin/repowise.ts
16271
16433
  var __filename = fileURLToPath4(import.meta.url);
16272
- var __dirname = dirname21(__filename);
16273
- var pkg = JSON.parse(readFileSync3(join60(__dirname, "..", "..", "package.json"), "utf-8"));
16434
+ var __dirname = dirname23(__filename);
16435
+ var pkg = JSON.parse(readFileSync3(join61(__dirname, "..", "..", "package.json"), "utf-8"));
16274
16436
  var program = new Command();
16275
16437
  program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
16276
16438
  await showWelcome(pkg.version);