repowisestage 0.0.50 → 0.0.52

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 +457 -51
  2. package/package.json +1 -1
@@ -669,6 +669,7 @@ var init_registry = __esm({
669
669
  extensions: [".java"],
670
670
  lspLanguageId: "java",
671
671
  installHint: "brew install jdtls (see https://github.com/eclipse-jdtls/eclipse.jdt.ls)",
672
+ nativeInstallKey: "jdtls",
672
673
  initializationOptions: {
673
674
  bundles: [],
674
675
  extendedClientCapabilities: {
@@ -784,7 +785,8 @@ var init_registry = __esm({
784
785
  args: [],
785
786
  extensions: [".kt", ".kts"],
786
787
  lspLanguageId: "kotlin",
787
- installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)"
788
+ installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)",
789
+ nativeInstallKey: "kotlin-language-server"
788
790
  }
789
791
  ],
790
792
  swift: [
@@ -1070,44 +1072,44 @@ var init_lsp_versions = __esm({
1070
1072
  "use strict";
1071
1073
  NATIVE_LSP_VERSIONS = {
1072
1074
  "rust-analyzer": {
1073
- repo: "rust-lang/rust-analyzer",
1074
- version: "2025-01-09",
1075
+ source: { kind: "github-release", repo: "rust-lang/rust-analyzer" },
1076
+ version: "2026-05-11",
1075
1077
  license: "MIT OR Apache-2.0",
1076
1078
  assets: {
1077
1079
  "darwin-arm64": {
1078
1080
  filename: "rust-analyzer-aarch64-apple-darwin.gz",
1079
- sha256: null,
1081
+ sha256: "eeb6367cd2c6cc9217ae06cea2e0c7e9cf20cdac5e934a1b272b13c76ece5aee",
1080
1082
  binaryPath: "rust-analyzer-aarch64-apple-darwin"
1081
1083
  },
1082
1084
  "darwin-x64": {
1083
1085
  filename: "rust-analyzer-x86_64-apple-darwin.gz",
1084
- sha256: null,
1086
+ sha256: "718398822ec2f282bca36f6f109bbbb33746437e1a63946fe7fd3d84d502a67d",
1085
1087
  binaryPath: "rust-analyzer-x86_64-apple-darwin"
1086
1088
  },
1087
1089
  "linux-x64": {
1088
1090
  filename: "rust-analyzer-x86_64-unknown-linux-gnu.gz",
1089
- sha256: null,
1091
+ sha256: "ede62b8536a37c90d804850d0bed3623e3d6af90a1195e4930a1627e5d8ae33f",
1090
1092
  binaryPath: "rust-analyzer-x86_64-unknown-linux-gnu"
1091
1093
  },
1092
1094
  "linux-arm64": {
1093
1095
  filename: "rust-analyzer-aarch64-unknown-linux-gnu.gz",
1094
- sha256: null,
1096
+ sha256: "dbe8c4c0bed79f36133c9d0a0eb629709d91343e065a179cd4a5f8137817de1c",
1095
1097
  binaryPath: "rust-analyzer-aarch64-unknown-linux-gnu"
1096
1098
  },
1097
1099
  "win32-x64": {
1098
1100
  filename: "rust-analyzer-x86_64-pc-windows-msvc.zip",
1099
- sha256: null,
1101
+ sha256: "1600365f2d8b0e42a811eafb5e129b75b991ddf9d9955d19d8fc2f4f2b9c19ef",
1100
1102
  binaryPath: "rust-analyzer.exe"
1101
1103
  },
1102
1104
  "win32-arm64": {
1103
1105
  filename: "rust-analyzer-aarch64-pc-windows-msvc.zip",
1104
- sha256: null,
1106
+ sha256: "3cd63d31e9abfea89488eebfd2c29d41550af6cb6f6b32f52e455fb783215fd0",
1105
1107
  binaryPath: "rust-analyzer.exe"
1106
1108
  }
1107
1109
  }
1108
1110
  },
1109
1111
  clangd: {
1110
- repo: "clangd/clangd",
1112
+ source: { kind: "github-release", repo: "clangd/clangd" },
1111
1113
  version: "18.1.3",
1112
1114
  license: "Apache-2.0 WITH LLVM-exception",
1113
1115
  assets: {
@@ -1115,22 +1117,22 @@ var init_lsp_versions = __esm({
1115
1117
  // architectures — same filename for darwin-arm64 and darwin-x64.
1116
1118
  "darwin-arm64": {
1117
1119
  filename: "clangd-mac-18.1.3.zip",
1118
- sha256: null,
1120
+ sha256: "442c18d98671d8b5d811b25002ad1f2fd0b9efb07e7905a41502f1f1311dc566",
1119
1121
  binaryPath: "clangd_18.1.3/bin/clangd"
1120
1122
  },
1121
1123
  "darwin-x64": {
1122
1124
  filename: "clangd-mac-18.1.3.zip",
1123
- sha256: null,
1125
+ sha256: "442c18d98671d8b5d811b25002ad1f2fd0b9efb07e7905a41502f1f1311dc566",
1124
1126
  binaryPath: "clangd_18.1.3/bin/clangd"
1125
1127
  },
1126
1128
  "linux-x64": {
1127
1129
  filename: "clangd-linux-18.1.3.zip",
1128
- sha256: null,
1130
+ sha256: "775f6ac798d9c599258e3f4fa14d0af908b1801d38250e72a18fb907b28a1be6",
1129
1131
  binaryPath: "clangd_18.1.3/bin/clangd"
1130
1132
  },
1131
1133
  "win32-x64": {
1132
1134
  filename: "clangd-windows-18.1.3.zip",
1133
- sha256: null,
1135
+ sha256: "3d287d39bab29406f002ff80b939668e59e25ef72346d8f38826305016e45896",
1134
1136
  binaryPath: "clangd_18.1.3/bin/clangd.exe"
1135
1137
  }
1136
1138
  }
@@ -1141,36 +1143,123 @@ var init_lsp_versions = __esm({
1141
1143
  // GitHub-release Tier 1 entry; the magic is what we DO with it after
1142
1144
  // install (see coursier-installer.ts).
1143
1145
  coursier: {
1144
- repo: "coursier/coursier",
1146
+ source: { kind: "github-release", repo: "coursier/coursier" },
1145
1147
  version: "v2.1.24",
1146
1148
  license: "Apache-2.0",
1147
1149
  assets: {
1148
1150
  "darwin-arm64": {
1149
1151
  filename: "cs-aarch64-apple-darwin.gz",
1150
- sha256: null,
1152
+ sha256: "53a5728c2016118c8296fa7d5678ddfe122e22dc6c36deb554d771b3b9295b4f",
1151
1153
  binaryPath: "cs"
1152
1154
  },
1153
1155
  "darwin-x64": {
1154
1156
  filename: "cs-x86_64-apple-darwin.gz",
1155
- sha256: null,
1157
+ sha256: "33913cd6b61658035d9e6fe971e919cb0ef1f659aa7bff7deeded963a2d36385",
1156
1158
  binaryPath: "cs"
1157
1159
  },
1158
1160
  "linux-x64": {
1159
1161
  filename: "cs-x86_64-pc-linux.gz",
1160
- sha256: null,
1161
- binaryPath: "cs"
1162
- },
1163
- "linux-arm64": {
1164
- filename: "cs-aarch64-pc-linux-static.gz",
1165
- sha256: null,
1162
+ sha256: "d2c0572a17fb6146ea65349b59dd216b38beff60ae22bce6e549867c6ed2eda6",
1166
1163
  binaryPath: "cs"
1167
1164
  },
1165
+ // coursier v2.1.24 ships no aarch64-linux build — Scala dev on
1166
+ // ARM Linux falls back to the toolchain-installed Metals path.
1168
1167
  "win32-x64": {
1169
1168
  filename: "cs-x86_64-pc-win32.zip",
1170
- sha256: null,
1169
+ sha256: "c16b4f95b59fbe035cdda4353cfca78befcbbef5a20a3b151c679717c1504c9f",
1171
1170
  binaryPath: "cs.exe"
1172
1171
  }
1173
1172
  }
1173
+ },
1174
+ // Eclipse JDT Language Server — single cross-platform tarball
1175
+ // hosted at download.eclipse.org. Ships as an Equinox launcher jar
1176
+ // that must be invoked via `java -jar`, so the installer generates
1177
+ // a `bin/jdtls` shim that selects the right config_{platform} dir.
1178
+ // Requires JDK 17+ (jdtls 1.51 minimum).
1179
+ jdtls: {
1180
+ source: { kind: "direct-url" },
1181
+ version: "1.51.0",
1182
+ license: "EPL-2.0",
1183
+ requiresJdk: 17,
1184
+ assets: {
1185
+ "darwin-arm64": {
1186
+ url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
1187
+ filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
1188
+ sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
1189
+ binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
1190
+ launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
1191
+ wrapperBinaryName: "jdtls"
1192
+ },
1193
+ "darwin-x64": {
1194
+ url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
1195
+ filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
1196
+ sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
1197
+ binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
1198
+ launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
1199
+ wrapperBinaryName: "jdtls"
1200
+ },
1201
+ "linux-x64": {
1202
+ url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
1203
+ filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
1204
+ sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
1205
+ binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
1206
+ launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
1207
+ wrapperBinaryName: "jdtls"
1208
+ },
1209
+ "linux-arm64": {
1210
+ url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
1211
+ filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
1212
+ sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
1213
+ binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
1214
+ launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
1215
+ wrapperBinaryName: "jdtls"
1216
+ },
1217
+ "win32-x64": {
1218
+ url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
1219
+ filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
1220
+ sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
1221
+ binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
1222
+ launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
1223
+ wrapperBinaryName: "jdtls"
1224
+ }
1225
+ }
1226
+ },
1227
+ // fwcd/kotlin-language-server — single cross-platform zip on GitHub
1228
+ // releases. Ships a `bin/kotlin-language-server` shell script + a
1229
+ // matching `.bat` for Windows — both invoke `java` internally, so
1230
+ // we just chmod+x and symlink (no shim needed). Requires JDK 11+.
1231
+ "kotlin-language-server": {
1232
+ source: { kind: "github-release", repo: "fwcd/kotlin-language-server" },
1233
+ version: "1.3.13",
1234
+ license: "MIT",
1235
+ requiresJdk: 11,
1236
+ assets: {
1237
+ "darwin-arm64": {
1238
+ filename: "server.zip",
1239
+ sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
1240
+ binaryPath: "server/bin/kotlin-language-server"
1241
+ },
1242
+ "darwin-x64": {
1243
+ filename: "server.zip",
1244
+ sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
1245
+ binaryPath: "server/bin/kotlin-language-server"
1246
+ },
1247
+ "linux-x64": {
1248
+ filename: "server.zip",
1249
+ sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
1250
+ binaryPath: "server/bin/kotlin-language-server"
1251
+ },
1252
+ "linux-arm64": {
1253
+ filename: "server.zip",
1254
+ sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
1255
+ binaryPath: "server/bin/kotlin-language-server"
1256
+ },
1257
+ "win32-x64": {
1258
+ filename: "server.zip",
1259
+ sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
1260
+ binaryPath: "server/bin/kotlin-language-server.bat"
1261
+ }
1262
+ }
1174
1263
  }
1175
1264
  };
1176
1265
  COURSIER_LSPS = {
@@ -1242,11 +1331,79 @@ var init_src = __esm({
1242
1331
  }
1243
1332
  });
1244
1333
 
1334
+ // ../listener/dist/lsp/jdk-probe.js
1335
+ import { spawn as spawn3 } from "child_process";
1336
+ function parseJavaMajorVersion(stderr) {
1337
+ const modernMatch = /\b(?:openjdk|java) version "(\d+)(?:\.\d+)*(?:[._]\w+)?"/.exec(stderr);
1338
+ if (!modernMatch?.[1])
1339
+ return void 0;
1340
+ const first = Number.parseInt(modernMatch[1], 10);
1341
+ if (!Number.isFinite(first))
1342
+ return void 0;
1343
+ if (first === 1) {
1344
+ const legacyMatch = /\b(?:openjdk|java) version "1\.(\d+)/.exec(stderr);
1345
+ if (legacyMatch?.[1]) {
1346
+ const legacy = Number.parseInt(legacyMatch[1], 10);
1347
+ return Number.isFinite(legacy) ? legacy : void 0;
1348
+ }
1349
+ }
1350
+ return first;
1351
+ }
1352
+ async function probeJdk(options = {}) {
1353
+ const timeoutMs = options.timeoutMs ?? 5e3;
1354
+ return new Promise((resolve5) => {
1355
+ let stderr = "";
1356
+ let settled = false;
1357
+ const child = spawn3("java", ["-version"], { stdio: ["ignore", "pipe", "pipe"] });
1358
+ const timer = setTimeout(() => {
1359
+ if (settled)
1360
+ return;
1361
+ settled = true;
1362
+ try {
1363
+ child.kill("SIGKILL");
1364
+ } catch {
1365
+ }
1366
+ resolve5({ present: false });
1367
+ }, timeoutMs);
1368
+ child.stderr.on("data", (chunk) => {
1369
+ stderr += chunk.toString("utf8");
1370
+ });
1371
+ child.on("error", () => {
1372
+ if (settled)
1373
+ return;
1374
+ settled = true;
1375
+ clearTimeout(timer);
1376
+ resolve5({ present: false });
1377
+ });
1378
+ child.on("close", (code) => {
1379
+ if (settled)
1380
+ return;
1381
+ settled = true;
1382
+ clearTimeout(timer);
1383
+ if (code !== 0) {
1384
+ resolve5({ present: false, rawOutput: stderr.trim() });
1385
+ return;
1386
+ }
1387
+ const major = parseJavaMajorVersion(stderr);
1388
+ if (major === void 0) {
1389
+ resolve5({ present: true, rawOutput: stderr.trim() });
1390
+ return;
1391
+ }
1392
+ resolve5({ present: true, majorVersion: major, rawOutput: stderr.trim() });
1393
+ });
1394
+ });
1395
+ }
1396
+ var init_jdk_probe = __esm({
1397
+ "../listener/dist/lsp/jdk-probe.js"() {
1398
+ "use strict";
1399
+ }
1400
+ });
1401
+
1245
1402
  // ../listener/dist/lsp/native-installer.js
1246
1403
  import { createWriteStream } from "fs";
1247
1404
  import { promises as fs } from "fs";
1248
1405
  import { createGunzip } from "zlib";
1249
- import { spawn as spawn3 } from "child_process";
1406
+ import { spawn as spawn4 } from "child_process";
1250
1407
  import { pipeline } from "stream/promises";
1251
1408
  import { createHash as createHash2 } from "crypto";
1252
1409
  import { join as join14, dirname as dirname5 } from "path";
@@ -1268,16 +1425,43 @@ async function ensureNativeLspInstalled(lspKey) {
1268
1425
  if (!asset) {
1269
1426
  return { installed: false, alreadyPresent: false, skipped: "unsupported-platform" };
1270
1427
  }
1428
+ if (entry.requiresJdk !== void 0) {
1429
+ const jdk = await probeJdk();
1430
+ if (!jdk.present || jdk.majorVersion === void 0 || jdk.majorVersion < entry.requiresJdk) {
1431
+ return {
1432
+ installed: false,
1433
+ alreadyPresent: false,
1434
+ skipped: "jvm-missing",
1435
+ error: `${lspKey} requires JDK ${entry.requiresJdk.toString()}+ (found: ${jdk.present ? `Java ${jdk.majorVersion?.toString() ?? "?"}` : "no java on PATH"}). Install with: brew install openjdk@21 (macOS) or apt install openjdk-21-jdk (Linux)`
1436
+ };
1437
+ }
1438
+ }
1439
+ const usesWrapper = Boolean(asset.launcherJarGlob && asset.wrapperBinaryName);
1271
1440
  const versionDir = join14(getNativeLspDir(), lspKey, entry.version);
1272
- const installedBinary = join14(versionDir, asset.binaryPath);
1273
- if (await pathExists(installedBinary)) {
1274
- await ensureSymlinkInBin(lspKey, installedBinary);
1275
- return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
1441
+ if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
1442
+ const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
1443
+ const shimPath = join14(getNativeLspBinDir(), asset.wrapperBinaryName);
1444
+ if (jarPath && await pathExists(shimPath)) {
1445
+ return { installed: false, alreadyPresent: true, binaryPath: shimPath };
1446
+ }
1447
+ } else {
1448
+ const installedBinary = join14(versionDir, asset.binaryPath);
1449
+ if (await pathExists(installedBinary)) {
1450
+ await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
1451
+ return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
1452
+ }
1276
1453
  }
1277
1454
  try {
1278
1455
  await fs.mkdir(versionDir, { recursive: true });
1279
1456
  const tmpDownload = join14(versionDir, `.${asset.filename}.download`);
1280
- const downloadUrl = `https://github.com/${entry.repo}/releases/download/${entry.version}/${asset.filename}`;
1457
+ const downloadUrl = entry.source.kind === "github-release" ? `https://github.com/${entry.source.repo}/releases/download/${entry.version}/${asset.filename}` : asset.url ?? "";
1458
+ if (!downloadUrl) {
1459
+ return {
1460
+ installed: false,
1461
+ alreadyPresent: false,
1462
+ error: `direct-url asset for ${lspKey} missing required \`url\` field`
1463
+ };
1464
+ }
1281
1465
  await downloadFile(downloadUrl, tmpDownload);
1282
1466
  if (asset.sha256) {
1283
1467
  const actual = await sha256File(tmpDownload);
@@ -1290,10 +1474,28 @@ async function ensureNativeLspInstalled(lspKey) {
1290
1474
  };
1291
1475
  }
1292
1476
  } else {
1293
- console.warn(`[lsp-install] WARN: SHA256 verification skipped for ${lspKey} (no pin in NATIVE_LSP_VERSIONS) \u2014 please pin in a follow-up commit`);
1477
+ console.warn(`[lsp-install] WARN: SHA256 verification skipped for ${lspKey} (no pin in NATIVE_LSP_VERSIONS) \u2014 run apps/listener/scripts/pin-native-shas.ts to record one`);
1294
1478
  }
1295
1479
  await extractAsset(tmpDownload, versionDir, asset);
1296
1480
  await fs.unlink(tmpDownload).catch(() => void 0);
1481
+ if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
1482
+ const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
1483
+ if (!jarPath) {
1484
+ return {
1485
+ installed: false,
1486
+ alreadyPresent: false,
1487
+ error: `extract completed but no JAR matched ${asset.launcherJarGlob} in ${versionDir}`
1488
+ };
1489
+ }
1490
+ const shimPath = await writeJdtlsWrapper({
1491
+ binaryName: asset.wrapperBinaryName,
1492
+ versionDir,
1493
+ launcherJarPath: jarPath
1494
+ });
1495
+ await pruneStaleVersions(lspKey, entry.version);
1496
+ return { installed: true, alreadyPresent: false, binaryPath: shimPath };
1497
+ }
1498
+ const installedBinary = join14(versionDir, asset.binaryPath);
1297
1499
  if (!await pathExists(installedBinary)) {
1298
1500
  return {
1299
1501
  installed: false,
@@ -1302,7 +1504,8 @@ async function ensureNativeLspInstalled(lspKey) {
1302
1504
  };
1303
1505
  }
1304
1506
  await fs.chmod(installedBinary, 493).catch(() => void 0);
1305
- await ensureSymlinkInBin(lspKey, installedBinary);
1507
+ await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
1508
+ await pruneStaleVersions(lspKey, entry.version);
1306
1509
  return { installed: true, alreadyPresent: false, binaryPath: installedBinary };
1307
1510
  } catch (err) {
1308
1511
  return {
@@ -1312,10 +1515,112 @@ async function ensureNativeLspInstalled(lspKey) {
1312
1515
  };
1313
1516
  }
1314
1517
  }
1315
- async function ensureSymlinkInBin(lspKey, target) {
1518
+ async function resolveGlobInsideDir(versionDir, glob) {
1519
+ const parts = glob.split("/");
1520
+ let dir = versionDir;
1521
+ for (let i = 0; i < parts.length - 1; i += 1) {
1522
+ dir = join14(dir, parts[i]);
1523
+ }
1524
+ const pattern = parts[parts.length - 1];
1525
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1526
+ const re = new RegExp(`^${escaped}$`);
1527
+ let entries;
1528
+ try {
1529
+ entries = await fs.readdir(dir);
1530
+ } catch {
1531
+ return null;
1532
+ }
1533
+ const matches = entries.filter((e) => re.test(e)).sort();
1534
+ const last = matches[matches.length - 1];
1535
+ return last ? join14(dir, last) : null;
1536
+ }
1537
+ async function pruneStaleVersions(lspKey, currentVersion) {
1538
+ const lspDir = join14(getNativeLspDir(), lspKey);
1539
+ let entries;
1540
+ try {
1541
+ entries = await fs.readdir(lspDir);
1542
+ } catch {
1543
+ return;
1544
+ }
1545
+ await Promise.all(entries.filter((name) => name !== currentVersion).map(async (name) => {
1546
+ try {
1547
+ await fs.rm(join14(lspDir, name), { recursive: true, force: true });
1548
+ } catch {
1549
+ }
1550
+ }));
1551
+ }
1552
+ function shQuote(value) {
1553
+ return value.replace(/[\\"$`]/g, (ch) => `\\${ch}`);
1554
+ }
1555
+ function cmdQuote(value) {
1556
+ return value.replace(/"/g, '""').replace(/%/g, "%%");
1557
+ }
1558
+ async function writeJdtlsWrapper(input) {
1559
+ const binDir = getNativeLspBinDir();
1560
+ await fs.mkdir(binDir, { recursive: true });
1561
+ const shimPath = join14(binDir, input.binaryName);
1562
+ const platformCfgDir = process.platform === "darwin" ? "config_mac" : process.platform === "win32" ? "config_win" : "config_linux";
1563
+ const workspaceBase = join14(input.versionDir, "workspaces");
1564
+ const qLauncher = shQuote(input.launcherJarPath);
1565
+ const qConfig = shQuote(`${input.versionDir}/${platformCfgDir}`);
1566
+ const qWorkspaceBase = shQuote(workspaceBase);
1567
+ const unixScript = `#!/usr/bin/env bash
1568
+ # Generated by repowise listener \u2014 do not edit by hand.
1569
+ # Invokes Eclipse JDT.LS via the equinox launcher.
1570
+ set -euo pipefail
1571
+ WS_NAME="\${REPOWISE_JDTLS_WS_NAME:-default}"
1572
+ WS_DIR="${qWorkspaceBase}/\${WS_NAME}"
1573
+ mkdir -p "$WS_DIR"
1574
+ exec java \\
1575
+ -Declipse.application=org.eclipse.jdt.ls.core.id1 \\
1576
+ -Dosgi.bundles.defaultStartLevel=4 \\
1577
+ -Declipse.product=org.eclipse.jdt.ls.core.product \\
1578
+ -Dlog.level=ALL \\
1579
+ -Xms1g \\
1580
+ --add-modules=ALL-SYSTEM \\
1581
+ --add-opens java.base/java.util=ALL-UNNAMED \\
1582
+ --add-opens java.base/java.lang=ALL-UNNAMED \\
1583
+ -jar "${qLauncher}" \\
1584
+ -configuration "${qConfig}" \\
1585
+ -data "$WS_DIR" \\
1586
+ "$@"
1587
+ `;
1588
+ await fs.writeFile(shimPath, unixScript);
1589
+ await fs.chmod(shimPath, 493);
1590
+ if (process.platform === "win32") {
1591
+ const winShim = `${shimPath}.cmd`;
1592
+ const cqLauncher = cmdQuote(input.launcherJarPath);
1593
+ const cqConfig = cmdQuote(`${input.versionDir}\\${platformCfgDir}`);
1594
+ const cqWorkspaceBase = cmdQuote(workspaceBase);
1595
+ const winScript = `@echo off\r
1596
+ rem Generated by repowise listener\r
1597
+ if "%REPOWISE_JDTLS_WS_NAME%"=="" set REPOWISE_JDTLS_WS_NAME=default\r
1598
+ set WS_DIR=${cqWorkspaceBase}\\%REPOWISE_JDTLS_WS_NAME%\r
1599
+ if not exist "%WS_DIR%" mkdir "%WS_DIR%"\r
1600
+ java ^\r
1601
+ -Declipse.application=org.eclipse.jdt.ls.core.id1 ^\r
1602
+ -Dosgi.bundles.defaultStartLevel=4 ^\r
1603
+ -Declipse.product=org.eclipse.jdt.ls.core.product ^\r
1604
+ -Dlog.level=ALL ^\r
1605
+ -Xms1g ^\r
1606
+ --add-modules=ALL-SYSTEM ^\r
1607
+ --add-opens java.base/java.util=ALL-UNNAMED ^\r
1608
+ --add-opens java.base/java.lang=ALL-UNNAMED ^\r
1609
+ -jar "${cqLauncher}" ^\r
1610
+ -configuration "${cqConfig}" ^\r
1611
+ -data "%WS_DIR%" ^\r
1612
+ %*\r
1613
+ `;
1614
+ await fs.writeFile(winShim, winScript);
1615
+ }
1616
+ return shimPath;
1617
+ }
1618
+ async function ensureSymlinkInBin(lspKey, target, sourceAssetPath) {
1316
1619
  const binDir = getNativeLspBinDir();
1317
1620
  await fs.mkdir(binDir, { recursive: true });
1318
- const linkPath = join14(binDir, lspKey);
1621
+ const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
1622
+ const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
1623
+ const linkPath = join14(binDir, linkName);
1319
1624
  try {
1320
1625
  await fs.unlink(linkPath);
1321
1626
  } catch {
@@ -1373,7 +1678,7 @@ async function extractAsset(source, destDir, asset) {
1373
1678
  }
1374
1679
  async function runExternal(cmd, args) {
1375
1680
  return new Promise((resolve5, reject) => {
1376
- const child = spawn3(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1681
+ const child = spawn4(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1377
1682
  let stderr = "";
1378
1683
  child.stderr.on("data", (chunk) => {
1379
1684
  stderr += chunk.toString();
@@ -1400,12 +1705,13 @@ var init_native_installer = __esm({
1400
1705
  "use strict";
1401
1706
  init_src();
1402
1707
  init_installer();
1708
+ init_jdk_probe();
1403
1709
  }
1404
1710
  });
1405
1711
 
1406
1712
  // ../listener/dist/lsp/coursier-installer.js
1407
1713
  import { promises as fs2 } from "fs";
1408
- import { spawn as spawn4 } from "child_process";
1714
+ import { spawn as spawn5 } from "child_process";
1409
1715
  import { join as join15 } from "path";
1410
1716
  function getCoursierBinDir() {
1411
1717
  return join15(getLspInstallDir(), "coursier-bin");
@@ -1462,7 +1768,7 @@ async function ensureCoursierLspInstalled(key) {
1462
1768
  }
1463
1769
  async function runCs(cs, args) {
1464
1770
  return new Promise((resolve5, reject) => {
1465
- const child = spawn4(cs, args, { stdio: ["ignore", "pipe", "pipe"] });
1771
+ const child = spawn5(cs, args, { stdio: ["ignore", "pipe", "pipe"] });
1466
1772
  let stderr = "";
1467
1773
  child.stderr.on("data", (chunk) => {
1468
1774
  stderr += chunk.toString();
@@ -1494,7 +1800,7 @@ var init_coursier_installer = __esm({
1494
1800
  });
1495
1801
 
1496
1802
  // ../listener/dist/lsp/toolchain-installer.js
1497
- import { spawn as spawn5 } from "child_process";
1803
+ import { spawn as spawn6 } from "child_process";
1498
1804
  import { promises as fs3 } from "fs";
1499
1805
  import { homedir as homedir4 } from "os";
1500
1806
  import { join as join16 } from "path";
@@ -1589,7 +1895,7 @@ function getToolchainBinDirs() {
1589
1895
  }
1590
1896
  function runToolchain(cmd, args) {
1591
1897
  return new Promise((resolve5, reject) => {
1592
- const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1898
+ const child = spawn6(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1593
1899
  let stderr = "";
1594
1900
  child.stderr.on("data", (chunk) => {
1595
1901
  stderr += chunk.toString();
@@ -1605,7 +1911,7 @@ function runToolchain(cmd, args) {
1605
1911
  }
1606
1912
  function captureStdout(cmd, args) {
1607
1913
  return new Promise((resolve5, reject) => {
1608
- const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1914
+ const child = spawn6(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
1609
1915
  let stdout = "";
1610
1916
  let stderr = "";
1611
1917
  child.stdout.on("data", (chunk) => {
@@ -1628,7 +1934,7 @@ async function isCommandOnPath(command) {
1628
1934
  return false;
1629
1935
  const probeCmd = process.platform === "win32" ? "where" : "which";
1630
1936
  return new Promise((resolve5) => {
1631
- const child = spawn5(probeCmd, [command], { stdio: "ignore", timeout: 5e3 });
1937
+ const child = spawn6(probeCmd, [command], { stdio: "ignore", timeout: 5e3 });
1632
1938
  child.on("error", () => resolve5(false));
1633
1939
  child.on("close", (code) => resolve5(code === 0));
1634
1940
  });
@@ -1651,7 +1957,7 @@ var init_toolchain_installer = __esm({
1651
1957
  // ../listener/dist/lsp/installer.js
1652
1958
  import { promises as fs4 } from "fs";
1653
1959
  import { join as join17, dirname as dirname6 } from "path";
1654
- import { spawn as spawn6 } from "child_process";
1960
+ import { spawn as spawn7 } from "child_process";
1655
1961
  function getLspInstallDir() {
1656
1962
  return join17(getConfigDir(), "lsp-servers");
1657
1963
  }
@@ -1710,7 +2016,7 @@ async function runNpmInstall(cwd, pkg2) {
1710
2016
  const nodeDir = dirname6(process.execPath);
1711
2017
  const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
1712
2018
  return new Promise((resolve5, reject) => {
1713
- const child = spawn6(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
2019
+ const child = spawn7(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
1714
2020
  cwd,
1715
2021
  stdio: ["ignore", "pipe", "pipe"],
1716
2022
  env: { ...process.env, PATH: augmentedPath }
@@ -1834,6 +2140,7 @@ async function prepareLspServersForRepos(repos, options = {}) {
1834
2140
  alreadyPresent: outcome.alreadyPresent,
1835
2141
  skippedNoNpmPackage: false,
1836
2142
  installMethod: "native",
2143
+ skipped: outcome.skipped,
1837
2144
  hint: outcome.skipped ? nativeConfig.installHint : void 0,
1838
2145
  error: outcome.error
1839
2146
  });
@@ -1848,6 +2155,7 @@ async function prepareLspServersForRepos(repos, options = {}) {
1848
2155
  alreadyPresent: outcome.alreadyPresent,
1849
2156
  skippedNoNpmPackage: false,
1850
2157
  installMethod: "coursier",
2158
+ skipped: outcome.skipped,
1851
2159
  hint: outcome.skipped ? coursierConfig.installHint : void 0,
1852
2160
  error: outcome.error
1853
2161
  });
@@ -1862,6 +2170,7 @@ async function prepareLspServersForRepos(repos, options = {}) {
1862
2170
  alreadyPresent: outcome.alreadyPresent,
1863
2171
  skippedNoNpmPackage: false,
1864
2172
  installMethod: "toolchain",
2173
+ skipped: outcome.skipped,
1865
2174
  // Surface the install hint for any skip reason — toolchain
1866
2175
  // missing means user needs to install it; bundled SDK means
1867
2176
  // user needs the SDK.
@@ -2524,6 +2833,82 @@ var init_lsp_tools = __esm({
2524
2833
  }
2525
2834
  });
2526
2835
 
2836
+ // ../listener/dist/lsp/telemetry.js
2837
+ var telemetry_exports = {};
2838
+ __export(telemetry_exports, {
2839
+ postLspInstallOutcomes: () => postLspInstallOutcomes
2840
+ });
2841
+ function toOutcomes(results) {
2842
+ return results.map((r) => {
2843
+ const skipped = r.skipped ?? (r.skippedNoNpmPackage ? "no-install-method" : void 0);
2844
+ return {
2845
+ language: r.language,
2846
+ ...r.installMethod ? { installMethod: r.installMethod } : {},
2847
+ installed: r.installed,
2848
+ alreadyPresent: r.alreadyPresent,
2849
+ ...skipped ? { skipped } : {},
2850
+ ...r.error ? { error: r.error.slice(0, 500) } : {}
2851
+ };
2852
+ });
2853
+ }
2854
+ async function postLspInstallOutcomes(input) {
2855
+ if (process.env["REPOWISE_LSP_INSTALL_TELEMETRY_DISABLED"] === "true")
2856
+ return;
2857
+ if (input.results.length === 0)
2858
+ return;
2859
+ const fetchImpl = input.fetchImpl ?? fetch;
2860
+ const url = `${input.apiUrl.replace(/\/+$/, "")}/v1/listeners/lsp-install-outcome`;
2861
+ const body = JSON.stringify({
2862
+ repoId: input.repoId,
2863
+ outcomes: toOutcomes(input.results)
2864
+ });
2865
+ const attempt = async () => {
2866
+ const ctrl = new AbortController();
2867
+ const timer = setTimeout(() => {
2868
+ ctrl.abort();
2869
+ }, REQUEST_TIMEOUT_MS);
2870
+ try {
2871
+ const res = await fetchImpl(url, {
2872
+ method: "POST",
2873
+ headers: {
2874
+ "Content-Type": "application/json",
2875
+ Authorization: `Bearer ${input.authToken}`
2876
+ },
2877
+ body,
2878
+ signal: ctrl.signal
2879
+ });
2880
+ const retriable = res.status >= 500 || res.status === 429;
2881
+ return { ok: res.ok, status: res.status, retriable };
2882
+ } catch {
2883
+ return { ok: false, status: 0, retriable: true };
2884
+ } finally {
2885
+ clearTimeout(timer);
2886
+ }
2887
+ };
2888
+ try {
2889
+ const first = await attempt();
2890
+ if (first.ok)
2891
+ return;
2892
+ if (!first.retriable) {
2893
+ console.warn(`[lsp-telemetry] upload rejected (${first.status.toString()}) \u2014 not retrying`);
2894
+ return;
2895
+ }
2896
+ const second = await attempt();
2897
+ if (!second.ok) {
2898
+ console.warn(`[lsp-telemetry] upload failed after retry (status=${second.status.toString()})`);
2899
+ }
2900
+ } catch (err) {
2901
+ console.warn(`[lsp-telemetry] unexpected failure: ${err instanceof Error ? err.message : String(err)}`);
2902
+ }
2903
+ }
2904
+ var REQUEST_TIMEOUT_MS;
2905
+ var init_telemetry = __esm({
2906
+ "../listener/dist/lsp/telemetry.js"() {
2907
+ "use strict";
2908
+ REQUEST_TIMEOUT_MS = 5e3;
2909
+ }
2910
+ });
2911
+
2527
2912
  // bin/repowise.ts
2528
2913
  import { readFileSync as readFileSync3 } from "fs";
2529
2914
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -4120,7 +4505,7 @@ init_coursier_installer();
4120
4505
  init_toolchain_installer();
4121
4506
 
4122
4507
  // ../listener/dist/lsp/bloop-lifecycle.js
4123
- import { spawn as spawn7 } from "child_process";
4508
+ import { spawn as spawn8 } from "child_process";
4124
4509
  function createDefaultBloopLauncher() {
4125
4510
  const attachedRepos = /* @__PURE__ */ new Set();
4126
4511
  return {
@@ -4135,7 +4520,7 @@ function createDefaultBloopLauncher() {
4135
4520
  return new Promise((resolveClose) => {
4136
4521
  let child;
4137
4522
  try {
4138
- child = spawn7("bloop", ["exit"], { stdio: "ignore" });
4523
+ child = spawn8("bloop", ["exit"], { stdio: "ignore" });
4139
4524
  } catch {
4140
4525
  resolveClose();
4141
4526
  return;
@@ -7215,7 +7600,7 @@ init_installer();
7215
7600
  // ../listener/dist/lib/workspace-prep.js
7216
7601
  init_config_dir();
7217
7602
  import { promises as fs14 } from "fs";
7218
- import { spawn as spawn8, execFile as execFile4 } from "child_process";
7603
+ import { spawn as spawn9, execFile as execFile4 } from "child_process";
7219
7604
  import { createWriteStream as createWriteStream2 } from "fs";
7220
7605
  import { join as join30 } from "path";
7221
7606
  import { promisify as promisify3 } from "util";
@@ -7501,7 +7886,7 @@ $ ${cmd.join(" ")}
7501
7886
  reject(new Error("spawnSimple called with empty command"));
7502
7887
  return;
7503
7888
  }
7504
- const child = spawn8(head, cmd.slice(1), {
7889
+ const child = spawn9(head, cmd.slice(1), {
7505
7890
  cwd: repoRoot,
7506
7891
  stdio: ["ignore", "pipe", "pipe"]
7507
7892
  });
@@ -8912,6 +9297,27 @@ async function startListener() {
8912
9297
  console.warn(`[lsp-install] ${r.language} install failed: ${r.error}`);
8913
9298
  }
8914
9299
  }
9300
+ if (lspResults.length > 0) {
9301
+ const { postLspInstallOutcomes: postLspInstallOutcomes2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
9302
+ for (const repo of config2.repos) {
9303
+ const apiUrl = repo.apiUrl ?? config2.defaultApiUrl;
9304
+ if (!apiUrl)
9305
+ continue;
9306
+ try {
9307
+ const fresh = await getValidCredentials();
9308
+ if (!fresh)
9309
+ continue;
9310
+ await postLspInstallOutcomes2({
9311
+ apiUrl,
9312
+ authToken: fresh.accessToken,
9313
+ repoId: repo.repoId,
9314
+ results: lspResults
9315
+ });
9316
+ } catch (telErr) {
9317
+ console.warn("[lsp-telemetry] upload skipped:", telErr instanceof Error ? telErr.message : String(telErr));
9318
+ }
9319
+ }
9320
+ }
8915
9321
  } catch (err) {
8916
9322
  console.warn("[lsp-install] Pre-install scan failed:", err instanceof Error ? err.message : String(err));
8917
9323
  }
@@ -10478,7 +10884,7 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
10478
10884
  }
10479
10885
 
10480
10886
  // src/lib/dep-installer-runner.ts
10481
- import { spawn as spawn9 } from "child_process";
10887
+ import { spawn as spawn10 } from "child_process";
10482
10888
  import { createWriteStream as createWriteStream3 } from "fs";
10483
10889
  import { promises as fs16 } from "fs";
10484
10890
  import { join as join38 } from "path";
@@ -10545,7 +10951,7 @@ $ ${cmd.join(" ")}
10545
10951
  reject(new Error("runSimple called with empty command"));
10546
10952
  return;
10547
10953
  }
10548
- const child = spawn9(head, cmd.slice(1), {
10954
+ const child = spawn10(head, cmd.slice(1), {
10549
10955
  cwd: repoRoot,
10550
10956
  stdio: ["ignore", "pipe", "pipe"]
10551
10957
  });
@@ -13275,7 +13681,7 @@ init_registry();
13275
13681
  init_native_installer();
13276
13682
  init_coursier_installer();
13277
13683
  init_toolchain_installer();
13278
- import { spawn as spawn10 } from "child_process";
13684
+ import { spawn as spawn11 } from "child_process";
13279
13685
  import { resolve as resolve4 } from "path";
13280
13686
  import chalk15 from "chalk";
13281
13687
  async function isOnPath(command) {
@@ -13283,7 +13689,7 @@ async function isOnPath(command) {
13283
13689
  const isWin = process.platform === "win32";
13284
13690
  const probeCmd = isWin ? "where" : "which";
13285
13691
  return new Promise((resolve5) => {
13286
- const child = spawn10(probeCmd, [command], { stdio: "ignore" });
13692
+ const child = spawn11(probeCmd, [command], { stdio: "ignore" });
13287
13693
  child.on("close", (code) => {
13288
13694
  resolve5(code === 0);
13289
13695
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowisestage",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "type": "module",
5
5
  "description": "AI-optimized codebase context generator",
6
6
  "bin": {