repowisestage 0.0.58 → 0.0.60

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 +2012 -702
  2. package/package.json +1 -1
@@ -25,19 +25,19 @@ var init_config_dir = __esm({
25
25
  // ../listener/dist/process-manager.js
26
26
  import { spawn } from "child_process";
27
27
  import { openSync, closeSync } from "fs";
28
- import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir6, unlink as unlink4 } from "fs/promises";
28
+ import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink5 } from "fs/promises";
29
29
  import { homedir as homedir2 } from "os";
30
- import { join as join9 } from "path";
30
+ import { join as join10 } from "path";
31
31
  import { createRequire } from "module";
32
32
  import { fileURLToPath } from "url";
33
33
  function repowiseDir() {
34
34
  return getConfigDir();
35
35
  }
36
36
  function pidPath() {
37
- return join9(repowiseDir(), "listener.pid");
37
+ return join10(repowiseDir(), "listener.pid");
38
38
  }
39
39
  function logDirPath() {
40
- return join9(repowiseDir(), "logs");
40
+ return join10(repowiseDir(), "logs");
41
41
  }
42
42
  function resolveListenerCommand() {
43
43
  try {
@@ -51,7 +51,7 @@ function resolveListenerCommand() {
51
51
  }
52
52
  async function readPid() {
53
53
  try {
54
- const content = await readFile5(pidPath(), "utf-8");
54
+ const content = await readFile6(pidPath(), "utf-8");
55
55
  const pid = parseInt(content.trim(), 10);
56
56
  return Number.isNaN(pid) ? null : pid;
57
57
  } catch (err) {
@@ -75,10 +75,10 @@ async function startBackground() {
75
75
  return pid2;
76
76
  }
77
77
  const logDir2 = logDirPath();
78
- await mkdir6(logDir2, { recursive: true });
78
+ await mkdir7(logDir2, { recursive: true });
79
79
  const cmd = resolveListenerCommand();
80
- const stdoutFd = openSync(join9(logDir2, "listener-stdout.log"), "a");
81
- const stderrFd = openSync(join9(logDir2, "listener-stderr.log"), "a");
80
+ const stdoutFd = openSync(join10(logDir2, "listener-stdout.log"), "a");
81
+ const stderrFd = openSync(join10(logDir2, "listener-stderr.log"), "a");
82
82
  const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
83
83
  detached: true,
84
84
  stdio: ["ignore", stdoutFd, stderrFd],
@@ -91,7 +91,7 @@ async function startBackground() {
91
91
  const pid = child.pid;
92
92
  if (!pid)
93
93
  throw new Error("Failed to spawn listener process");
94
- await writeFile6(pidPath(), String(pid));
94
+ await writeFile7(pidPath(), String(pid));
95
95
  return pid;
96
96
  }
97
97
  async function stopProcess() {
@@ -126,7 +126,7 @@ async function isRunning() {
126
126
  }
127
127
  async function removePidFile() {
128
128
  try {
129
- await unlink4(pidPath());
129
+ await unlink5(pidPath());
130
130
  } catch {
131
131
  }
132
132
  }
@@ -146,13 +146,13 @@ __export(service_installer_exports, {
146
146
  stopService: () => stopService,
147
147
  uninstall: () => uninstall
148
148
  });
149
- import { execFile as execFile3 } from "child_process";
150
- import { writeFile as writeFile7, mkdir as mkdir7, unlink as unlink5 } from "fs/promises";
149
+ import { execFile as execFile4 } from "child_process";
150
+ import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
151
151
  import { homedir as homedir3 } from "os";
152
- import { join as join12 } from "path";
152
+ import { join as join13 } from "path";
153
153
  function exec(cmd, args) {
154
154
  return new Promise((resolve5, reject) => {
155
- execFile3(cmd, args, (err, stdout) => {
155
+ execFile4(cmd, args, (err, stdout) => {
156
156
  if (err) {
157
157
  reject(err);
158
158
  return;
@@ -162,10 +162,10 @@ function exec(cmd, args) {
162
162
  });
163
163
  }
164
164
  function plistPath() {
165
- return join12(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
165
+ return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
166
166
  }
167
167
  function logDir() {
168
- return join12(getConfigDir(), "logs");
168
+ return join13(getConfigDir(), "logs");
169
169
  }
170
170
  function buildPlist() {
171
171
  const cmd = resolveListenerCommand();
@@ -187,9 +187,9 @@ ${programArgs}
187
187
  <key>KeepAlive</key>
188
188
  <true/>
189
189
  <key>StandardOutPath</key>
190
- <string>${join12(logs, "listener-stdout.log")}</string>
190
+ <string>${join13(logs, "listener-stdout.log")}</string>
191
191
  <key>StandardErrorPath</key>
192
- <string>${join12(logs, "listener-stderr.log")}</string>
192
+ <string>${join13(logs, "listener-stderr.log")}</string>
193
193
  <key>ProcessType</key>
194
194
  <string>Background</string>
195
195
  <!--
@@ -217,13 +217,13 @@ ${programArgs}
217
217
  </plist>`;
218
218
  }
219
219
  async function darwinInstall() {
220
- await mkdir7(logDir(), { recursive: true });
221
- await mkdir7(join12(homedir3(), "Library", "LaunchAgents"), { recursive: true });
220
+ await mkdir8(logDir(), { recursive: true });
221
+ await mkdir8(join13(homedir3(), "Library", "LaunchAgents"), { recursive: true });
222
222
  try {
223
223
  await exec("launchctl", ["unload", plistPath()]);
224
224
  } catch {
225
225
  }
226
- await writeFile7(plistPath(), buildPlist());
226
+ await writeFile8(plistPath(), buildPlist());
227
227
  await exec("launchctl", ["load", plistPath()]);
228
228
  }
229
229
  async function darwinUninstall() {
@@ -232,7 +232,7 @@ async function darwinUninstall() {
232
232
  } catch {
233
233
  }
234
234
  try {
235
- await unlink5(plistPath());
235
+ await unlink6(plistPath());
236
236
  } catch {
237
237
  }
238
238
  }
@@ -245,7 +245,7 @@ async function darwinIsInstalled() {
245
245
  }
246
246
  }
247
247
  function unitPath() {
248
- return join12(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
248
+ return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
249
249
  }
250
250
  function buildUnit() {
251
251
  const cmd = resolveListenerCommand();
@@ -261,16 +261,16 @@ Type=simple
261
261
  ExecStart=${execStart}
262
262
  Restart=always
263
263
  RestartSec=10
264
- StandardOutput=append:${join12(logs, "listener-stdout.log")}
265
- StandardError=append:${join12(logs, "listener-stderr.log")}
264
+ StandardOutput=append:${join13(logs, "listener-stdout.log")}
265
+ StandardError=append:${join13(logs, "listener-stderr.log")}
266
266
 
267
267
  [Install]
268
268
  WantedBy=default.target`;
269
269
  }
270
270
  async function linuxInstall() {
271
- await mkdir7(logDir(), { recursive: true });
272
- await mkdir7(join12(homedir3(), ".config", "systemd", "user"), { recursive: true });
273
- await writeFile7(unitPath(), buildUnit());
271
+ await mkdir8(logDir(), { recursive: true });
272
+ await mkdir8(join13(homedir3(), ".config", "systemd", "user"), { recursive: true });
273
+ await writeFile8(unitPath(), buildUnit());
274
274
  await exec("systemctl", ["--user", "daemon-reload"]);
275
275
  await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
276
276
  await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
@@ -285,7 +285,7 @@ async function linuxUninstall() {
285
285
  } catch {
286
286
  }
287
287
  try {
288
- await unlink5(unitPath());
288
+ await unlink6(unitPath());
289
289
  } catch {
290
290
  }
291
291
  try {
@@ -302,7 +302,7 @@ async function linuxIsInstalled() {
302
302
  }
303
303
  }
304
304
  async function win32Install() {
305
- await mkdir7(logDir(), { recursive: true });
305
+ await mkdir8(logDir(), { recursive: true });
306
306
  const cmd = resolveListenerCommand();
307
307
  const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
308
308
  await exec("schtasks", [
@@ -517,7 +517,10 @@ async function probeServers(isAvailable, env = process.env, configOverrides) {
517
517
  }
518
518
  return out;
519
519
  }
520
- var LSP_REGISTRY;
520
+ function getBuildSpec(language) {
521
+ return BUILD_SPECS[language] ?? null;
522
+ }
523
+ var LSP_REGISTRY, BUILD_SPECS;
521
524
  var init_registry = __esm({
522
525
  "../listener/dist/lsp/registry.js"() {
523
526
  "use strict";
@@ -798,7 +801,8 @@ var init_registry = __esm({
798
801
  extensions: [".swift"],
799
802
  lspLanguageId: "swift",
800
803
  installHint: "Bundled with Xcode Command Line Tools on macOS",
801
- toolchainInstallKey: "sourcekit-lsp"
804
+ toolchainInstallKey: "sourcekit-lsp",
805
+ requiresBuild: true
802
806
  }
803
807
  ],
804
808
  dart: [
@@ -913,6 +917,17 @@ var init_registry = __esm({
913
917
  }
914
918
  ]
915
919
  };
920
+ BUILD_SPECS = {
921
+ swift: {
922
+ gateCommands: ["swift", "xcrun"],
923
+ gateExecs: [{ command: "xcode-select", args: ["-p"] }],
924
+ manifests: ["Package.swift", "*.xcodeproj", "*.xcworkspace"],
925
+ platform: "darwin",
926
+ command: "swift",
927
+ args: ["build"],
928
+ indexStoreProbe: ".build/index/store"
929
+ }
930
+ };
916
931
  }
917
932
  });
918
933
 
@@ -1467,12 +1482,12 @@ import { createGunzip } from "zlib";
1467
1482
  import { spawn as spawn4 } from "child_process";
1468
1483
  import { pipeline } from "stream/promises";
1469
1484
  import { createHash as createHash2 } from "crypto";
1470
- import { join as join14, dirname as dirname5 } from "path";
1485
+ import { join as join15, dirname as dirname5 } from "path";
1471
1486
  function getNativeLspDir() {
1472
- return join14(getLspInstallDir(), "native");
1487
+ return join15(getLspInstallDir(), "native");
1473
1488
  }
1474
1489
  function getNativeLspBinDir() {
1475
- return join14(getNativeLspDir(), "bin");
1490
+ return join15(getNativeLspDir(), "bin");
1476
1491
  }
1477
1492
  async function ensureNativeLspInstalled(lspKey) {
1478
1493
  const entry = NATIVE_LSP_VERSIONS[lspKey];
@@ -1498,15 +1513,15 @@ async function ensureNativeLspInstalled(lspKey) {
1498
1513
  }
1499
1514
  }
1500
1515
  const usesWrapper = Boolean(asset.launcherJarGlob && asset.wrapperBinaryName);
1501
- const versionDir = join14(getNativeLspDir(), lspKey, entry.version);
1516
+ const versionDir = join15(getNativeLspDir(), lspKey, entry.version);
1502
1517
  if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
1503
1518
  const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
1504
- const shimPath = join14(getNativeLspBinDir(), asset.wrapperBinaryName);
1519
+ const shimPath = join15(getNativeLspBinDir(), asset.wrapperBinaryName);
1505
1520
  if (jarPath && await pathExists(shimPath)) {
1506
1521
  return { installed: false, alreadyPresent: true, binaryPath: shimPath };
1507
1522
  }
1508
1523
  } else {
1509
- const installedBinary = join14(versionDir, asset.binaryPath);
1524
+ const installedBinary = join15(versionDir, asset.binaryPath);
1510
1525
  if (await pathExists(installedBinary)) {
1511
1526
  await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
1512
1527
  return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
@@ -1514,7 +1529,7 @@ async function ensureNativeLspInstalled(lspKey) {
1514
1529
  }
1515
1530
  try {
1516
1531
  await fs.mkdir(versionDir, { recursive: true });
1517
- const tmpDownload = join14(versionDir, `.${asset.filename}.download`);
1532
+ const tmpDownload = join15(versionDir, `.${asset.filename}.download`);
1518
1533
  const downloadUrl = entry.source.kind === "github-release" ? `https://github.com/${entry.source.repo}/releases/download/${entry.version}/${asset.filename}` : asset.url ?? "";
1519
1534
  if (!downloadUrl) {
1520
1535
  return {
@@ -1556,7 +1571,7 @@ async function ensureNativeLspInstalled(lspKey) {
1556
1571
  await pruneStaleVersions(lspKey, entry.version);
1557
1572
  return { installed: true, alreadyPresent: false, binaryPath: shimPath };
1558
1573
  }
1559
- const installedBinary = join14(versionDir, asset.binaryPath);
1574
+ const installedBinary = join15(versionDir, asset.binaryPath);
1560
1575
  if (!await pathExists(installedBinary)) {
1561
1576
  return {
1562
1577
  installed: false,
@@ -1580,7 +1595,7 @@ async function resolveGlobInsideDir(versionDir, glob) {
1580
1595
  const parts = glob.split("/");
1581
1596
  let dir = versionDir;
1582
1597
  for (let i = 0; i < parts.length - 1; i += 1) {
1583
- dir = join14(dir, parts[i]);
1598
+ dir = join15(dir, parts[i]);
1584
1599
  }
1585
1600
  const pattern = parts[parts.length - 1];
1586
1601
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
@@ -1593,10 +1608,10 @@ async function resolveGlobInsideDir(versionDir, glob) {
1593
1608
  }
1594
1609
  const matches = entries.filter((e) => re.test(e)).sort();
1595
1610
  const last = matches[matches.length - 1];
1596
- return last ? join14(dir, last) : null;
1611
+ return last ? join15(dir, last) : null;
1597
1612
  }
1598
1613
  async function pruneStaleVersions(lspKey, currentVersion) {
1599
- const lspDir = join14(getNativeLspDir(), lspKey);
1614
+ const lspDir = join15(getNativeLspDir(), lspKey);
1600
1615
  let entries;
1601
1616
  try {
1602
1617
  entries = await fs.readdir(lspDir);
@@ -1605,7 +1620,7 @@ async function pruneStaleVersions(lspKey, currentVersion) {
1605
1620
  }
1606
1621
  await Promise.all(entries.filter((name) => name !== currentVersion).map(async (name) => {
1607
1622
  try {
1608
- await fs.rm(join14(lspDir, name), { recursive: true, force: true });
1623
+ await fs.rm(join15(lspDir, name), { recursive: true, force: true });
1609
1624
  } catch {
1610
1625
  }
1611
1626
  }));
@@ -1619,9 +1634,9 @@ function cmdQuote(value) {
1619
1634
  async function writeJdtlsWrapper(input) {
1620
1635
  const binDir = getNativeLspBinDir();
1621
1636
  await fs.mkdir(binDir, { recursive: true });
1622
- const shimPath = join14(binDir, input.binaryName);
1637
+ const shimPath = join15(binDir, input.binaryName);
1623
1638
  const platformCfgDir = process.platform === "darwin" ? "config_mac" : process.platform === "win32" ? "config_win" : "config_linux";
1624
- const workspaceBase = join14(input.versionDir, "workspaces");
1639
+ const workspaceBase = join15(input.versionDir, "workspaces");
1625
1640
  const qLauncher = shQuote(input.launcherJarPath);
1626
1641
  const qConfig = shQuote(`${input.versionDir}/${platformCfgDir}`);
1627
1642
  const qWorkspaceBase = shQuote(workspaceBase);
@@ -1681,7 +1696,7 @@ async function ensureSymlinkInBin(lspKey, target, sourceAssetPath) {
1681
1696
  await fs.mkdir(binDir, { recursive: true });
1682
1697
  const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
1683
1698
  const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
1684
- const linkPath = join14(binDir, linkName);
1699
+ const linkPath = join15(binDir, linkName);
1685
1700
  try {
1686
1701
  await fs.unlink(linkPath);
1687
1702
  } catch {
@@ -1730,12 +1745,12 @@ async function extractAsset(source, destDir, asset) {
1730
1745
  return;
1731
1746
  }
1732
1747
  if (filename.endsWith(".gz")) {
1733
- const outPath = join14(destDir, asset.binaryPath);
1748
+ const outPath = join15(destDir, asset.binaryPath);
1734
1749
  await fs.mkdir(dirname5(outPath), { recursive: true });
1735
1750
  await pipeline((await fs.open(source)).createReadStream(), createGunzip(), createWriteStream(outPath));
1736
1751
  return;
1737
1752
  }
1738
- await fs.copyFile(source, join14(destDir, asset.binaryPath));
1753
+ await fs.copyFile(source, join15(destDir, asset.binaryPath));
1739
1754
  }
1740
1755
  async function runExternal(cmd, args) {
1741
1756
  return new Promise((resolve5, reject) => {
@@ -1773,9 +1788,9 @@ var init_native_installer = __esm({
1773
1788
  // ../listener/dist/lsp/coursier-installer.js
1774
1789
  import { promises as fs2 } from "fs";
1775
1790
  import { spawn as spawn5 } from "child_process";
1776
- import { join as join15 } from "path";
1791
+ import { join as join16 } from "path";
1777
1792
  function getCoursierBinDir() {
1778
- return join15(getLspInstallDir(), "coursier-bin");
1793
+ return join16(getLspInstallDir(), "coursier-bin");
1779
1794
  }
1780
1795
  async function ensureCoursierLspInstalled(key) {
1781
1796
  const entry = COURSIER_LSPS[key];
@@ -1783,8 +1798,8 @@ async function ensureCoursierLspInstalled(key) {
1783
1798
  return { installed: false, alreadyPresent: false, skipped: "no-coursier-entry" };
1784
1799
  }
1785
1800
  const binDir = getCoursierBinDir();
1786
- const binaryPath = join15(binDir, entry.appName);
1787
- const binaryPathExe = join15(binDir, `${entry.appName}.bat`);
1801
+ const binaryPath = join16(binDir, entry.appName);
1802
+ const binaryPathExe = join16(binDir, `${entry.appName}.bat`);
1788
1803
  if (await pathExists2(binaryPath) || await pathExists2(binaryPathExe)) {
1789
1804
  return { installed: false, alreadyPresent: true, binaryPath };
1790
1805
  }
@@ -1797,8 +1812,8 @@ async function ensureCoursierLspInstalled(key) {
1797
1812
  error: csBootstrap.error ?? csBootstrap.skipped
1798
1813
  };
1799
1814
  }
1800
- const csPath = join15(getNativeLspBinDir(), "coursier");
1801
- const csPathExe = join15(getNativeLspBinDir(), "coursier.exe");
1815
+ const csPath = join16(getNativeLspBinDir(), "coursier");
1816
+ const csPathExe = join16(getNativeLspBinDir(), "coursier.exe");
1802
1817
  const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
1803
1818
  if (!cs) {
1804
1819
  return {
@@ -1864,7 +1879,7 @@ var init_coursier_installer = __esm({
1864
1879
  import { spawn as spawn6 } from "child_process";
1865
1880
  import { promises as fs3 } from "fs";
1866
1881
  import { homedir as homedir4 } from "os";
1867
- import { join as join16 } from "path";
1882
+ import { join as join17 } from "path";
1868
1883
  async function ensureToolchainLspInstalled(key) {
1869
1884
  const entry = TOOLCHAIN_LSPS[key];
1870
1885
  if (!entry) {
@@ -1912,7 +1927,7 @@ async function ensureToolchainLspInstalled(key) {
1912
1927
  error: err instanceof Error ? err.message : String(err)
1913
1928
  };
1914
1929
  }
1915
- const binaryPath = join16(binDir, entry.binaryName);
1930
+ const binaryPath = join17(binDir, entry.binaryName);
1916
1931
  if (!await pathExists3(binaryPath)) {
1917
1932
  return {
1918
1933
  installed: false,
@@ -1929,14 +1944,14 @@ async function resolveToolchainBinDir(entry) {
1929
1944
  }
1930
1945
  if (entry.toolchainBinDir) {
1931
1946
  if (entry.toolchain === "go") {
1932
- const gopath = process.env["GOPATH"] ?? join16(homedir4(), "go");
1933
- return join16(gopath, "bin");
1947
+ const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
1948
+ return join17(gopath, "bin");
1934
1949
  }
1935
- return join16(homedir4(), entry.toolchainBinDir);
1950
+ return join17(homedir4(), entry.toolchainBinDir);
1936
1951
  }
1937
1952
  if (entry.toolchain === "gem") {
1938
1953
  const userdir = await captureStdout("gem", ["env", "userdir"]);
1939
- return join16(userdir.trim(), "bin");
1954
+ return join17(userdir.trim(), "bin");
1940
1955
  }
1941
1956
  throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
1942
1957
  }
@@ -1946,10 +1961,10 @@ function getToolchainBinDirs() {
1946
1961
  if (entry.bundled || !entry.toolchain || !entry.toolchainBinDir)
1947
1962
  continue;
1948
1963
  if (entry.toolchain === "go") {
1949
- const gopath = process.env["GOPATH"] ?? join16(homedir4(), "go");
1950
- dirs.add(join16(gopath, "bin"));
1964
+ const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
1965
+ dirs.add(join17(gopath, "bin"));
1951
1966
  } else {
1952
- dirs.add(join16(homedir4(), entry.toolchainBinDir));
1967
+ dirs.add(join17(homedir4(), entry.toolchainBinDir));
1953
1968
  }
1954
1969
  }
1955
1970
  return [...dirs];
@@ -2017,20 +2032,20 @@ var init_toolchain_installer = __esm({
2017
2032
 
2018
2033
  // ../listener/dist/lsp/installer.js
2019
2034
  import { promises as fs4 } from "fs";
2020
- import { join as join17, dirname as dirname6 } from "path";
2035
+ import { join as join18, dirname as dirname6 } from "path";
2021
2036
  import { spawn as spawn7 } from "child_process";
2022
2037
  import lockfile3 from "proper-lockfile";
2023
2038
  function getLspInstallDir() {
2024
- return join17(getConfigDir(), "lsp-servers");
2039
+ return join18(getConfigDir(), "lsp-servers");
2025
2040
  }
2026
2041
  function getLspBinDir() {
2027
- return join17(getLspInstallDir(), "node_modules", ".bin");
2042
+ return join18(getLspInstallDir(), "node_modules", ".bin");
2028
2043
  }
2029
2044
  async function ensureNpmLspInstalled(config2) {
2030
2045
  if (!config2.npmPackage)
2031
2046
  return { installed: false, skipped: "no-npm-package" };
2032
2047
  const installDir = getLspInstallDir();
2033
- const binPath = join17(installDir, "node_modules", ".bin", config2.command);
2048
+ const binPath = join18(installDir, "node_modules", ".bin", config2.command);
2034
2049
  if (await pathExists4(binPath)) {
2035
2050
  return { installed: false, skipped: "already-present" };
2036
2051
  }
@@ -2052,7 +2067,7 @@ async function ensureNpmLspInstalled(config2) {
2052
2067
  if (await pathExists4(binPath)) {
2053
2068
  return { installed: false, skipped: "already-present" };
2054
2069
  }
2055
- const pkgJsonPath = join17(installDir, "package.json");
2070
+ const pkgJsonPath = join18(installDir, "package.json");
2056
2071
  if (!await pathExists4(pkgJsonPath)) {
2057
2072
  await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
2058
2073
  }
@@ -2081,7 +2096,7 @@ async function ensureNpmLspInstalled(config2) {
2081
2096
  }
2082
2097
  async function resolveNpmCommand() {
2083
2098
  const nodeDir = dirname6(process.execPath);
2084
- for (const candidate of [join17(nodeDir, "npm"), join17(nodeDir, "npm.cmd")]) {
2099
+ for (const candidate of [join18(nodeDir, "npm"), join18(nodeDir, "npm.cmd")]) {
2085
2100
  try {
2086
2101
  await fs4.access(candidate);
2087
2102
  return candidate;
@@ -2172,10 +2187,10 @@ async function detectRepoLanguages(repoRoot) {
2172
2187
  continue;
2173
2188
  if (name === "node_modules" || name === "dist" || name === "build")
2174
2189
  continue;
2175
- const sub = join17(repoRoot, name);
2190
+ const sub = join18(repoRoot, name);
2176
2191
  try {
2177
- const stat7 = await fs4.stat(sub);
2178
- if (stat7.isDirectory())
2192
+ const stat8 = await fs4.stat(sub);
2193
+ if (stat8.isDirectory())
2179
2194
  await inspect(sub);
2180
2195
  } catch {
2181
2196
  }
@@ -2188,8 +2203,8 @@ async function prepareLspServersForRepos(repos, options = {}) {
2188
2203
  if (!r.localPath)
2189
2204
  continue;
2190
2205
  try {
2191
- const stat7 = await fs4.stat(r.localPath);
2192
- if (!stat7.isDirectory())
2206
+ const stat8 = await fs4.stat(r.localPath);
2207
+ if (!stat8.isDirectory())
2193
2208
  continue;
2194
2209
  } catch {
2195
2210
  continue;
@@ -2293,8 +2308,8 @@ __export(sidecar_cache_exports, {
2293
2308
  persistMergedSidecar: () => persistMergedSidecar,
2294
2309
  sidecarCachePaths: () => sidecarCachePaths
2295
2310
  });
2296
- import { mkdir as mkdir8, readFile as readFile7, readdir as readdir3, stat as stat2, unlink as unlink7, writeFile as writeFile8 } from "fs/promises";
2297
- import { dirname as dirname8, join as join18 } from "path";
2311
+ import { mkdir as mkdir9, readFile as readFile8, readdir as readdir3, stat as stat2, unlink as unlink8, writeFile as writeFile9 } from "fs/promises";
2312
+ import { dirname as dirname8, join as join19 } from "path";
2298
2313
  function repoIdSafe(repoId) {
2299
2314
  return /^[A-Za-z0-9_.-]{1,128}$/.test(repoId) && !repoId.startsWith(".");
2300
2315
  }
@@ -2308,15 +2323,15 @@ function sidecarCachePaths(repoId, commitSha) {
2308
2323
  if (!commitShaSafe(commitSha)) {
2309
2324
  throw new Error(`unsafe commitSha: ${commitSha}`);
2310
2325
  }
2311
- const repoDir = join18(getConfigDir(), "typed-resolution", repoId);
2312
- const fullPath = join18(repoDir, `${commitSha}.jsonl`);
2326
+ const repoDir = join19(getConfigDir(), "typed-resolution", repoId);
2327
+ const fullPath = join19(repoDir, `${commitSha}.jsonl`);
2313
2328
  return { fullPath, repoDir };
2314
2329
  }
2315
2330
  async function persistMergedSidecar(repoId, commitSha, sidecar) {
2316
2331
  const { fullPath, repoDir } = sidecarCachePaths(repoId, commitSha);
2317
- await mkdir8(repoDir, { recursive: true });
2332
+ await mkdir9(repoDir, { recursive: true });
2318
2333
  const tmpPath = `${fullPath}.tmp`;
2319
- await writeFile8(tmpPath, JSON.stringify(sidecar), "utf-8");
2334
+ await writeFile9(tmpPath, JSON.stringify(sidecar), "utf-8");
2320
2335
  const { rename: rename5 } = await import("fs/promises");
2321
2336
  await rename5(tmpPath, fullPath);
2322
2337
  try {
@@ -2332,7 +2347,7 @@ async function sweepSidecarDir(repoDir) {
2332
2347
  const withMtime = [];
2333
2348
  for (const name of jsonlFiles) {
2334
2349
  try {
2335
- const s = await stat2(join18(repoDir, name));
2350
+ const s = await stat2(join19(repoDir, name));
2336
2351
  withMtime.push({ name, mtimeMs: s.mtimeMs });
2337
2352
  } catch {
2338
2353
  }
@@ -2343,7 +2358,7 @@ async function sweepSidecarDir(repoDir) {
2343
2358
  for (const { name, mtimeMs } of candidates) {
2344
2359
  if (now - mtimeMs >= SWEEP_MAX_AGE_MS) {
2345
2360
  try {
2346
- await unlink7(join18(repoDir, name));
2361
+ await unlink8(join19(repoDir, name));
2347
2362
  } catch {
2348
2363
  }
2349
2364
  }
@@ -2369,7 +2384,7 @@ async function loadMergedSidecar(repoId, commitSha) {
2369
2384
  let body;
2370
2385
  try {
2371
2386
  const { fullPath } = sidecarCachePaths(repoId, commitSha);
2372
- body = await readFile7(fullPath, "utf-8");
2387
+ body = await readFile8(fullPath, "utf-8");
2373
2388
  } catch {
2374
2389
  return null;
2375
2390
  }
@@ -2954,7 +2969,7 @@ async function postLspInstallOutcomes(input) {
2954
2969
  const ctrl = new AbortController();
2955
2970
  const timer = setTimeout(() => {
2956
2971
  ctrl.abort();
2957
- }, REQUEST_TIMEOUT_MS);
2972
+ }, REQUEST_TIMEOUT_MS3);
2958
2973
  try {
2959
2974
  const res = await fetchImpl(url, {
2960
2975
  method: "POST",
@@ -2989,30 +3004,30 @@ async function postLspInstallOutcomes(input) {
2989
3004
  console.warn(`[lsp-telemetry] unexpected failure: ${err instanceof Error ? err.message : String(err)}`);
2990
3005
  }
2991
3006
  }
2992
- var REQUEST_TIMEOUT_MS;
3007
+ var REQUEST_TIMEOUT_MS3;
2993
3008
  var init_telemetry = __esm({
2994
3009
  "../listener/dist/lsp/telemetry.js"() {
2995
3010
  "use strict";
2996
- REQUEST_TIMEOUT_MS = 5e3;
3011
+ REQUEST_TIMEOUT_MS3 = 5e3;
2997
3012
  }
2998
3013
  });
2999
3014
 
3000
3015
  // bin/repowise.ts
3001
3016
  import { readFileSync as readFileSync3 } from "fs";
3002
3017
  import { fileURLToPath as fileURLToPath4 } from "url";
3003
- import { dirname as dirname20, join as join52 } from "path";
3018
+ import { dirname as dirname20, join as join59 } from "path";
3004
3019
  import { Command } from "commander";
3005
3020
 
3006
3021
  // ../listener/dist/main.js
3007
3022
  init_config_dir();
3008
- import { readFile as readFile12, writeFile as writeFile14, mkdir as mkdir14, stat as fsStat } from "fs/promises";
3009
- import { join as join32, dirname as dirname15 } from "path";
3023
+ import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir16, stat as fsStat } from "fs/promises";
3024
+ import { join as join40, dirname as dirname15 } from "path";
3010
3025
  import { fileURLToPath as fileURLToPath3 } from "url";
3011
3026
  import lockfile4 from "proper-lockfile";
3012
3027
 
3013
3028
  // ../../packages/shared/dist/lib/ai-tools.js
3014
- import { readFile, writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
3015
- import { join as join2, dirname } from "path";
3029
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, readdir, stat, unlink as unlink2 } from "fs/promises";
3030
+ import { join as join3, dirname } from "path";
3016
3031
 
3017
3032
  // ../../packages/shared/dist/lib/project-overview-injection.js
3018
3033
  var START_MARKER = "<!-- repowise:agent-instructions:start -->";
@@ -3029,7 +3044,7 @@ Treat RepoWise as your default starting point for every task that touches this c
3029
3044
  1. **Always start with RepoWise.** Before any task \u2014 questions, planning, coding, debugging, refactoring, tests, code review \u2014 gather context from RepoWise FIRST. Skipping it leads to wrong patterns, duplicated abstractions, and missed constraints.
3030
3045
 
3031
3046
  2. **Use the three-tier hierarchy in this order:**
3032
- - **Tier 1 \u2014 RepoWise MCP** for surgical structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`mcp__repowise__find_symbol\`, \`mcp__repowise__get_impact\`, \`mcp__repowise__lsp_definition\`, etc.). Each tool's description tells you when to use it.
3047
+ - **Tier 1 \u2014 RepoWise MCP** for surgical structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`find_symbol\`, \`get_impact\`, \`lsp_definition\`, etc. \u2014 the server is named "RepoWise MCP for <repo>", so hosts that prefix tool names expose them accordingly). Each tool's description tells you when to use it.
3033
3048
  - **Tier 2 \u2014 Context files in \`repowise-context/\`** for narrative context: how a domain is designed, what patterns exist, what conventions to follow. Use the *Context File Routing Map* below to pick which files to read.
3034
3049
  - **Tier 3 \u2014 Native code search** (\`read_file\` / \`grep\` / \`glob\` on raw source) ONLY when tiers 1-2 don't cover the question, or for ephemeral state (recent commits, in-flight branches).
3035
3050
 
@@ -3055,7 +3070,7 @@ Treat RepoWise as your default starting point for every task that touches this c
3055
3070
 
3056
3071
  _Powered by RepoWise._
3057
3072
 
3058
- That's it \u2014 one short line, no longer preamble. The host's tool-call UI already shows \`mcp__repowise__*\` calls visibly; this tag adds the same surface for context-file reads. If you didn't use RepoWise on this turn, omit the tag \u2014 never fake it.
3073
+ That's it \u2014 one short line, no longer preamble. The host's tool-call UI already shows RepoWise MCP calls visibly; this tag adds the same surface for context-file reads. If you didn't use RepoWise on this turn, omit the tag \u2014 never fake it.
3059
3074
 
3060
3075
  8. **Surface MCP gaps explicitly.** If RepoWise MCP is unavailable (no \`.mcp.json\` configured, listener not running), tell the user \u2014 don't silently fall back to \`grep\`.
3061
3076
  ${END_MARKER}`;
@@ -3084,7 +3099,428 @@ function injectAgentInstructions(content) {
3084
3099
  return canonicalBlock + lineEnding + lineEnding + content;
3085
3100
  }
3086
3101
 
3102
+ // ../../packages/shared/dist/lib/enforcement-hooks.js
3103
+ import { execFile } from "child_process";
3104
+ import { readFile, writeFile, mkdir, unlink } from "fs/promises";
3105
+ import { basename, join as join2 } from "path";
3106
+ var REPOWISE_MANAGED_COMMENT = "# repowise-managed";
3107
+ function isRepoWiseHookCommand(command) {
3108
+ if (command.includes(REPOWISE_MANAGED_COMMENT))
3109
+ return true;
3110
+ return command.startsWith("echo '") && command.includes("additionalContext") && command.includes("/project-overview.md");
3111
+ }
3112
+ function sanitizeForShellText(value) {
3113
+ return value.replace(/['\r\n]/g, "");
3114
+ }
3115
+ function mcpServerName(repoName) {
3116
+ return `RepoWise MCP for ${repoName}`;
3117
+ }
3118
+ function claudeToolPrefix(repoName) {
3119
+ return `mcp__${mcpServerName(repoName).replace(/ /g, "_")}__`;
3120
+ }
3121
+ var GRAPH_TOOLS = "find_symbol, get_symbol, get_impact, find_callers, find_references, batch_query";
3122
+ var LSP_TOOLS = "lsp_definition, lsp_references, lsp_hover";
3123
+ function buildRepoWiseFirstDirective(repoName, contextFolder, style = "generic") {
3124
+ const repo = sanitizeForShellText(repoName);
3125
+ const folder = sanitizeForShellText(contextFolder);
3126
+ const toolNaming = style === "claude" ? `${claudeToolPrefix(repo)}<tool>, e.g. ${claudeToolPrefix(repo)}find_symbol` : `the "${mcpServerName(repo)}" MCP server`;
3127
+ return `RepoWise-first: before any codebase task, gather context from RepoWise first. Tier 1: RepoWise MCP graph tools (${toolNaming}; tools: ${GRAPH_TOOLS}) and LSP tools (${LSP_TOOLS}) for symbol resolution and blast radius. Tier 2: ${folder}/ files, starting with ${folder}/project-overview.md. Tier 3: native file search only when tiers 1-2 do not cover the question. If these tools are deferred in your environment, load them via tool search before calling.`;
3128
+ }
3129
+ function buildSubagentDirective(contextFolder) {
3130
+ const folder = sanitizeForShellText(contextFolder);
3131
+ return `IMPORTANT: Read ${folder}/project-overview.md before performing any work. This file maps every context file to its domain.`;
3132
+ }
3133
+ var GATE_SCRIPT_REL = ".claude/repowise-gate.cjs";
3134
+ var GATE_PRETOOLUSE_MATCHER = "Read|Grep|Glob|mcp__RepoWise.*";
3135
+ var GATE_MAX_DENIES = 2;
3136
+ function buildGateHookCommand() {
3137
+ return `node "$CLAUDE_PROJECT_DIR/${GATE_SCRIPT_REL}" ${REPOWISE_MANAGED_COMMENT}`;
3138
+ }
3139
+ function buildGateScript(contextFolder) {
3140
+ const folder = contextFolder.replace(/[^A-Za-z0-9_./-]/g, "");
3141
+ return GATE_SCRIPT_TEMPLATE.replace(/__CONTEXT_FOLDER__/g, folder).replace(/__MAX_DENIES__/g, String(GATE_MAX_DENIES));
3142
+ }
3143
+ var GATE_SCRIPT_TEMPLATE = [
3144
+ "#!/usr/bin/env node",
3145
+ "// repowise-managed \u2014 RepoWise-first enforcement gate. Regenerated on sync; do not edit.",
3146
+ "var fs = require('fs'); var os = require('os'); var path = require('path');",
3147
+ "var CONTEXT_FOLDER = '__CONTEXT_FOLDER__'; var MAX_DENIES = __MAX_DENIES__;",
3148
+ 'function readStdin(){ try { return fs.readFileSync(0, "utf8"); } catch (e) { return ""; } }',
3149
+ 'var payload = {}; try { payload = JSON.parse(readStdin() || "{}"); } catch (e) {}',
3150
+ 'var event = String(payload.hook_event_name || "");',
3151
+ 'var sessionId = String(payload.session_id || "nosession").replace(/[^A-Za-z0-9_.-]/g, "_");',
3152
+ 'var stateFile = path.join(os.tmpdir(), "repowise-gate", sessionId + ".json");',
3153
+ 'function readState(){ try { return JSON.parse(fs.readFileSync(stateFile, "utf8")); } catch (e) { return { consulted:false, denies:0 }; } }',
3154
+ "function writeState(s){ try { fs.mkdirSync(path.dirname(stateFile), {recursive:true}); fs.writeFileSync(stateFile, JSON.stringify(s)); } catch (e) {} }",
3155
+ "// UserPromptSubmit: re-arm RepoWise-first for the new question (the echo hook carries the reminder).",
3156
+ 'if (event === "UserPromptSubmit") { try { fs.rmSync(stateFile, {force:true}); } catch (e) {} process.exit(0); }',
3157
+ 'if (event !== "PreToolUse") { process.exit(0); }',
3158
+ 'var tool = String(payload.tool_name || "");',
3159
+ "var input = payload.tool_input || {};",
3160
+ 'var filePath = String(input.file_path || input.path || input.notebook_path || "");',
3161
+ 'var isContext = filePath.indexOf(CONTEXT_FOLDER + "/") !== -1;',
3162
+ 'var tier = "other";',
3163
+ 'if (tool.indexOf("mcp__RepoWise") === 0) { tier = "tier1-mcp"; }',
3164
+ 'else if (tool === "Read" && isContext) { tier = "tier2-context"; }',
3165
+ 'else if (tool === "Grep" || tool === "Glob" || (tool === "Read" && !isContext)) { tier = "tier3-native"; }',
3166
+ "var state = readState();",
3167
+ 'if (tier === "tier1-mcp" || tier === "tier2-context") { state.consulted = true; writeState(state); process.exit(0); }',
3168
+ 'if (tier !== "tier3-native") { process.exit(0); }',
3169
+ "function logMiss(){ try {",
3170
+ " var root = process.env.CLAUDE_PROJECT_DIR || payload.cwd || process.cwd();",
3171
+ " var blob = JSON.stringify(input);",
3172
+ " var ephemeral = /\\.git|commit|\\blog\\b|branch|HEAD|stash|reflog/i.test(blob);",
3173
+ ' var rec = { ts: new Date().toISOString(), event: "repowise-miss", sessionId: sessionId, tool: tool, filePath: filePath, query: String(input.pattern || input.query || ""), ephemeral: ephemeral };',
3174
+ ' var dir = path.join(root, ".repowise"); fs.mkdirSync(dir, {recursive:true});',
3175
+ ' fs.appendFileSync(path.join(dir, "repowise-misses.jsonl"), JSON.stringify(rec) + "\\n");',
3176
+ "} catch (e) {} }",
3177
+ "if (state.consulted) { logMiss(); process.exit(0); }",
3178
+ "if ((state.denies || 0) >= MAX_DENIES) { process.exit(0); }",
3179
+ "state.denies = (state.denies || 0) + 1; writeState(state);",
3180
+ 'var reason = "RepoWise-first: this codebase has RepoWise. Use Tier 1 (RepoWise MCP tools find_symbol/get_impact/lsp_*) or Tier 2 (" + CONTEXT_FOLDER + "/ docs) to answer this before native search. Retry this search and it will be allowed.";',
3181
+ 'process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason } }));',
3182
+ "process.exit(0);",
3183
+ ""
3184
+ ].join("\n");
3185
+ function assertShellSafe(payload) {
3186
+ if (payload.includes("'")) {
3187
+ throw new Error("hook payload must not contain single quotes");
3188
+ }
3189
+ if (/[\r\n]/.test(payload)) {
3190
+ throw new Error("hook payload must be a single line");
3191
+ }
3192
+ }
3193
+ function buildHookEchoCommand(eventName, additionalContext) {
3194
+ const json = JSON.stringify({
3195
+ hookSpecificOutput: { hookEventName: eventName, additionalContext }
3196
+ });
3197
+ assertShellSafe(json);
3198
+ return `echo '${json}' ${REPOWISE_MANAGED_COMMENT}`;
3199
+ }
3200
+ function buildPlainJsonEchoCommand(payload) {
3201
+ const json = JSON.stringify(payload);
3202
+ assertShellSafe(json);
3203
+ return `echo '${json}' ${REPOWISE_MANAGED_COMMENT}`;
3204
+ }
3205
+ function upsertRepoWiseEntry(entries, next) {
3206
+ const list = Array.isArray(entries) ? entries : [];
3207
+ const idx = list.findIndex((entry) => entry?.hooks?.some?.((h) => typeof h?.command === "string" && isRepoWiseHookCommand(h.command)));
3208
+ if (idx >= 0) {
3209
+ list[idx] = next;
3210
+ } else {
3211
+ list.push(next);
3212
+ }
3213
+ return list;
3214
+ }
3215
+ function removeRepoWiseEntries(entries) {
3216
+ const list = Array.isArray(entries) ? entries : [];
3217
+ return list.filter((entry) => !entry?.hooks?.some?.((h) => typeof h?.command === "string" && isRepoWiseHookCommand(h.command)));
3218
+ }
3219
+ function mergeHookEvents(existingContent, events) {
3220
+ let settings = {};
3221
+ if (existingContent) {
3222
+ let parsed;
3223
+ try {
3224
+ parsed = JSON.parse(existingContent);
3225
+ } catch {
3226
+ return existingContent;
3227
+ }
3228
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3229
+ return existingContent;
3230
+ }
3231
+ settings = parsed;
3232
+ }
3233
+ if (settings["hooks"] !== void 0 && !isPlainObject(settings["hooks"])) {
3234
+ return existingContent ?? JSON.stringify(settings, null, 2) + "\n";
3235
+ }
3236
+ const hooks = isPlainObject(settings["hooks"]) ? settings["hooks"] : {};
3237
+ for (const [event, entry] of Object.entries(events)) {
3238
+ if (hooks[event] !== void 0 && !Array.isArray(hooks[event])) {
3239
+ continue;
3240
+ }
3241
+ hooks[event] = upsertRepoWiseEntry(hooks[event], entry);
3242
+ }
3243
+ settings["hooks"] = hooks;
3244
+ const next = JSON.stringify(settings, null, 2) + "\n";
3245
+ return existingContent === next ? existingContent : next;
3246
+ }
3247
+ function isPlainObject(value) {
3248
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3249
+ }
3250
+ function removeHookEvents(existingContent, eventNames) {
3251
+ let settings;
3252
+ try {
3253
+ const parsed = JSON.parse(existingContent);
3254
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
3255
+ return existingContent;
3256
+ settings = parsed;
3257
+ } catch {
3258
+ return existingContent;
3259
+ }
3260
+ const hooks = settings["hooks"] && typeof settings["hooks"] === "object" && !Array.isArray(settings["hooks"]) ? settings["hooks"] : null;
3261
+ if (!hooks)
3262
+ return existingContent;
3263
+ for (const event of eventNames) {
3264
+ if (hooks[event] !== void 0 && !Array.isArray(hooks[event])) {
3265
+ continue;
3266
+ }
3267
+ const remaining = removeRepoWiseEntries(hooks[event]);
3268
+ if (remaining.length > 0) {
3269
+ hooks[event] = remaining;
3270
+ } else {
3271
+ delete hooks[event];
3272
+ }
3273
+ }
3274
+ if (Object.keys(hooks).length === 0) {
3275
+ delete settings["hooks"];
3276
+ }
3277
+ if (Object.keys(settings).length === 0)
3278
+ return null;
3279
+ const next = JSON.stringify(settings, null, 2) + "\n";
3280
+ return existingContent === next ? existingContent : next;
3281
+ }
3282
+ function mergeClaudeHookSettings(existingContent, params) {
3283
+ return mergeHookEvents(existingContent, {
3284
+ SubagentStart: {
3285
+ matcher: "",
3286
+ hooks: [
3287
+ {
3288
+ type: "command",
3289
+ command: buildHookEchoCommand("SubagentStart", buildSubagentDirective(params.contextFolder))
3290
+ }
3291
+ ]
3292
+ },
3293
+ UserPromptSubmit: {
3294
+ hooks: [
3295
+ {
3296
+ type: "command",
3297
+ command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "claude"))
3298
+ },
3299
+ // Re-arm the per-question gate (silent; the echo above carries the reminder).
3300
+ { type: "command", command: buildGateHookCommand() }
3301
+ ]
3302
+ },
3303
+ // The enforcement gate: steer native search → RepoWise first, log misses.
3304
+ PreToolUse: {
3305
+ matcher: GATE_PRETOOLUSE_MATCHER,
3306
+ hooks: [{ type: "command", command: buildGateHookCommand() }]
3307
+ }
3308
+ });
3309
+ }
3310
+ function removeClaudeHookSettings(existingContent) {
3311
+ return removeHookEvents(existingContent, ["SubagentStart", "UserPromptSubmit", "PreToolUse"]);
3312
+ }
3313
+ function mergeGeminiHookSettings(existingContent, params) {
3314
+ return mergeHookEvents(existingContent, {
3315
+ BeforeAgent: {
3316
+ hooks: [
3317
+ {
3318
+ type: "command",
3319
+ name: "repowise-first",
3320
+ command: buildPlainJsonEchoCommand({
3321
+ hookSpecificOutput: {
3322
+ additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3323
+ }
3324
+ })
3325
+ }
3326
+ ]
3327
+ }
3328
+ });
3329
+ }
3330
+ function removeGeminiHookSettings(existingContent) {
3331
+ return removeHookEvents(existingContent, ["BeforeAgent"]);
3332
+ }
3333
+ function mergeCodexHookSettings(existingContent, params) {
3334
+ return mergeHookEvents(existingContent, {
3335
+ UserPromptSubmit: {
3336
+ hooks: [
3337
+ {
3338
+ type: "command",
3339
+ command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder))
3340
+ }
3341
+ ]
3342
+ }
3343
+ });
3344
+ }
3345
+ function removeCodexHookSettings(existingContent) {
3346
+ return removeHookEvents(existingContent, ["UserPromptSubmit"]);
3347
+ }
3348
+ function buildCopilotHooksFile(params) {
3349
+ const command = buildPlainJsonEchoCommand({
3350
+ additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3351
+ });
3352
+ return JSON.stringify({
3353
+ version: 1,
3354
+ hooks: {
3355
+ sessionStart: [{ type: "command", bash: command, powershell: command, timeoutSec: 10 }]
3356
+ }
3357
+ }, null, 2) + "\n";
3358
+ }
3359
+ function buildClineHookScript(params) {
3360
+ const command = buildPlainJsonEchoCommand({
3361
+ contextModification: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3362
+ });
3363
+ return [
3364
+ "#!/bin/sh",
3365
+ "# repowise-context hook managed by RepoWise. Do not edit; regenerated on sync.",
3366
+ command,
3367
+ ""
3368
+ ].join("\n");
3369
+ }
3370
+ var GITIGNORE_START = "# repowise:start (managed by RepoWise, do not edit between markers)";
3371
+ var GITIGNORE_END = "# repowise:end";
3372
+ function updateGitignoreBlock(existingContent, changes) {
3373
+ let content = existingContent ?? "";
3374
+ const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
3375
+ let startIdx = content.indexOf(GITIGNORE_START);
3376
+ let endIdx = startIdx === -1 ? -1 : content.indexOf(GITIGNORE_END, startIdx);
3377
+ let hasBlock = startIdx !== -1 && endIdx > startIdx;
3378
+ if (!hasBlock && (content.includes(GITIGNORE_START) || content.includes(GITIGNORE_END))) {
3379
+ content = content.split(/\r?\n/).filter((line) => {
3380
+ const t = line.trim();
3381
+ return t !== GITIGNORE_START && t !== GITIGNORE_END;
3382
+ }).join(lineEnding);
3383
+ startIdx = -1;
3384
+ endIdx = -1;
3385
+ hasBlock = false;
3386
+ }
3387
+ const current = /* @__PURE__ */ new Set();
3388
+ if (hasBlock) {
3389
+ const inner = content.slice(startIdx + GITIGNORE_START.length, endIdx);
3390
+ for (const line of inner.split(/\r?\n/)) {
3391
+ const trimmed = line.trim();
3392
+ if (trimmed.length > 0 && !trimmed.startsWith("#"))
3393
+ current.add(trimmed);
3394
+ }
3395
+ }
3396
+ for (const p of changes.add ?? [])
3397
+ current.add(p);
3398
+ for (const p of changes.remove ?? [])
3399
+ current.delete(p);
3400
+ const entries = [...current].sort();
3401
+ const block = entries.length === 0 ? "" : [GITIGNORE_START, ...entries, GITIGNORE_END].join(lineEnding);
3402
+ let next;
3403
+ if (hasBlock) {
3404
+ const before = content.slice(0, startIdx);
3405
+ const after = content.slice(endIdx + GITIGNORE_END.length);
3406
+ if (block === "") {
3407
+ next = before.replace(/(\r?\n){2,}$/, lineEnding) + after.replace(/^\r?\n/, "");
3408
+ if (next.trim() === "")
3409
+ return "";
3410
+ } else {
3411
+ next = before + block + after;
3412
+ }
3413
+ } else if (block === "") {
3414
+ next = content;
3415
+ } else {
3416
+ const separator = content.length === 0 ? "" : content.endsWith(lineEnding) ? lineEnding : lineEnding + lineEnding;
3417
+ next = content + separator + block + lineEnding;
3418
+ }
3419
+ return next === (existingContent ?? "") ? existingContent ?? "" : next;
3420
+ }
3421
+ var CLAUDE_PROJECT_SETTINGS = ".claude/settings.json";
3422
+ var CLAUDE_LOCAL_SETTINGS = ".claude/settings.local.json";
3423
+ async function isGitTracked(repoRoot, relPath) {
3424
+ return new Promise((resolve5) => {
3425
+ execFile("git", ["ls-files", "--error-unmatch", relPath], { cwd: repoRoot }, (err) => {
3426
+ if (!err)
3427
+ return resolve5(true);
3428
+ if (err.code === 1 || err.code === 128)
3429
+ return resolve5(false);
3430
+ resolve5(true);
3431
+ });
3432
+ });
3433
+ }
3434
+ async function applyGitignoreChanges(repoRoot, changes) {
3435
+ const path = join2(repoRoot, ".gitignore");
3436
+ let existing = null;
3437
+ try {
3438
+ existing = await readFile(path, "utf-8");
3439
+ } catch {
3440
+ }
3441
+ const next = updateGitignoreBlock(existing, changes);
3442
+ if (next !== (existing ?? "")) {
3443
+ await writeFile(path, next, "utf-8");
3444
+ }
3445
+ }
3446
+ async function writeClaudeHooksToRepo(repoRoot, contextFolder) {
3447
+ const repoName = basename(repoRoot);
3448
+ const projectTracked = await isGitTracked(repoRoot, CLAUDE_PROJECT_SETTINGS);
3449
+ if (projectTracked) {
3450
+ let trackedContent = "";
3451
+ try {
3452
+ trackedContent = await readFile(join2(repoRoot, CLAUDE_PROJECT_SETTINGS), "utf-8");
3453
+ } catch {
3454
+ }
3455
+ const trackedHasOurHooks = trackedContent.includes(REPOWISE_MANAGED_COMMENT) || trackedContent.includes("additionalContext") && trackedContent.includes("/project-overview.md");
3456
+ if (trackedHasOurHooks) {
3457
+ await applyGitignoreChanges(repoRoot, {
3458
+ remove: [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS]
3459
+ });
3460
+ return { status: "unchanged", relPath: CLAUDE_PROJECT_SETTINGS };
3461
+ }
3462
+ }
3463
+ const targetRel = projectTracked ? CLAUDE_LOCAL_SETTINGS : CLAUDE_PROJECT_SETTINGS;
3464
+ const targetPath = join2(repoRoot, targetRel);
3465
+ let existing = null;
3466
+ try {
3467
+ existing = await readFile(targetPath, "utf-8");
3468
+ } catch {
3469
+ }
3470
+ const merged = mergeClaudeHookSettings(existing, { repoName, contextFolder });
3471
+ let status2 = "unchanged";
3472
+ if (merged !== existing) {
3473
+ await mkdir(join2(repoRoot, ".claude"), { recursive: true });
3474
+ await writeFile(targetPath, merged, "utf-8");
3475
+ status2 = "written";
3476
+ }
3477
+ const gateScript = buildGateScript(contextFolder);
3478
+ const gatePath = join2(repoRoot, GATE_SCRIPT_REL);
3479
+ let gateExisting = null;
3480
+ try {
3481
+ gateExisting = await readFile(gatePath, "utf-8");
3482
+ } catch {
3483
+ }
3484
+ if (gateExisting !== gateScript) {
3485
+ await mkdir(join2(repoRoot, ".claude"), { recursive: true });
3486
+ await writeFile(gatePath, gateScript, "utf-8");
3487
+ status2 = "written";
3488
+ }
3489
+ if (status2 === "written") {
3490
+ await applyGitignoreChanges(repoRoot, {
3491
+ add: [targetRel, GATE_SCRIPT_REL],
3492
+ remove: [projectTracked ? CLAUDE_PROJECT_SETTINGS : CLAUDE_LOCAL_SETTINGS]
3493
+ });
3494
+ }
3495
+ return { status: status2, relPath: targetRel };
3496
+ }
3497
+ async function removeClaudeHooksFromRepo(repoRoot) {
3498
+ for (const rel of [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS]) {
3499
+ const path = join2(repoRoot, rel);
3500
+ let existing;
3501
+ try {
3502
+ existing = await readFile(path, "utf-8");
3503
+ } catch {
3504
+ continue;
3505
+ }
3506
+ const next = removeClaudeHookSettings(existing);
3507
+ if (next === null) {
3508
+ await unlink(path);
3509
+ } else if (next !== existing) {
3510
+ await writeFile(path, next, "utf-8");
3511
+ }
3512
+ }
3513
+ try {
3514
+ await unlink(join2(repoRoot, GATE_SCRIPT_REL));
3515
+ } catch {
3516
+ }
3517
+ await applyGitignoreChanges(repoRoot, {
3518
+ remove: [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS, GATE_SCRIPT_REL]
3519
+ });
3520
+ }
3521
+
3087
3522
  // ../../packages/shared/dist/lib/ai-tools.js
3523
+ var AI_TOOLS_REFERENCE_REV = 2;
3088
3524
  var AI_TOOL_CONFIG = {
3089
3525
  cursor: {
3090
3526
  label: "Cursor",
@@ -3155,6 +3591,15 @@ var AI_TOOL_CONFIG = {
3155
3591
  format: "markdown",
3156
3592
  owned: true
3157
3593
  },
3594
+ kilo: {
3595
+ label: "Kilo Code",
3596
+ fileName: "repowise.md",
3597
+ filePath: ".kilocode/rules/repowise.md",
3598
+ markerStart: "<!-- repowise-start -->",
3599
+ markerEnd: "<!-- repowise-end -->",
3600
+ format: "markdown",
3601
+ owned: true
3602
+ },
3158
3603
  gemini: {
3159
3604
  label: "Gemini CLI",
3160
3605
  fileName: "GEMINI.md",
@@ -3227,6 +3672,8 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3227
3672
  return { path: f.relativePath, desc: isOverview ? `${desc} (full index of all files)` : desc };
3228
3673
  });
3229
3674
  const hasFiles = fileLines.length > 0;
3675
+ const mcpName = repoName.replace(/`/g, "");
3676
+ const mcpNamingNote = tool === "claude-code" ? `**RepoWise MCP tool naming in this repo:** tools are exposed as \`${claudeToolPrefix(mcpName)}<tool>\`, e.g. \`${claudeToolPrefix(mcpName)}find_symbol\`, \`${claudeToolPrefix(mcpName)}get_impact\`, \`${claudeToolPrefix(mcpName)}lsp_definition\`.` : `**RepoWise MCP server for this repo:** "${mcpServerName(mcpName)}" \u2014 graph tools: find_symbol, get_symbol, get_impact, find_callers, find_references, batch_query; LSP tools: lsp_definition, lsp_references, lsp_hover.`;
3230
3677
  const contentLines = [
3231
3678
  `## Project Context \u2014 ${safeName}`,
3232
3679
  "",
@@ -3235,6 +3682,8 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3235
3682
  AI_AGENT_USAGE_INSTRUCTIONS,
3236
3683
  "",
3237
3684
  `**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that maps every context file to its domain.`,
3685
+ "",
3686
+ mcpNamingNote,
3238
3687
  ""
3239
3688
  ];
3240
3689
  if (hasFiles) {
@@ -3252,10 +3701,10 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3252
3701
  }
3253
3702
  async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
3254
3703
  const config2 = AI_TOOL_CONFIG[tool];
3255
- const fullPath = join2(repoRoot, config2.filePath);
3704
+ const fullPath = join3(repoRoot, config2.filePath);
3256
3705
  const dir = dirname(fullPath);
3257
3706
  if (dir !== repoRoot) {
3258
- await mkdir(dir, { recursive: true });
3707
+ await mkdir2(dir, { recursive: true });
3259
3708
  }
3260
3709
  const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
3261
3710
  if (config2.owned) {
@@ -3265,13 +3714,13 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
3265
3714
  created2 = false;
3266
3715
  } catch {
3267
3716
  }
3268
- await writeFile(fullPath, referenceBlock, "utf-8");
3717
+ await writeFile2(fullPath, referenceBlock, "utf-8");
3269
3718
  return { created: created2 };
3270
3719
  }
3271
3720
  let existing = "";
3272
3721
  let created = true;
3273
3722
  try {
3274
- existing = await readFile(fullPath, "utf-8");
3723
+ existing = await readFile2(fullPath, "utf-8");
3275
3724
  created = false;
3276
3725
  } catch (err) {
3277
3726
  if (err.code !== "ENOENT")
@@ -3288,14 +3737,43 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
3288
3737
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3289
3738
  content = existing + separator + referenceBlock + "\n";
3290
3739
  }
3291
- await writeFile(fullPath, content, "utf-8");
3740
+ await writeFile2(fullPath, content, "utf-8");
3292
3741
  return { created };
3293
3742
  }
3743
+ async function removeToolConfig(repoRoot, tool) {
3744
+ const config2 = AI_TOOL_CONFIG[tool];
3745
+ const fullPath = join3(repoRoot, config2.filePath);
3746
+ if (config2.owned) {
3747
+ try {
3748
+ await unlink2(fullPath);
3749
+ } catch (err) {
3750
+ if (err.code !== "ENOENT")
3751
+ throw err;
3752
+ }
3753
+ return;
3754
+ }
3755
+ let existing;
3756
+ try {
3757
+ existing = await readFile2(fullPath, "utf-8");
3758
+ } catch (err) {
3759
+ if (err.code === "ENOENT")
3760
+ return;
3761
+ throw err;
3762
+ }
3763
+ const startIdx = existing.indexOf(config2.markerStart);
3764
+ const endIdx = existing.indexOf(config2.markerEnd);
3765
+ if (startIdx === -1 || endIdx === -1)
3766
+ return;
3767
+ const before = existing.slice(0, startIdx);
3768
+ const after = existing.slice(endIdx + config2.markerEnd.length);
3769
+ const content = (before + after).replace(/\n{3,}/g, "\n\n").trim() + "\n";
3770
+ await writeFile2(fullPath, content, "utf-8");
3771
+ }
3294
3772
  async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
3295
3773
  const config2 = AI_TOOL_CONFIG[tool];
3296
3774
  if (!config2.legacyFilePath)
3297
3775
  return { migrated: false, legacyRemoved: false };
3298
- const legacyPath = join2(repoRoot, config2.legacyFilePath);
3776
+ const legacyPath = join3(repoRoot, config2.legacyFilePath);
3299
3777
  let legacyIsFile = false;
3300
3778
  try {
3301
3779
  const s = await stat(legacyPath);
@@ -3308,7 +3786,7 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
3308
3786
  await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
3309
3787
  return { migrated: false, legacyRemoved: false };
3310
3788
  }
3311
- const legacyContent = await readFile(legacyPath, "utf-8");
3789
+ const legacyContent = await readFile2(legacyPath, "utf-8");
3312
3790
  let cleaned = legacyContent;
3313
3791
  const oldMarkers = [
3314
3792
  { start: "# --- repowise-start ---", end: "# --- repowise-end ---" },
@@ -3324,16 +3802,16 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
3324
3802
  cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
3325
3803
  if (tool === "cline") {
3326
3804
  if (cleaned.length > 0) {
3327
- await unlink(legacyPath);
3328
- await mkdir(join2(repoRoot, ".clinerules"), { recursive: true });
3329
- await writeFile(join2(repoRoot, ".clinerules/user-rules.md"), cleaned + "\n", "utf-8");
3805
+ await unlink2(legacyPath);
3806
+ await mkdir2(join3(repoRoot, ".clinerules"), { recursive: true });
3807
+ await writeFile2(join3(repoRoot, ".clinerules/user-rules.md"), cleaned + "\n", "utf-8");
3330
3808
  } else {
3331
- await unlink(legacyPath);
3809
+ await unlink2(legacyPath);
3332
3810
  }
3333
3811
  } else if (cleaned.length > 0) {
3334
- await writeFile(legacyPath, cleaned + "\n", "utf-8");
3812
+ await writeFile2(legacyPath, cleaned + "\n", "utf-8");
3335
3813
  } else {
3336
- await unlink(legacyPath);
3814
+ await unlink2(legacyPath);
3337
3815
  }
3338
3816
  await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
3339
3817
  return { migrated: true, legacyRemoved: cleaned.length === 0 };
@@ -3351,16 +3829,44 @@ async function detectInstalledTools(repoRoot) {
3351
3829
  for (const [tool, config2] of Object.entries(AI_TOOL_CONFIG)) {
3352
3830
  if (tool !== "codex" && config2.filePath === "AGENTS.md")
3353
3831
  continue;
3354
- if (await fileExists(join2(repoRoot, config2.filePath))) {
3832
+ if (await fileExists(join3(repoRoot, config2.filePath))) {
3355
3833
  detected.push(tool);
3356
- } else if (config2.legacyFilePath && await fileExists(join2(repoRoot, config2.legacyFilePath))) {
3834
+ } else if (config2.legacyFilePath && await fileExists(join3(repoRoot, config2.legacyFilePath))) {
3357
3835
  detected.push(tool);
3358
3836
  }
3359
3837
  }
3360
- return detected;
3838
+ return detected.map((t) => t === "roo-code" ? "kilo" : t).filter(dedupe);
3839
+ }
3840
+ function dedupe(value, index, arr) {
3841
+ return arr.indexOf(value) === index;
3842
+ }
3843
+ async function migrateRooToKilo(repoRoot) {
3844
+ let migrated = false;
3845
+ if (await fileExists(join3(repoRoot, ".roo/rules/repowise.md"))) {
3846
+ await removeToolConfig(repoRoot, "roo-code");
3847
+ migrated = true;
3848
+ }
3849
+ const legacyPath = join3(repoRoot, ".roo/rules.md");
3850
+ try {
3851
+ const legacy = await readFile2(legacyPath, "utf-8");
3852
+ const config2 = AI_TOOL_CONFIG["roo-code"];
3853
+ const si = legacy.indexOf(config2.markerStart);
3854
+ const ei = legacy.indexOf(config2.markerEnd);
3855
+ if (si !== -1 && ei > si) {
3856
+ const cleaned = (legacy.slice(0, si) + legacy.slice(ei + config2.markerEnd.length)).replace(/\n{3,}/g, "\n\n").trim();
3857
+ if (cleaned.length > 0) {
3858
+ await writeFile2(legacyPath, cleaned + "\n", "utf-8");
3859
+ } else {
3860
+ await unlink2(legacyPath);
3861
+ }
3862
+ migrated = true;
3863
+ }
3864
+ } catch {
3865
+ }
3866
+ return migrated;
3361
3867
  }
3362
3868
  async function scanLocalContextFiles(repoRoot, contextFolder) {
3363
- const folderPath = join2(repoRoot, contextFolder);
3869
+ const folderPath = join3(repoRoot, contextFolder);
3364
3870
  try {
3365
3871
  const entries = await readdir(folderPath, { withFileTypes: true, recursive: true });
3366
3872
  const results = [];
@@ -3368,7 +3874,7 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
3368
3874
  if (!entry.isFile() || !entry.name.endsWith(".md"))
3369
3875
  continue;
3370
3876
  const parentDir = entry.parentPath ?? folderPath;
3371
- const fullPath = join2(parentDir, entry.name);
3877
+ const fullPath = join3(parentDir, entry.name);
3372
3878
  const relFromContext = fullPath.slice(folderPath.length + 1);
3373
3879
  results.push({
3374
3880
  fileName: relFromContext,
@@ -3385,8 +3891,8 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
3385
3891
 
3386
3892
  // ../listener/dist/lib/config.js
3387
3893
  init_config_dir();
3388
- import { readFile as readFile2, writeFile as writeFile2, rename, unlink as unlink2, mkdir as mkdir2, chmod, open } from "fs/promises";
3389
- import { join as join3 } from "path";
3894
+ import { readFile as readFile3, writeFile as writeFile3, rename, unlink as unlink3, mkdir as mkdir3, chmod, open } from "fs/promises";
3895
+ import { join as join4 } from "path";
3390
3896
  import lockfile from "proper-lockfile";
3391
3897
  function sanitizeLspOverrides(raw) {
3392
3898
  if (raw === void 0 || raw === null)
@@ -3404,10 +3910,10 @@ function sanitizeLspOverrides(raw) {
3404
3910
  var DEFAULT_API_URL = true ? "https://staging-api.repowise.ai" : "https://api.repowise.ai";
3405
3911
  async function getListenerConfig() {
3406
3912
  const configDir = getConfigDir();
3407
- const configPath = join3(configDir, "config.json");
3913
+ const configPath = join4(configDir, "config.json");
3408
3914
  const apiUrl = process.env["REPOWISE_API_URL"] ?? DEFAULT_API_URL;
3409
3915
  try {
3410
- const data = await readFile2(configPath, "utf-8");
3916
+ const data = await readFile3(configPath, "utf-8");
3411
3917
  const raw = JSON.parse(data);
3412
3918
  const validRepos = (raw.repos ?? []).filter((r) => typeof r === "object" && r !== null && typeof r.repoId === "string" && typeof r.localPath === "string");
3413
3919
  const lspOverrides = sanitizeLspOverrides(raw.lspOverrides);
@@ -3433,10 +3939,10 @@ async function getListenerConfig() {
3433
3939
  }
3434
3940
  async function saveListenerConfig(config2) {
3435
3941
  const configDir = getConfigDir();
3436
- const configPath = join3(configDir, "config.json");
3437
- await mkdir2(configDir, { recursive: true, mode: 448 });
3942
+ const configPath = join4(configDir, "config.json");
3943
+ await mkdir3(configDir, { recursive: true, mode: 448 });
3438
3944
  try {
3439
- await writeFile2(configPath, "", { flag: "a" });
3945
+ await writeFile3(configPath, "", { flag: "a" });
3440
3946
  } catch {
3441
3947
  }
3442
3948
  let release = null;
@@ -3444,7 +3950,7 @@ async function saveListenerConfig(config2) {
3444
3950
  release = await lockfile.lock(configPath, { stale: 1e4, retries: 3, realpath: false });
3445
3951
  let raw = {};
3446
3952
  try {
3447
- const data = await readFile2(configPath, "utf-8");
3953
+ const data = await readFile3(configPath, "utf-8");
3448
3954
  raw = JSON.parse(data);
3449
3955
  } catch {
3450
3956
  }
@@ -3462,7 +3968,7 @@ async function saveListenerConfig(config2) {
3462
3968
  };
3463
3969
  const tmpPath = configPath + ".tmp";
3464
3970
  try {
3465
- await writeFile2(tmpPath, JSON.stringify(output, null, 2));
3971
+ await writeFile3(tmpPath, JSON.stringify(output, null, 2));
3466
3972
  await chmod(tmpPath, 384);
3467
3973
  let fh;
3468
3974
  try {
@@ -3474,7 +3980,7 @@ async function saveListenerConfig(config2) {
3474
3980
  await rename(tmpPath, configPath);
3475
3981
  } catch (err) {
3476
3982
  try {
3477
- await unlink2(tmpPath);
3983
+ await unlink3(tmpPath);
3478
3984
  } catch {
3479
3985
  }
3480
3986
  throw err;
@@ -3491,16 +3997,16 @@ async function saveListenerConfig(config2) {
3491
3997
 
3492
3998
  // ../listener/dist/lib/state.js
3493
3999
  init_config_dir();
3494
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, chmod as chmod2, rename as rename2, unlink as unlink3, open as open2 } from "fs/promises";
3495
- import { join as join4 } from "path";
4000
+ import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, chmod as chmod2, rename as rename2, unlink as unlink4, open as open2 } from "fs/promises";
4001
+ import { join as join5 } from "path";
3496
4002
  import lockfile2 from "proper-lockfile";
3497
4003
  function emptyState() {
3498
4004
  return { repos: {} };
3499
4005
  }
3500
4006
  async function loadState() {
3501
- const statePath = join4(getConfigDir(), "listener-state.json");
4007
+ const statePath = join5(getConfigDir(), "listener-state.json");
3502
4008
  try {
3503
- const data = await readFile3(statePath, "utf-8");
4009
+ const data = await readFile4(statePath, "utf-8");
3504
4010
  const state = JSON.parse(data);
3505
4011
  if (typeof state !== "object" || state === null || typeof state.repos !== "object" || state.repos === null) {
3506
4012
  return emptyState();
@@ -3515,10 +4021,10 @@ async function loadState() {
3515
4021
  }
3516
4022
  async function saveState(state) {
3517
4023
  const configDir = getConfigDir();
3518
- const statePath = join4(configDir, "listener-state.json");
3519
- await mkdir3(configDir, { recursive: true, mode: 448 });
4024
+ const statePath = join5(configDir, "listener-state.json");
4025
+ await mkdir4(configDir, { recursive: true, mode: 448 });
3520
4026
  try {
3521
- await writeFile3(statePath, "", { flag: "a" });
4027
+ await writeFile4(statePath, "", { flag: "a" });
3522
4028
  } catch {
3523
4029
  }
3524
4030
  let release = null;
@@ -3529,7 +4035,7 @@ async function saveState(state) {
3529
4035
  }
3530
4036
  const tmpPath = statePath + ".tmp";
3531
4037
  try {
3532
- await writeFile3(tmpPath, JSON.stringify(state, null, 2));
4038
+ await writeFile4(tmpPath, JSON.stringify(state, null, 2));
3533
4039
  await chmod2(tmpPath, 384);
3534
4040
  let fh;
3535
4041
  try {
@@ -3541,7 +4047,7 @@ async function saveState(state) {
3541
4047
  await rename2(tmpPath, statePath);
3542
4048
  } catch (err) {
3543
4049
  try {
3544
- await unlink3(tmpPath);
4050
+ await unlink4(tmpPath);
3545
4051
  } catch {
3546
4052
  }
3547
4053
  throw err;
@@ -3555,7 +4061,7 @@ async function saveState(state) {
3555
4061
 
3556
4062
  // ../listener/dist/lib/reconcile.js
3557
4063
  import { statSync, readdirSync } from "fs";
3558
- import { basename, dirname as dirname2, join as join5 } from "path";
4064
+ import { basename as basename2, dirname as dirname2, join as join6 } from "path";
3559
4065
  function reconcileRepos(configRepos, activeRepos, state, apiUrl, options) {
3560
4066
  if (activeRepos.length === 0) {
3561
4067
  return { updated: false, repos: configRepos, changes: [], addedRepos: [] };
@@ -3598,8 +4104,8 @@ function reconcileRepos(configRepos, activeRepos, state, apiUrl, options) {
3598
4104
  };
3599
4105
  }
3600
4106
  }
3601
- const dirName = basename(repo.localPath);
3602
- const parentDir = basename(dirname2(repo.localPath));
4107
+ const dirName = basename2(repo.localPath);
4108
+ const parentDir = basename2(dirname2(repo.localPath));
3603
4109
  const fullPathName = `${parentDir}/${dirName}`;
3604
4110
  let matches = activeRepos.filter((ar) => ar.fullName === fullPathName);
3605
4111
  if (matches.length === 0) {
@@ -3673,7 +4179,7 @@ function findLocalRepo(activeRepo, parentDirs, usedPaths) {
3673
4179
  const nameParts = activeRepo.fullName.split("/");
3674
4180
  const repoName = nameParts[nameParts.length - 1];
3675
4181
  for (const parentDir of parentDirs) {
3676
- const candidate = join5(parentDir, repoName);
4182
+ const candidate = join6(parentDir, repoName);
3677
4183
  if (!usedPaths.has(candidate) && hasContextFolder(candidate)) {
3678
4184
  return candidate;
3679
4185
  }
@@ -3682,7 +4188,7 @@ function findLocalRepo(activeRepo, parentDirs, usedPaths) {
3682
4188
  }
3683
4189
  function hasContextFolder(dirPath) {
3684
4190
  try {
3685
- const contextPath = join5(dirPath, "repowise-context");
4191
+ const contextPath = join6(dirPath, "repowise-context");
3686
4192
  const s = statSync(contextPath);
3687
4193
  if (!s.isDirectory())
3688
4194
  return false;
@@ -3713,8 +4219,8 @@ function migrateState(state, oldId, newId) {
3713
4219
 
3714
4220
  // ../listener/dist/lib/auth.js
3715
4221
  init_config_dir();
3716
- import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, chmod as chmod3 } from "fs/promises";
3717
- import { join as join6 } from "path";
4222
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, chmod as chmod3 } from "fs/promises";
4223
+ import { join as join7 } from "path";
3718
4224
  function getTokenUrl(creds) {
3719
4225
  const cognito = creds?.cognito;
3720
4226
  const domain = process.env["REPOWISE_COGNITO_DOMAIN"] ?? cognito?.domain ?? "auth-repowise-dev";
@@ -3753,8 +4259,8 @@ async function refreshTokens(refreshToken, creds) {
3753
4259
  }
3754
4260
  async function getStoredCredentials() {
3755
4261
  try {
3756
- const credPath = join6(getConfigDir(), "credentials.json");
3757
- const data = await readFile4(credPath, "utf-8");
4262
+ const credPath = join7(getConfigDir(), "credentials.json");
4263
+ const data = await readFile5(credPath, "utf-8");
3758
4264
  return JSON.parse(data);
3759
4265
  } catch (err) {
3760
4266
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -3765,9 +4271,9 @@ async function getStoredCredentials() {
3765
4271
  }
3766
4272
  async function storeCredentials(credentials) {
3767
4273
  const dir = getConfigDir();
3768
- const credPath = join6(dir, "credentials.json");
3769
- await mkdir4(dir, { recursive: true, mode: 448 });
3770
- await writeFile4(credPath, JSON.stringify(credentials, null, 2));
4274
+ const credPath = join7(dir, "credentials.json");
4275
+ await mkdir5(dir, { recursive: true, mode: 448 });
4276
+ await writeFile5(credPath, JSON.stringify(credentials, null, 2));
3771
4277
  await chmod3(credPath, 384);
3772
4278
  }
3773
4279
  async function getValidCredentials(options) {
@@ -3917,17 +4423,17 @@ function notifyContextUpdated(repoId, fileCount) {
3917
4423
  }
3918
4424
 
3919
4425
  // ../listener/dist/context-fetcher.js
3920
- import { execFile } from "child_process";
3921
- import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
3922
- import { dirname as dirname3, join as join8 } from "path";
4426
+ import { execFile as execFile2 } from "child_process";
4427
+ import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
4428
+ import { dirname as dirname3, join as join9 } from "path";
3923
4429
  import { promisify } from "util";
3924
4430
 
3925
4431
  // ../listener/dist/file-writer.js
3926
4432
  import { access } from "fs/promises";
3927
- import { join as join7 } from "path";
4433
+ import { join as join8 } from "path";
3928
4434
  async function verifyContextFolder(localPath) {
3929
4435
  try {
3930
- await access(join7(localPath, "repowise-context"));
4436
+ await access(join8(localPath, "repowise-context"));
3931
4437
  return true;
3932
4438
  } catch {
3933
4439
  return false;
@@ -3935,7 +4441,7 @@ async function verifyContextFolder(localPath) {
3935
4441
  }
3936
4442
 
3937
4443
  // ../listener/dist/context-fetcher.js
3938
- var execFileAsync = promisify(execFile);
4444
+ var execFileAsync = promisify(execFile2);
3939
4445
  async function fetchContextUpdates(localPath) {
3940
4446
  try {
3941
4447
  const { stdout: beforeSha } = await execFileAsync("git", [
@@ -4015,8 +4521,8 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4015
4521
  if (files.length === 0) {
4016
4522
  return { success: true, updatedFiles: [] };
4017
4523
  }
4018
- const contextDir = join8(localPath, "repowise-context");
4019
- await mkdir5(contextDir, { recursive: true });
4524
+ const contextDir = join9(localPath, "repowise-context");
4525
+ await mkdir6(contextDir, { recursive: true });
4020
4526
  const updatedFiles = [];
4021
4527
  for (const file of files) {
4022
4528
  if (file.fileName.includes(".."))
@@ -4040,10 +4546,10 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4040
4546
  continue;
4041
4547
  }
4042
4548
  const content = await contentRes.text();
4043
- const filePath = join8(contextDir, file.fileName);
4044
- await mkdir5(dirname3(filePath), { recursive: true });
4549
+ const filePath = join9(contextDir, file.fileName);
4550
+ await mkdir6(dirname3(filePath), { recursive: true });
4045
4551
  const finalContent = file.fileName === "project-overview.md" ? injectAgentInstructions(content) : content;
4046
- await writeFile5(filePath, finalContent, "utf-8");
4552
+ await writeFile6(filePath, finalContent, "utf-8");
4047
4553
  updatedFiles.push(file.fileName);
4048
4554
  }
4049
4555
  console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
@@ -4059,12 +4565,12 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4059
4565
  init_process_manager();
4060
4566
 
4061
4567
  // ../listener/dist/lib/auto-updater.js
4062
- import { execFile as execFile2 } from "child_process";
4568
+ import { execFile as execFile3 } from "child_process";
4063
4569
  import { readFileSync } from "fs";
4064
4570
  import { access as access2, constants, realpath } from "fs/promises";
4065
- import { dirname as dirname4, join as join10 } from "path";
4571
+ import { dirname as dirname4, join as join11 } from "path";
4066
4572
  import { promisify as promisify2 } from "util";
4067
- var execFileAsync2 = promisify2(execFile2);
4573
+ var execFileAsync2 = promisify2(execFile3);
4068
4574
  var installInFlight = false;
4069
4575
  async function installUpdate(currentVersion, packageName, targetVersion) {
4070
4576
  if (process.env["CI"] === "true")
@@ -4086,12 +4592,12 @@ async function installUpdate(currentVersion, packageName, targetVersion) {
4086
4592
  }
4087
4593
  installInFlight = true;
4088
4594
  try {
4089
- const npmWrapper = join10(dirname4(process.execPath), "npm");
4595
+ const npmWrapper = join11(dirname4(process.execPath), "npm");
4090
4596
  const npmScript = await realpath(npmWrapper);
4091
4597
  const runNpm = (args) => execFileAsync2(process.execPath, [npmScript, ...args], { timeout: 6e4 });
4092
4598
  try {
4093
4599
  const { stdout: prefix } = await runNpm(["prefix", "-g"]);
4094
- const npmDir = join10(prefix.trim(), "lib", "node_modules");
4600
+ const npmDir = join11(prefix.trim(), "lib", "node_modules");
4095
4601
  const checkDir = process.platform === "win32" ? prefix.trim() : npmDir;
4096
4602
  await access2(checkDir, constants.W_OK);
4097
4603
  } catch (err) {
@@ -4109,7 +4615,7 @@ async function installUpdate(currentVersion, packageName, targetVersion) {
4109
4615
  }
4110
4616
  try {
4111
4617
  const { stdout: prefix } = await runNpm(["prefix", "-g"]);
4112
- const installedPkgJson = join10(prefix.trim(), "lib", "node_modules", packageName, "package.json");
4618
+ const installedPkgJson = join11(prefix.trim(), "lib", "node_modules", packageName, "package.json");
4113
4619
  const installedVersion = JSON.parse(readFileSync(installedPkgJson, "utf-8")).version;
4114
4620
  if (installedVersion !== targetVersion) {
4115
4621
  const msg = `post-install check failed: expected ${targetVersion}, found ${installedVersion ?? "unknown"}`;
@@ -4180,7 +4686,7 @@ function comparePrerelease(a, b) {
4180
4686
  import { createHash } from "crypto";
4181
4687
  import { lstat, open as open3, readdir as readdir2 } from "fs/promises";
4182
4688
  import { constants as fsConstants } from "fs";
4183
- import { join as join11, resolve as pathResolve } from "path";
4689
+ import { join as join12, resolve as pathResolve } from "path";
4184
4690
  async function verifyBinary(check) {
4185
4691
  const fh = await open3(check.path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
4186
4692
  const hash = createHash("sha256");
@@ -4230,7 +4736,7 @@ async function auditNativeBindings(rootDir, expected) {
4230
4736
  return;
4231
4737
  }
4232
4738
  for (const entryName of entries) {
4233
- const full = pathResolve(join11(dir, entryName));
4739
+ const full = pathResolve(join12(dir, entryName));
4234
4740
  const entryStat = await lstat(full).catch(() => null);
4235
4741
  if (!entryStat)
4236
4742
  continue;
@@ -4271,8 +4777,8 @@ async function auditNativeBindings(rootDir, expected) {
4271
4777
  init_config_dir();
4272
4778
  init_process_manager();
4273
4779
  init_service_installer();
4274
- import { unlink as unlink6 } from "fs/promises";
4275
- import { join as join13 } from "path";
4780
+ import { unlink as unlink7 } from "fs/promises";
4781
+ import { join as join14 } from "path";
4276
4782
  async function getListenerStatus() {
4277
4783
  const pid = await readPid();
4278
4784
  if (pid !== null) {
@@ -4281,7 +4787,7 @@ async function getListenerStatus() {
4281
4787
  return { running: true, method: "pid", pid, serviceInstalled: serviceInstalled2 };
4282
4788
  }
4283
4789
  try {
4284
- await unlink6(join13(getConfigDir(), "listener.pid"));
4790
+ await unlink7(join14(getConfigDir(), "listener.pid"));
4285
4791
  } catch {
4286
4792
  }
4287
4793
  }
@@ -4326,11 +4832,11 @@ async function stopListener() {
4326
4832
 
4327
4833
  // ../listener/dist/mcp/bootstrap.js
4328
4834
  init_config_dir();
4329
- import { join as join21 } from "path";
4835
+ import { join as join22 } from "path";
4330
4836
 
4331
4837
  // ../listener/dist/lsp/workspace-session.js
4332
4838
  import { pathToFileURL } from "url";
4333
- import { readFile as readFile6 } from "fs/promises";
4839
+ import { readFile as readFile7 } from "fs/promises";
4334
4840
  import { resolve as pathResolve2, sep as pathSep, dirname as dirname7 } from "path";
4335
4841
 
4336
4842
  // ../listener/dist/lsp/lsp-client.js
@@ -4776,7 +5282,7 @@ var WorkspaceManager = class {
4776
5282
  }
4777
5283
  const absolute = this.joinPath(session.repoRoot, repoRelativePath);
4778
5284
  const p = (async () => {
4779
- const text = await readFile6(absolute, "utf-8");
5285
+ const text = await readFile7(absolute, "utf-8");
4780
5286
  session.client.notify("textDocument/didOpen", {
4781
5287
  textDocument: {
4782
5288
  uri,
@@ -5046,8 +5552,8 @@ function buildLspSpawnPath(basePath, nodeDir, lspBinDirs) {
5046
5552
 
5047
5553
  // ../listener/dist/mcp/graph-cache.js
5048
5554
  init_config_dir();
5049
- import { readFile as readFile8, stat as stat3 } from "fs/promises";
5050
- import { join as join19 } from "path";
5555
+ import { readFile as readFile9, stat as stat3 } from "fs/promises";
5556
+ import { join as join20 } from "path";
5051
5557
  var EVICT_DEBOUNCE_MS = 6e4;
5052
5558
  function assertSafeRepoId(repoId) {
5053
5559
  if (!repoId || typeof repoId !== "string") {
@@ -5062,13 +5568,13 @@ function assertSafeRepoId(repoId) {
5062
5568
  }
5063
5569
  function createGraphCache(options = {}) {
5064
5570
  const evictMs = options.evictDebounceMs ?? EVICT_DEBOUNCE_MS;
5065
- const resolvePath = options.resolveGraphPath ?? ((repoId) => join19(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5571
+ const resolvePath = options.resolveGraphPath ?? ((repoId) => join20(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5066
5572
  const entries = /* @__PURE__ */ new Map();
5067
5573
  const inFlight = /* @__PURE__ */ new Map();
5068
5574
  async function loadFromDisk(repoId) {
5069
5575
  const graphPath = resolvePath(repoId);
5070
5576
  const s = await stat3(graphPath);
5071
- const body = await readFile8(graphPath, "utf-8");
5577
+ const body = await readFile9(graphPath, "utf-8");
5072
5578
  const graph = JSON.parse(body);
5073
5579
  if (graph.commitSha && Array.isArray(graph.edges)) {
5074
5580
  try {
@@ -5155,7 +5661,7 @@ function defaultRepoWiseHome() {
5155
5661
  }
5156
5662
 
5157
5663
  // ../listener/dist/mcp/graph-downloader.js
5158
- import { mkdir as mkdir9, rename as rename3, writeFile as writeFile9 } from "fs/promises";
5664
+ import { mkdir as mkdir10, rename as rename3, writeFile as writeFile10 } from "fs/promises";
5159
5665
  import { dirname as dirname9 } from "path";
5160
5666
  function createGraphDownloader(options) {
5161
5667
  const fetchFn = options.fetchImpl ?? fetch;
@@ -5207,9 +5713,9 @@ function createGraphDownloader(options) {
5207
5713
  message: `graph parse: ${err instanceof Error ? err.message : String(err)}`
5208
5714
  };
5209
5715
  }
5210
- await mkdir9(dirname9(options.targetPath), { recursive: true });
5716
+ await mkdir10(dirname9(options.targetPath), { recursive: true });
5211
5717
  const tmpPath = `${options.targetPath}.${Date.now()}.tmp`;
5212
- await writeFile9(tmpPath, body, { encoding: "utf-8", mode: 384 });
5718
+ await writeFile10(tmpPath, body, { encoding: "utf-8", mode: 384 });
5213
5719
  await rename3(tmpPath, options.targetPath);
5214
5720
  options.graphCache.invalidate(options.repoId);
5215
5721
  lastSha = serverSha;
@@ -5232,7 +5738,7 @@ function createGraphDownloader(options) {
5232
5738
 
5233
5739
  // ../listener/dist/mcp/log-encryption.js
5234
5740
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
5235
- import { mkdir as mkdir10, readFile as readFile9, stat as stat4, writeFile as writeFile10 } from "fs/promises";
5741
+ import { mkdir as mkdir11, readFile as readFile10, stat as stat4, writeFile as writeFile11 } from "fs/promises";
5236
5742
  import { dirname as dirname10 } from "path";
5237
5743
  var KEY_BYTES = 32;
5238
5744
  var IV_BYTES = 12;
@@ -5260,7 +5766,7 @@ function createFileKeyStore(keyPath) {
5260
5766
  return cached;
5261
5767
  let parsed = null;
5262
5768
  try {
5263
- const body = await readFile9(keyPath, "utf-8");
5769
+ const body = await readFile10(keyPath, "utf-8");
5264
5770
  const decoded = Buffer.from(body.trim(), "base64");
5265
5771
  if (decoded.length === KEY_BYTES)
5266
5772
  parsed = decoded;
@@ -5280,9 +5786,9 @@ function createFileKeyStore(keyPath) {
5280
5786
  return parsed;
5281
5787
  }
5282
5788
  const fresh = randomBytes(KEY_BYTES);
5283
- await mkdir10(dirname10(keyPath), { recursive: true });
5789
+ await mkdir11(dirname10(keyPath), { recursive: true });
5284
5790
  const tmp = `${keyPath}.tmp`;
5285
- await writeFile10(tmp, fresh.toString("base64") + "\n", {
5791
+ await writeFile11(tmp, fresh.toString("base64") + "\n", {
5286
5792
  encoding: "utf-8",
5287
5793
  mode: 384
5288
5794
  });
@@ -5321,7 +5827,7 @@ function decryptLine(encoded, key) {
5321
5827
  }
5322
5828
 
5323
5829
  // ../listener/dist/mcp/mcp-logger.js
5324
- import { appendFile, mkdir as mkdir11, stat as stat5, unlink as unlink8, writeFile as writeFile11, readFile as readFile10 } from "fs/promises";
5830
+ import { appendFile, mkdir as mkdir12, stat as stat5, unlink as unlink9, writeFile as writeFile12, readFile as readFile11 } from "fs/promises";
5325
5831
  import { dirname as dirname11 } from "path";
5326
5832
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
5327
5833
  function createMcpLogger(options) {
@@ -5336,7 +5842,7 @@ function createMcpLogger(options) {
5336
5842
  if (legacyPath === options.filePath)
5337
5843
  return;
5338
5844
  try {
5339
- await unlink8(legacyPath);
5845
+ await unlink9(legacyPath);
5340
5846
  console.log(`[mcp-logger] removed legacy plaintext log at ${legacyPath} (one-time migration)`);
5341
5847
  } catch {
5342
5848
  }
@@ -5345,7 +5851,7 @@ function createMcpLogger(options) {
5345
5851
  await migrateLegacyPlaintext();
5346
5852
  const key = await options.keyStore.getKey();
5347
5853
  const encoded = encryptLine(JSON.stringify(entry), key) + "\n";
5348
- await mkdir11(dirname11(options.filePath), { recursive: true });
5854
+ await mkdir12(dirname11(options.filePath), { recursive: true });
5349
5855
  await appendFile(options.filePath, encoded, { encoding: "utf-8", mode: 384 });
5350
5856
  try {
5351
5857
  const s = await stat5(options.filePath);
@@ -5367,18 +5873,18 @@ function createMcpLogger(options) {
5367
5873
  }
5368
5874
  async function rotate(path, size) {
5369
5875
  const dropAt = Math.floor(size * 0.25);
5370
- const body = await readFile10(path, "utf-8");
5876
+ const body = await readFile11(path, "utf-8");
5371
5877
  const idx = body.indexOf("\n", dropAt);
5372
5878
  if (idx < 0) {
5373
- await writeFile11(path, "", { encoding: "utf-8", mode: 384 });
5879
+ await writeFile12(path, "", { encoding: "utf-8", mode: 384 });
5374
5880
  return;
5375
5881
  }
5376
- await writeFile11(path, body.slice(idx + 1), { encoding: "utf-8", mode: 384 });
5882
+ await writeFile12(path, body.slice(idx + 1), { encoding: "utf-8", mode: 384 });
5377
5883
  }
5378
5884
 
5379
5885
  // ../listener/dist/mcp/mcp-log-uploader.js
5380
5886
  import { randomUUID } from "crypto";
5381
- import { readFile as readFile11, writeFile as writeFile12, mkdir as mkdir12 } from "fs/promises";
5887
+ import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir13 } from "fs/promises";
5382
5888
  import { dirname as dirname12 } from "path";
5383
5889
  var DEFAULT_MAX_ENTRIES = 5e3;
5384
5890
  var DEFAULT_MAX_BYTES2 = 1 * 1024 * 1024;
@@ -5404,7 +5910,7 @@ function createMcpLogUploader(options) {
5404
5910
  }
5405
5911
  let body;
5406
5912
  try {
5407
- body = await readFile11(options.logFilePath, "utf-8");
5913
+ body = await readFile12(options.logFilePath, "utf-8");
5408
5914
  } catch (err) {
5409
5915
  if (err.code === "ENOENT") {
5410
5916
  return { uploaded: 0, bytesAdvanced: 0 };
@@ -5521,7 +6027,7 @@ function pendingPath(watermarkPath) {
5521
6027
  }
5522
6028
  async function readPendingBatch(watermarkPath) {
5523
6029
  try {
5524
- const body = await readFile11(pendingPath(watermarkPath), "utf-8");
6030
+ const body = await readFile12(pendingPath(watermarkPath), "utf-8");
5525
6031
  const trimmed = body.trim();
5526
6032
  return /^[A-Za-z0-9-]{8,128}$/.test(trimmed) ? trimmed : null;
5527
6033
  } catch {
@@ -5529,19 +6035,19 @@ async function readPendingBatch(watermarkPath) {
5529
6035
  }
5530
6036
  }
5531
6037
  async function writePendingBatch(watermarkPath, batchId) {
5532
- await mkdir12(dirname12(watermarkPath), { recursive: true });
5533
- await writeFile12(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
6038
+ await mkdir13(dirname12(watermarkPath), { recursive: true });
6039
+ await writeFile13(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
5534
6040
  }
5535
6041
  async function clearPendingBatch(watermarkPath) {
5536
- const { unlink: unlink11 } = await import("fs/promises");
6042
+ const { unlink: unlink12 } = await import("fs/promises");
5537
6043
  try {
5538
- await unlink11(pendingPath(watermarkPath));
6044
+ await unlink12(pendingPath(watermarkPath));
5539
6045
  } catch {
5540
6046
  }
5541
6047
  }
5542
6048
  async function readWatermark(path) {
5543
6049
  try {
5544
- const body = await readFile11(path, "utf-8");
6050
+ const body = await readFile12(path, "utf-8");
5545
6051
  const parsed = Number.parseInt(body.trim(), 10);
5546
6052
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
5547
6053
  } catch (err) {
@@ -5551,15 +6057,15 @@ async function readWatermark(path) {
5551
6057
  }
5552
6058
  }
5553
6059
  async function writeWatermark(path, offset) {
5554
- await mkdir12(dirname12(path), { recursive: true });
6060
+ await mkdir13(dirname12(path), { recursive: true });
5555
6061
  const tmp = `${path}.tmp`;
5556
- await writeFile12(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
6062
+ await writeFile13(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
5557
6063
  const { rename: rename5 } = await import("fs/promises");
5558
6064
  await rename5(tmp, path);
5559
6065
  }
5560
6066
  async function isLocallyConsented(flagPath2) {
5561
6067
  try {
5562
- const body = await readFile11(flagPath2, "utf-8");
6068
+ const body = await readFile12(flagPath2, "utf-8");
5563
6069
  const parsed = JSON.parse(body);
5564
6070
  return parsed.enabled !== false;
5565
6071
  } catch {
@@ -5570,8 +6076,8 @@ async function isLocallyConsented(flagPath2) {
5570
6076
  // ../listener/dist/mcp/mcp-server.js
5571
6077
  init_config_dir();
5572
6078
  import { createServer } from "http";
5573
- import { mkdir as mkdir13, writeFile as writeFile13 } from "fs/promises";
5574
- import { dirname as dirname13, join as join20 } from "path";
6079
+ import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
6080
+ import { dirname as dirname13, join as join21 } from "path";
5575
6081
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
5576
6082
 
5577
6083
  // ../listener/dist/mcp/sanitize.js
@@ -7089,8 +7595,8 @@ async function readJson(req) {
7089
7595
  }
7090
7596
  }
7091
7597
  async function writeEndpointFile(path, endpoint, secret) {
7092
- await mkdir13(dirname13(path), { recursive: true });
7093
- await writeFile13(path, formatEndpointFile({ endpoint, secret }), {
7598
+ await mkdir14(dirname13(path), { recursive: true });
7599
+ await writeFile14(path, formatEndpointFile({ endpoint, secret }), {
7094
7600
  encoding: "utf-8",
7095
7601
  mode: 384
7096
7602
  });
@@ -7103,7 +7609,7 @@ function extractBearer(header) {
7103
7609
  return m ? m[1] : null;
7104
7610
  }
7105
7611
  function defaultEndpointFile() {
7106
- return join20(getConfigDir(), "listener.endpoint");
7612
+ return join21(getConfigDir(), "listener.endpoint");
7107
7613
  }
7108
7614
 
7109
7615
  // ../listener/dist/mcp/bootstrap.js
@@ -7111,7 +7617,7 @@ async function startMcp(options) {
7111
7617
  const disabled = process.env.REPOWISE_MCP_DISABLED === "true";
7112
7618
  const graphsDir = options.graphsDir ?? defaultGraphsDir();
7113
7619
  const graphCache = createGraphCache({
7114
- resolveGraphPath: (repoId) => join21(graphsDir, `${repoId}.json`)
7620
+ resolveGraphPath: (repoId) => join22(graphsDir, `${repoId}.json`)
7115
7621
  });
7116
7622
  if (disabled) {
7117
7623
  return {
@@ -7134,12 +7640,12 @@ async function startMcp(options) {
7134
7640
  }
7135
7641
  const firstRepoLocal = options.repos.find((r) => r.localPath)?.localPath;
7136
7642
  const mcpHome = defaultMcpHome();
7137
- const logFilePath = join21(mcpHome, "mcp-log.jsonl.enc");
7138
- const keyStore = createFileKeyStore(join21(mcpHome, "mcp-log.key"));
7643
+ const logFilePath = join22(mcpHome, "mcp-log.jsonl.enc");
7644
+ const keyStore = createFileKeyStore(join22(mcpHome, "mcp-log.key"));
7139
7645
  const mcpLogger = createMcpLogger({ filePath: logFilePath, keyStore });
7140
- const flagFilePath = join21(mcpHome, "mcp-log.flag");
7141
- const watermarkFilePath = join21(mcpHome, "mcp-log.watermark");
7142
- const consentSentMarkerPath = join21(mcpHome, "mcp-log.consent-sent");
7646
+ const flagFilePath = join22(mcpHome, "mcp-log.flag");
7647
+ const watermarkFilePath = join22(mcpHome, "mcp-log.watermark");
7648
+ const consentSentMarkerPath = join22(mcpHome, "mcp-log.consent-sent");
7143
7649
  let uploader = null;
7144
7650
  let lastConsentState = false;
7145
7651
  let serverConsentEnsuredThisProcess = false;
@@ -7186,7 +7692,7 @@ async function startMcp(options) {
7186
7692
  apiBaseUrl: repo.apiUrl,
7187
7693
  getAuthToken: options.getAuthToken,
7188
7694
  repoId: repo.repoId,
7189
- targetPath: join21(graphsDir, `${repo.repoId}.json`),
7695
+ targetPath: join22(graphsDir, `${repo.repoId}.json`),
7190
7696
  graphCache,
7191
7697
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7192
7698
  }));
@@ -7271,7 +7777,7 @@ async function startMcp(options) {
7271
7777
  apiBaseUrl: repo.apiUrl,
7272
7778
  getAuthToken: options.getAuthToken,
7273
7779
  repoId: repo.repoId,
7274
- targetPath: join21(graphsDir, `${repo.repoId}.json`),
7780
+ targetPath: join22(graphsDir, `${repo.repoId}.json`),
7275
7781
  graphCache,
7276
7782
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7277
7783
  }));
@@ -7292,15 +7798,15 @@ async function startMcp(options) {
7292
7798
  };
7293
7799
  }
7294
7800
  function defaultGraphsDir() {
7295
- return join21(getConfigDir(), "graphs");
7801
+ return join22(getConfigDir(), "graphs");
7296
7802
  }
7297
7803
  function defaultMcpHome() {
7298
7804
  return getConfigDir();
7299
7805
  }
7300
7806
  async function logFileExists(path) {
7301
7807
  try {
7302
- const { stat: stat7 } = await import("fs/promises");
7303
- const s = await stat7(path);
7808
+ const { stat: stat8 } = await import("fs/promises");
7809
+ const s = await stat8(path);
7304
7810
  return s.size > 0;
7305
7811
  } catch {
7306
7812
  return false;
@@ -7333,10 +7839,10 @@ async function ensureServerConsent(opts) {
7333
7839
  });
7334
7840
  if (!res.ok)
7335
7841
  return;
7336
- const fs25 = await import("fs/promises");
7842
+ const fs30 = await import("fs/promises");
7337
7843
  const path = await import("path");
7338
- await fs25.mkdir(path.dirname(opts.markerPath), { recursive: true });
7339
- await fs25.writeFile(opts.markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n", {
7844
+ await fs30.mkdir(path.dirname(opts.markerPath), { recursive: true });
7845
+ await fs30.writeFile(opts.markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n", {
7340
7846
  encoding: "utf-8",
7341
7847
  mode: 384
7342
7848
  });
@@ -7346,11 +7852,11 @@ async function ensureServerConsent(opts) {
7346
7852
 
7347
7853
  // ../listener/dist/mcp/auto-config/index.js
7348
7854
  import { homedir as homedir5 } from "os";
7349
- import { basename as basename2 } from "path";
7855
+ import { basename as basename3 } from "path";
7350
7856
 
7351
7857
  // ../listener/dist/mcp/auto-config/writers/claude-code.js
7352
7858
  import { promises as fs6 } from "fs";
7353
- import { join as join22 } from "path";
7859
+ import { join as join23 } from "path";
7354
7860
 
7355
7861
  // ../listener/dist/mcp/auto-config/markers.js
7356
7862
  import { promises as fs5 } from "fs";
@@ -7430,10 +7936,10 @@ function safeExistingContent(path, current) {
7430
7936
  var claudeCodeWriter = {
7431
7937
  tool: "claude-code",
7432
7938
  async detect(repoRoot, home) {
7433
- return await fileExists2(join22(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
7939
+ return await fileExists2(join23(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
7434
7940
  },
7435
7941
  async write(ctx) {
7436
- const path = join22(ctx.repoRoot, ".mcp.json");
7942
+ const path = join23(ctx.repoRoot, ".mcp.json");
7437
7943
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7438
7944
  const status2 = await writeMergedConfig({
7439
7945
  path,
@@ -7443,7 +7949,7 @@ var claudeCodeWriter = {
7443
7949
  return { status: status2, path };
7444
7950
  },
7445
7951
  async remove(ctx) {
7446
- const path = join22(ctx.repoRoot, ".mcp.json");
7952
+ const path = join23(ctx.repoRoot, ".mcp.json");
7447
7953
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7448
7954
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7449
7955
  }
@@ -7457,31 +7963,23 @@ async function fileExists2(path) {
7457
7963
  }
7458
7964
  }
7459
7965
  async function hasClaudeBinary(home) {
7460
- return fileExists2(join22(home, ".claude", "claude.json"));
7966
+ return fileExists2(join23(home, ".claude", "claude.json"));
7461
7967
  }
7462
7968
 
7463
- // ../listener/dist/mcp/auto-config/writers/cline.js
7969
+ // ../listener/dist/mcp/auto-config/writers/claude-code-hooks.js
7464
7970
  import { promises as fs7 } from "fs";
7465
- import { join as join23 } from "path";
7466
- var clineWriter = {
7467
- tool: "cline",
7971
+ import { join as join24 } from "path";
7972
+ var claudeCodeHooksWriter = {
7973
+ tool: "claude-code-hooks",
7468
7974
  async detect(repoRoot, home) {
7469
- return await fileExists3(join23(repoRoot, ".clinerules")) || await fileExists3(join23(home, ".cline"));
7975
+ return await fileExists3(join24(repoRoot, "CLAUDE.md")) || await fileExists3(join24(home, ".claude.json"));
7470
7976
  },
7471
7977
  async write(ctx) {
7472
- const path = join23(ctx.home, ".cline", "mcp.json");
7473
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
7474
- const status2 = await writeMergedConfig({
7475
- path,
7476
- serverName: `RepoWise MCP for ${ctx.repoName}`,
7477
- spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
7478
- });
7479
- return { status: status2, path };
7978
+ const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context");
7979
+ return { status: result.status, path: join24(ctx.repoRoot, result.relPath) };
7480
7980
  },
7481
7981
  async remove(ctx) {
7482
- const path = join23(ctx.home, ".cline", "mcp.json");
7483
- await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7484
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
7982
+ await removeClaudeHooksFromRepo(ctx.repoRoot);
7485
7983
  }
7486
7984
  };
7487
7985
  async function fileExists3(path) {
@@ -7493,16 +7991,16 @@ async function fileExists3(path) {
7493
7991
  }
7494
7992
  }
7495
7993
 
7496
- // ../listener/dist/mcp/auto-config/writers/codex.js
7994
+ // ../listener/dist/mcp/auto-config/writers/cline.js
7497
7995
  import { promises as fs8 } from "fs";
7498
- import { join as join24 } from "path";
7499
- var codexWriter = {
7500
- tool: "codex",
7501
- async detect(_repoRoot, home) {
7502
- return fileExists4(join24(home, ".codex"));
7996
+ import { join as join25 } from "path";
7997
+ var clineWriter = {
7998
+ tool: "cline",
7999
+ async detect(repoRoot, home) {
8000
+ return await fileExists4(join25(repoRoot, ".clinerules")) || await fileExists4(join25(home, ".cline"));
7503
8001
  },
7504
8002
  async write(ctx) {
7505
- const path = join24(ctx.home, ".codex", "mcp.json");
8003
+ const path = join25(ctx.home, ".cline", "mcp.json");
7506
8004
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7507
8005
  const status2 = await writeMergedConfig({
7508
8006
  path,
@@ -7512,7 +8010,7 @@ var codexWriter = {
7512
8010
  return { status: status2, path };
7513
8011
  },
7514
8012
  async remove(ctx) {
7515
- const path = join24(ctx.home, ".codex", "mcp.json");
8013
+ const path = join25(ctx.home, ".cline", "mcp.json");
7516
8014
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7517
8015
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7518
8016
  }
@@ -7526,30 +8024,64 @@ async function fileExists4(path) {
7526
8024
  }
7527
8025
  }
7528
8026
 
7529
- // ../listener/dist/mcp/auto-config/writers/copilot.js
8027
+ // ../listener/dist/mcp/auto-config/writers/cline-hooks.js
7530
8028
  import { promises as fs9 } from "fs";
7531
- import { join as join25 } from "path";
7532
- var copilotWriter = {
7533
- tool: "copilot",
8029
+ import { join as join26 } from "path";
8030
+ var SCRIPT_REL = ".clinerules/hooks/UserPromptSubmit";
8031
+ var OWNERSHIP_MARKER = "managed by RepoWise";
8032
+ var clineHooksWriter = {
8033
+ tool: "cline-hooks",
7534
8034
  async detect(repoRoot) {
7535
- return fileExists5(join25(repoRoot, ".vscode"));
8035
+ return fileExists5(join26(repoRoot, ".clinerules"));
7536
8036
  },
7537
8037
  async write(ctx) {
7538
- const path = join25(ctx.repoRoot, ".vscode", "mcp.json");
7539
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
7540
- const status2 = await writeMergedConfig({
7541
- path,
7542
- serverName: `RepoWise MCP for ${ctx.repoName}`,
7543
- spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8038
+ if (await isGitTracked(ctx.repoRoot, SCRIPT_REL)) {
8039
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SCRIPT_REL] });
8040
+ return {
8041
+ status: "skipped",
8042
+ reason: `${SCRIPT_REL} is git-tracked; update it manually to opt in`
8043
+ };
8044
+ }
8045
+ const path = join26(ctx.repoRoot, SCRIPT_REL);
8046
+ const next = buildClineHookScript({
8047
+ repoName: ctx.repoName,
8048
+ contextFolder: ctx.contextFolder ?? "repowise-context"
7544
8049
  });
7545
- return { status: status2, path };
8050
+ const existing = await readOrNull(path);
8051
+ if (existing !== null && !existing.includes(OWNERSHIP_MARKER)) {
8052
+ return {
8053
+ status: "skipped",
8054
+ reason: `${SCRIPT_REL} exists and is user-authored; not overwriting`
8055
+ };
8056
+ }
8057
+ if (existing === next) {
8058
+ await ensureExecutable(path);
8059
+ return { status: "unchanged", path };
8060
+ }
8061
+ await fs9.mkdir(join26(ctx.repoRoot, ".clinerules", "hooks"), { recursive: true });
8062
+ await fs9.writeFile(path, next, { encoding: "utf-8", mode: 493 });
8063
+ await ensureExecutable(path);
8064
+ await applyGitignoreChanges(ctx.repoRoot, { add: [SCRIPT_REL] });
8065
+ return { status: "written", path };
7546
8066
  },
7547
8067
  async remove(ctx) {
7548
- const path = join25(ctx.repoRoot, ".vscode", "mcp.json");
7549
- await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7550
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
8068
+ const path = join26(ctx.repoRoot, SCRIPT_REL);
8069
+ const existing = await readOrNull(path);
8070
+ if (existing !== null && existing.includes(OWNERSHIP_MARKER)) {
8071
+ await fs9.unlink(path);
8072
+ }
8073
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SCRIPT_REL] });
7551
8074
  }
7552
8075
  };
8076
+ async function ensureExecutable(path) {
8077
+ try {
8078
+ const stat8 = await fs9.stat(path);
8079
+ if ((stat8.mode & 73) === 0) {
8080
+ await fs9.chmod(path, 493);
8081
+ }
8082
+ } catch {
8083
+ }
8084
+ }
7553
8085
  async function fileExists5(path) {
7554
8086
  try {
7555
8087
  await fs9.access(path);
@@ -7558,17 +8090,24 @@ async function fileExists5(path) {
7558
8090
  return false;
7559
8091
  }
7560
8092
  }
8093
+ async function readOrNull(path) {
8094
+ try {
8095
+ return await fs9.readFile(path, "utf-8");
8096
+ } catch {
8097
+ return null;
8098
+ }
8099
+ }
7561
8100
 
7562
- // ../listener/dist/mcp/auto-config/writers/cursor.js
8101
+ // ../listener/dist/mcp/auto-config/writers/codex.js
7563
8102
  import { promises as fs10 } from "fs";
7564
- import { join as join26 } from "path";
7565
- var cursorWriter = {
7566
- tool: "cursor",
7567
- async detect(repoRoot) {
7568
- return await fileExists6(join26(repoRoot, ".cursor")) || await fileExists6(join26(repoRoot, ".cursorrules"));
8103
+ import { join as join27 } from "path";
8104
+ var codexWriter = {
8105
+ tool: "codex",
8106
+ async detect(_repoRoot, home) {
8107
+ return fileExists6(join27(home, ".codex"));
7569
8108
  },
7570
8109
  async write(ctx) {
7571
- const path = join26(ctx.repoRoot, ".cursor", "mcp.json");
8110
+ const path = join27(ctx.home, ".codex", "mcp.json");
7572
8111
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7573
8112
  const status2 = await writeMergedConfig({
7574
8113
  path,
@@ -7578,7 +8117,7 @@ var cursorWriter = {
7578
8117
  return { status: status2, path };
7579
8118
  },
7580
8119
  async remove(ctx) {
7581
- const path = join26(ctx.repoRoot, ".cursor", "mcp.json");
8120
+ const path = join27(ctx.home, ".codex", "mcp.json");
7582
8121
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7583
8122
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7584
8123
  }
@@ -7592,28 +8131,54 @@ async function fileExists6(path) {
7592
8131
  }
7593
8132
  }
7594
8133
 
7595
- // ../listener/dist/mcp/auto-config/writers/gemini-cli.js
8134
+ // ../listener/dist/mcp/auto-config/writers/codex-hooks.js
7596
8135
  import { promises as fs11 } from "fs";
7597
- import { join as join27 } from "path";
7598
- var geminiCliWriter = {
7599
- tool: "gemini-cli",
7600
- async detect(_repoRoot, home) {
7601
- return fileExists7(join27(home, ".gemini"));
8136
+ import { join as join28 } from "path";
8137
+ var HOOKS_REL = ".codex/hooks.json";
8138
+ var codexHooksWriter = {
8139
+ tool: "codex-hooks",
8140
+ async detect(repoRoot, home) {
8141
+ return await fileExists7(join28(home, ".codex")) || await fileExists7(join28(repoRoot, ".codex"));
7602
8142
  },
7603
8143
  async write(ctx) {
7604
- const path = join27(ctx.home, ".gemini", "settings.json");
7605
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
7606
- const status2 = await writeMergedConfig({
7607
- path,
7608
- serverName: `RepoWise MCP for ${ctx.repoName}`,
7609
- spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8144
+ const repoSignal = await fileExists7(join28(ctx.repoRoot, ".codex"));
8145
+ const selected = ctx.selectedTools?.includes("codex") ?? false;
8146
+ if (!repoSignal && !selected) {
8147
+ return { status: "skipped", reason: "no repo-scoped Codex signal and not selected" };
8148
+ }
8149
+ if (await isGitTracked(ctx.repoRoot, HOOKS_REL)) {
8150
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL] });
8151
+ return {
8152
+ status: "skipped",
8153
+ reason: `${HOOKS_REL} is git-tracked; add the RepoWise UserPromptSubmit hook manually to opt in`
8154
+ };
8155
+ }
8156
+ const path = join28(ctx.repoRoot, HOOKS_REL);
8157
+ const existing = await readOrNull2(path);
8158
+ const merged = mergeCodexHookSettings(existing, {
8159
+ repoName: ctx.repoName,
8160
+ contextFolder: ctx.contextFolder ?? "repowise-context"
7610
8161
  });
7611
- return { status: status2, path };
8162
+ if (merged === existing) {
8163
+ return { status: "unchanged", path };
8164
+ }
8165
+ await fs11.mkdir(join28(ctx.repoRoot, ".codex"), { recursive: true });
8166
+ await fs11.writeFile(path, merged, "utf-8");
8167
+ await applyGitignoreChanges(ctx.repoRoot, { add: [HOOKS_REL] });
8168
+ return { status: "written", path };
7612
8169
  },
7613
8170
  async remove(ctx) {
7614
- const path = join27(ctx.home, ".gemini", "settings.json");
7615
- await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7616
- await removeFromConfig(path, `repowise-${ctx.repoId}`);
8171
+ const path = join28(ctx.repoRoot, HOOKS_REL);
8172
+ const existing = await readOrNull2(path);
8173
+ if (existing !== null) {
8174
+ const next = removeCodexHookSettings(existing);
8175
+ if (next === null) {
8176
+ await fs11.unlink(path);
8177
+ } else if (next !== existing) {
8178
+ await fs11.writeFile(path, next, "utf-8");
8179
+ }
8180
+ }
8181
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL] });
7617
8182
  }
7618
8183
  };
7619
8184
  async function fileExists7(path) {
@@ -7624,19 +8189,24 @@ async function fileExists7(path) {
7624
8189
  return false;
7625
8190
  }
7626
8191
  }
7627
-
7628
- // ../listener/dist/mcp/auto-config/writers/roo.js
7629
- import { promises as fs12 } from "fs";
7630
- import { join as join28 } from "path";
7631
- var rooWriter = {
7632
- tool: "roo",
7633
- async detect(repoRoot, home) {
7634
- return await fileExists8(join28(repoRoot, ".roo")) || await fileExists8(join28(home, ".roo"));
8192
+ async function readOrNull2(path) {
8193
+ try {
8194
+ return await fs11.readFile(path, "utf-8");
8195
+ } catch {
8196
+ return null;
8197
+ }
8198
+ }
8199
+
8200
+ // ../listener/dist/mcp/auto-config/writers/copilot.js
8201
+ import { promises as fs12 } from "fs";
8202
+ import { join as join29 } from "path";
8203
+ var copilotWriter = {
8204
+ tool: "copilot",
8205
+ async detect(repoRoot) {
8206
+ return fileExists8(join29(repoRoot, ".vscode"));
7635
8207
  },
7636
8208
  async write(ctx) {
7637
- const repoConfig = join28(ctx.repoRoot, ".roo", "mcp.json");
7638
- const useRepoScope = await fileExists8(join28(ctx.repoRoot, ".roo"));
7639
- const path = useRepoScope ? repoConfig : join28(ctx.home, ".roo", "mcp.json");
8209
+ const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
7640
8210
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7641
8211
  const status2 = await writeMergedConfig({
7642
8212
  path,
@@ -7646,12 +8216,9 @@ var rooWriter = {
7646
8216
  return { status: status2, path };
7647
8217
  },
7648
8218
  async remove(ctx) {
7649
- const repoConfig = join28(ctx.repoRoot, ".roo", "mcp.json");
7650
- const homeConfig = join28(ctx.home, ".roo", "mcp.json");
7651
- await removeFromConfig(repoConfig, `RepoWise MCP for ${ctx.repoName}`);
7652
- await removeFromConfig(repoConfig, `repowise-${ctx.repoId}`);
7653
- await removeFromConfig(homeConfig, `RepoWise MCP for ${ctx.repoName}`);
7654
- await removeFromConfig(homeConfig, `repowise-${ctx.repoId}`);
8219
+ const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
8220
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8221
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
7655
8222
  }
7656
8223
  };
7657
8224
  async function fileExists8(path) {
@@ -7663,16 +8230,254 @@ async function fileExists8(path) {
7663
8230
  }
7664
8231
  }
7665
8232
 
7666
- // ../listener/dist/mcp/auto-config/writers/windsurf.js
8233
+ // ../listener/dist/mcp/auto-config/writers/copilot-hooks.js
7667
8234
  import { promises as fs13 } from "fs";
7668
- import { join as join29 } from "path";
8235
+ import { join as join30 } from "path";
8236
+ var HOOKS_REL2 = ".github/hooks/repowise.json";
8237
+ var copilotHooksWriter = {
8238
+ tool: "copilot-hooks",
8239
+ async detect(repoRoot) {
8240
+ return fileExists9(join30(repoRoot, ".github", "copilot-instructions.md"));
8241
+ },
8242
+ async write(ctx) {
8243
+ const path = join30(ctx.repoRoot, HOOKS_REL2);
8244
+ const next = buildCopilotHooksFile({
8245
+ repoName: ctx.repoName,
8246
+ contextFolder: ctx.contextFolder ?? "repowise-context"
8247
+ });
8248
+ const existing = await readOrNull3(path);
8249
+ if (await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
8250
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL2] });
8251
+ if (existing === next) {
8252
+ return { status: "unchanged", path };
8253
+ }
8254
+ return {
8255
+ status: "skipped",
8256
+ reason: `${HOOKS_REL2} is committed; not modifying a tracked file`
8257
+ };
8258
+ }
8259
+ if (existing === next) {
8260
+ return { status: "unchanged", path };
8261
+ }
8262
+ await fs13.mkdir(join30(ctx.repoRoot, ".github", "hooks"), { recursive: true });
8263
+ await fs13.writeFile(path, next, "utf-8");
8264
+ const userSelected = ctx.selectedTools?.includes("copilot") ?? false;
8265
+ await applyGitignoreChanges(ctx.repoRoot, userSelected ? { remove: [HOOKS_REL2] } : { add: [HOOKS_REL2] });
8266
+ return { status: "written", path };
8267
+ },
8268
+ async remove(ctx) {
8269
+ const path = join30(ctx.repoRoot, HOOKS_REL2);
8270
+ if (!await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
8271
+ await fs13.unlink(path).catch(() => {
8272
+ });
8273
+ }
8274
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL2] });
8275
+ }
8276
+ };
8277
+ async function fileExists9(path) {
8278
+ try {
8279
+ await fs13.access(path);
8280
+ return true;
8281
+ } catch {
8282
+ return false;
8283
+ }
8284
+ }
8285
+ async function readOrNull3(path) {
8286
+ try {
8287
+ return await fs13.readFile(path, "utf-8");
8288
+ } catch {
8289
+ return null;
8290
+ }
8291
+ }
8292
+
8293
+ // ../listener/dist/mcp/auto-config/writers/cursor.js
8294
+ import { promises as fs14 } from "fs";
8295
+ import { join as join31 } from "path";
8296
+ var cursorWriter = {
8297
+ tool: "cursor",
8298
+ async detect(repoRoot) {
8299
+ return await fileExists10(join31(repoRoot, ".cursor")) || await fileExists10(join31(repoRoot, ".cursorrules"));
8300
+ },
8301
+ async write(ctx) {
8302
+ const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8303
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8304
+ const status2 = await writeMergedConfig({
8305
+ path,
8306
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
8307
+ spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8308
+ });
8309
+ return { status: status2, path };
8310
+ },
8311
+ async remove(ctx) {
8312
+ const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8313
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8314
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8315
+ }
8316
+ };
8317
+ async function fileExists10(path) {
8318
+ try {
8319
+ await fs14.access(path);
8320
+ return true;
8321
+ } catch {
8322
+ return false;
8323
+ }
8324
+ }
8325
+
8326
+ // ../listener/dist/mcp/auto-config/writers/gemini-cli.js
8327
+ import { promises as fs15 } from "fs";
8328
+ import { join as join32 } from "path";
8329
+ var geminiCliWriter = {
8330
+ tool: "gemini-cli",
8331
+ async detect(_repoRoot, home) {
8332
+ return fileExists11(join32(home, ".gemini"));
8333
+ },
8334
+ async write(ctx) {
8335
+ const path = join32(ctx.home, ".gemini", "settings.json");
8336
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8337
+ const status2 = await writeMergedConfig({
8338
+ path,
8339
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
8340
+ spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8341
+ });
8342
+ return { status: status2, path };
8343
+ },
8344
+ async remove(ctx) {
8345
+ const path = join32(ctx.home, ".gemini", "settings.json");
8346
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8347
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8348
+ }
8349
+ };
8350
+ async function fileExists11(path) {
8351
+ try {
8352
+ await fs15.access(path);
8353
+ return true;
8354
+ } catch {
8355
+ return false;
8356
+ }
8357
+ }
8358
+
8359
+ // ../listener/dist/mcp/auto-config/writers/gemini-hooks.js
8360
+ import { promises as fs16 } from "fs";
8361
+ import { join as join33 } from "path";
8362
+ var SETTINGS_REL = ".gemini/settings.json";
8363
+ var geminiHooksWriter = {
8364
+ tool: "gemini-hooks",
8365
+ async detect(repoRoot, home) {
8366
+ return await fileExists12(join33(home, ".gemini")) || await fileExists12(join33(repoRoot, ".gemini"));
8367
+ },
8368
+ async write(ctx) {
8369
+ const repoSignal = await fileExists12(join33(ctx.repoRoot, ".gemini")) || await fileExists12(join33(ctx.repoRoot, "GEMINI.md"));
8370
+ const selected = ctx.selectedTools?.includes("gemini") ?? false;
8371
+ if (!repoSignal && !selected) {
8372
+ return { status: "skipped", reason: "no repo-scoped Gemini signal and not selected" };
8373
+ }
8374
+ if (await isGitTracked(ctx.repoRoot, SETTINGS_REL)) {
8375
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SETTINGS_REL] });
8376
+ return {
8377
+ status: "skipped",
8378
+ reason: `${SETTINGS_REL} is git-tracked; add the RepoWise BeforeAgent hook manually to opt in`
8379
+ };
8380
+ }
8381
+ const path = join33(ctx.repoRoot, SETTINGS_REL);
8382
+ const existing = await readOrNull4(path);
8383
+ const merged = mergeGeminiHookSettings(existing, {
8384
+ repoName: ctx.repoName,
8385
+ contextFolder: ctx.contextFolder ?? "repowise-context"
8386
+ });
8387
+ if (merged === existing) {
8388
+ return { status: "unchanged", path };
8389
+ }
8390
+ await fs16.mkdir(join33(ctx.repoRoot, ".gemini"), { recursive: true });
8391
+ await fs16.writeFile(path, merged, "utf-8");
8392
+ await applyGitignoreChanges(ctx.repoRoot, { add: [SETTINGS_REL] });
8393
+ return { status: "written", path };
8394
+ },
8395
+ async remove(ctx) {
8396
+ const path = join33(ctx.repoRoot, SETTINGS_REL);
8397
+ const existing = await readOrNull4(path);
8398
+ if (existing !== null) {
8399
+ const next = removeGeminiHookSettings(existing);
8400
+ if (next === null) {
8401
+ await fs16.unlink(path);
8402
+ } else if (next !== existing) {
8403
+ await fs16.writeFile(path, next, "utf-8");
8404
+ }
8405
+ }
8406
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SETTINGS_REL] });
8407
+ }
8408
+ };
8409
+ async function fileExists12(path) {
8410
+ try {
8411
+ await fs16.access(path);
8412
+ return true;
8413
+ } catch {
8414
+ return false;
8415
+ }
8416
+ }
8417
+ async function readOrNull4(path) {
8418
+ try {
8419
+ return await fs16.readFile(path, "utf-8");
8420
+ } catch {
8421
+ return null;
8422
+ }
8423
+ }
8424
+
8425
+ // ../listener/dist/mcp/auto-config/writers/roo.js
8426
+ import { promises as fs17 } from "fs";
8427
+ import { join as join34 } from "path";
8428
+ var rooWriter = {
8429
+ tool: "roo",
8430
+ async detect(repoRoot, home) {
8431
+ return await fileExists13(join34(repoRoot, ".roo")) || await fileExists13(join34(home, ".roo"));
8432
+ },
8433
+ async write(ctx) {
8434
+ const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8435
+ const before = await readOrNull5(repoConfig);
8436
+ await this.remove(ctx);
8437
+ const after = await readOrNull5(repoConfig);
8438
+ return {
8439
+ status: before === after ? "unchanged" : "written",
8440
+ path: repoConfig
8441
+ };
8442
+ },
8443
+ async remove(ctx) {
8444
+ const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8445
+ const homeConfig = join34(ctx.home, ".roo", "mcp.json");
8446
+ const homeSettings = join34(ctx.home, ".roo", "settings.json");
8447
+ for (const path of [repoConfig, homeConfig, homeSettings]) {
8448
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`).catch(() => {
8449
+ });
8450
+ await removeFromConfig(path, `repowise-${ctx.repoId}`).catch(() => {
8451
+ });
8452
+ }
8453
+ }
8454
+ };
8455
+ async function fileExists13(path) {
8456
+ try {
8457
+ await fs17.access(path);
8458
+ return true;
8459
+ } catch {
8460
+ return false;
8461
+ }
8462
+ }
8463
+ async function readOrNull5(path) {
8464
+ try {
8465
+ return await fs17.readFile(path, "utf-8");
8466
+ } catch {
8467
+ return null;
8468
+ }
8469
+ }
8470
+
8471
+ // ../listener/dist/mcp/auto-config/writers/windsurf.js
8472
+ import { promises as fs18 } from "fs";
8473
+ import { join as join35 } from "path";
7669
8474
  var windsurfWriter = {
7670
8475
  tool: "windsurf",
7671
8476
  async detect(_repoRoot, home) {
7672
- return fileExists9(join29(home, ".codeium", "windsurf"));
8477
+ return fileExists14(join35(home, ".codeium", "windsurf"));
7673
8478
  },
7674
8479
  async write(ctx) {
7675
- const path = join29(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8480
+ const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
7676
8481
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7677
8482
  const status2 = await writeMergedConfig({
7678
8483
  path,
@@ -7682,14 +8487,14 @@ var windsurfWriter = {
7682
8487
  return { status: status2, path };
7683
8488
  },
7684
8489
  async remove(ctx) {
7685
- const path = join29(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8490
+ const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
7686
8491
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7687
8492
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7688
8493
  }
7689
8494
  };
7690
- async function fileExists9(path) {
8495
+ async function fileExists14(path) {
7691
8496
  try {
7692
- await fs13.access(path);
8497
+ await fs18.access(path);
7693
8498
  return true;
7694
8499
  } catch {
7695
8500
  return false;
@@ -7699,11 +8504,16 @@ async function fileExists9(path) {
7699
8504
  // ../listener/dist/mcp/auto-config/writers/index.js
7700
8505
  var WRITERS = [
7701
8506
  claudeCodeWriter,
8507
+ claudeCodeHooksWriter,
7702
8508
  clineWriter,
8509
+ clineHooksWriter,
7703
8510
  codexWriter,
8511
+ codexHooksWriter,
7704
8512
  copilotWriter,
8513
+ copilotHooksWriter,
7705
8514
  cursorWriter,
7706
8515
  geminiCliWriter,
8516
+ geminiHooksWriter,
7707
8517
  rooWriter,
7708
8518
  windsurfWriter
7709
8519
  ];
@@ -7732,10 +8542,12 @@ async function runAutoConfig(opts) {
7732
8542
  try {
7733
8543
  const outcome = await writer.write({
7734
8544
  repoRoot: opts.repoRoot,
7735
- repoName: basename2(opts.repoRoot),
8545
+ repoName: basename3(opts.repoRoot),
7736
8546
  repoId: opts.repoId,
7737
8547
  shimCmd: opts.shimCmd,
7738
- home
8548
+ home,
8549
+ contextFolder: opts.contextFolder,
8550
+ selectedTools: opts.selectedTools
7739
8551
  });
7740
8552
  results.push({ tool: writer.tool, detected: true, outcome });
7741
8553
  } catch (err) {
@@ -7755,8 +8567,8 @@ init_installer();
7755
8567
  // ../listener/dist/lsp/warm.js
7756
8568
  init_registry();
7757
8569
  init_lsp_tools();
7758
- import { promises as fs14 } from "fs";
7759
- import { join as join30 } from "path";
8570
+ import { promises as fs19 } from "fs";
8571
+ import { join as join36 } from "path";
7760
8572
  async function collectRepresentativeFiles(repoRoot) {
7761
8573
  const reps = /* @__PURE__ */ new Map();
7762
8574
  let inspected = 0;
@@ -7766,7 +8578,7 @@ async function collectRepresentativeFiles(repoRoot) {
7766
8578
  return;
7767
8579
  let entries;
7768
8580
  try {
7769
- entries = await fs14.readdir(dir);
8581
+ entries = await fs19.readdir(dir);
7770
8582
  } catch {
7771
8583
  return;
7772
8584
  }
@@ -7788,7 +8600,7 @@ async function collectRepresentativeFiles(repoRoot) {
7788
8600
  await inspect(repoRoot, "");
7789
8601
  let topEntries = [];
7790
8602
  try {
7791
- topEntries = await fs14.readdir(repoRoot);
8603
+ topEntries = await fs19.readdir(repoRoot);
7792
8604
  } catch {
7793
8605
  return reps;
7794
8606
  }
@@ -7797,10 +8609,10 @@ async function collectRepresentativeFiles(repoRoot) {
7797
8609
  continue;
7798
8610
  if (name === "node_modules" || name === "dist" || name === "build")
7799
8611
  continue;
7800
- const sub = join30(repoRoot, name);
8612
+ const sub = join36(repoRoot, name);
7801
8613
  try {
7802
- const stat7 = await fs14.stat(sub);
7803
- if (stat7.isDirectory())
8614
+ const stat8 = await fs19.stat(sub);
8615
+ if (stat8.isDirectory())
7804
8616
  await inspect(sub, name);
7805
8617
  } catch {
7806
8618
  }
@@ -7813,8 +8625,8 @@ async function warmReposLsp(repos, workspaces, lspOverrides, isAvailable) {
7813
8625
  if (!repoRoot)
7814
8626
  continue;
7815
8627
  try {
7816
- const stat7 = await fs14.stat(repoRoot);
7817
- if (!stat7.isDirectory())
8628
+ const stat8 = await fs19.stat(repoRoot);
8629
+ if (!stat8.isDirectory())
7818
8630
  continue;
7819
8631
  } catch {
7820
8632
  continue;
@@ -7842,12 +8654,12 @@ async function warmReposLsp(repos, workspaces, lspOverrides, isAvailable) {
7842
8654
 
7843
8655
  // ../listener/dist/lib/workspace-prep.js
7844
8656
  init_config_dir();
7845
- import { promises as fs15 } from "fs";
7846
- import { spawn as spawn9, execFile as execFile4 } from "child_process";
8657
+ import { promises as fs20 } from "fs";
8658
+ import { spawn as spawn9, execFile as execFile5 } from "child_process";
7847
8659
  import { createWriteStream as createWriteStream2 } from "fs";
7848
- import { join as join31 } from "path";
8660
+ import { join as join37 } from "path";
7849
8661
  import { promisify as promisify3 } from "util";
7850
- var execFileAsync3 = promisify3(execFile4);
8662
+ var execFileAsync3 = promisify3(execFile5);
7851
8663
  async function isCommandOnPath2(cmd) {
7852
8664
  try {
7853
8665
  await execFileAsync3("which", [cmd], { timeout: 2e3 });
@@ -7879,7 +8691,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7879
8691
  ".repowise/compile_commands.json"
7880
8692
  ];
7881
8693
  for (const c of candidates) {
7882
- if (await fileExists10(repoRoot, c))
8694
+ if (await fileExists15(repoRoot, c))
7883
8695
  return false;
7884
8696
  }
7885
8697
  const sources = [];
@@ -7899,7 +8711,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7899
8711
  return;
7900
8712
  let ents;
7901
8713
  try {
7902
- ents = await fs15.readdir(dir, { withFileTypes: true });
8714
+ ents = await fs20.readdir(dir, { withFileTypes: true });
7903
8715
  } catch {
7904
8716
  return;
7905
8717
  }
@@ -7909,7 +8721,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7909
8721
  if (e.isDirectory()) {
7910
8722
  if (skip.has(e.name) || e.name.startsWith("."))
7911
8723
  continue;
7912
- await rec(join31(dir, e.name));
8724
+ await rec(join37(dir, e.name));
7913
8725
  continue;
7914
8726
  }
7915
8727
  const dot = e.name.lastIndexOf(".");
@@ -7918,7 +8730,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7918
8730
  const ext = e.name.slice(dot);
7919
8731
  if (!C_EXTS.has(ext) && !CPP_EXTS.has(ext))
7920
8732
  continue;
7921
- sources.push({ path: join31(dir, e.name), ext });
8733
+ sources.push({ path: join37(dir, e.name), ext });
7922
8734
  }
7923
8735
  }
7924
8736
  await rec(repoRoot);
@@ -7934,18 +8746,18 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7934
8746
  command: `${driver} ${std} -I. -I./include -c "${src}"`
7935
8747
  };
7936
8748
  });
7937
- const outDir = join31(repoRoot, ".repowise");
7938
- await fs15.mkdir(outDir, { recursive: true });
7939
- const outPath = join31(outDir, "compile_commands.json");
7940
- await fs15.writeFile(outPath, JSON.stringify(entries, null, 2));
8749
+ const outDir = join37(repoRoot, ".repowise");
8750
+ await fs20.mkdir(outDir, { recursive: true });
8751
+ const outPath = join37(outDir, "compile_commands.json");
8752
+ await fs20.writeFile(outPath, JSON.stringify(entries, null, 2));
7941
8753
  const capHit = sources.length >= MAX_STUB_SOURCES;
7942
8754
  console.log(`[workspace-prep] ${repoId}: wrote ${entries.length.toString()}-entry compile_commands stub to .repowise/${capHit ? " (cap reached \u2014 additional sources truncated)" : ""}`);
7943
8755
  return true;
7944
8756
  }
7945
8757
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
7946
- async function fileExists10(repoRoot, name) {
8758
+ async function fileExists15(repoRoot, name) {
7947
8759
  try {
7948
- await fs15.access(join31(repoRoot, name));
8760
+ await fs20.access(join37(repoRoot, name));
7949
8761
  return true;
7950
8762
  } catch {
7951
8763
  return false;
@@ -7953,10 +8765,10 @@ async function fileExists10(repoRoot, name) {
7953
8765
  }
7954
8766
  async function detectWorkspaceDeps(repoRoot) {
7955
8767
  const missing = [];
7956
- if (await fileExists10(repoRoot, "package.json")) {
8768
+ if (await fileExists15(repoRoot, "package.json")) {
7957
8769
  let pkgRaw = "";
7958
8770
  try {
7959
- pkgRaw = await fs15.readFile(join31(repoRoot, "package.json"), "utf8");
8771
+ pkgRaw = await fs20.readFile(join37(repoRoot, "package.json"), "utf8");
7960
8772
  } catch {
7961
8773
  }
7962
8774
  if (pkgRaw) {
@@ -7968,16 +8780,16 @@ async function detectWorkspaceDeps(repoRoot) {
7968
8780
  } catch {
7969
8781
  }
7970
8782
  if (hasFrameworkDep) {
7971
- const installed = await fileExists10(repoRoot, "node_modules/.package-lock.json") || await fileExists10(repoRoot, "node_modules/.modules.yaml") || await fileExists10(repoRoot, "node_modules/.yarn-integrity") || await fileExists10(repoRoot, "node_modules/.bun-tag");
8783
+ const installed = await fileExists15(repoRoot, "node_modules/.package-lock.json") || await fileExists15(repoRoot, "node_modules/.modules.yaml") || await fileExists15(repoRoot, "node_modules/.yarn-integrity") || await fileExists15(repoRoot, "node_modules/.bun-tag");
7972
8784
  if (!installed) {
7973
8785
  let command = ["npm", "install", "--no-audit", "--no-fund"];
7974
- if (await fileExists10(repoRoot, "pnpm-lock.yaml")) {
8786
+ if (await fileExists15(repoRoot, "pnpm-lock.yaml")) {
7975
8787
  command = ["pnpm", "install", "--frozen-lockfile"];
7976
- } else if (await fileExists10(repoRoot, "yarn.lock")) {
8788
+ } else if (await fileExists15(repoRoot, "yarn.lock")) {
7977
8789
  command = ["yarn", "install", "--frozen-lockfile"];
7978
- } else if (await fileExists10(repoRoot, "bun.lock") || await fileExists10(repoRoot, "bun.lockb")) {
8790
+ } else if (await fileExists15(repoRoot, "bun.lock") || await fileExists15(repoRoot, "bun.lockb")) {
7979
8791
  command = ["bun", "install", "--frozen-lockfile"];
7980
- } else if (await fileExists10(repoRoot, "package-lock.json")) {
8792
+ } else if (await fileExists15(repoRoot, "package-lock.json")) {
7981
8793
  command = ["npm", "ci"];
7982
8794
  }
7983
8795
  missing.push({
@@ -7990,8 +8802,8 @@ async function detectWorkspaceDeps(repoRoot) {
7990
8802
  }
7991
8803
  }
7992
8804
  }
7993
- if (await fileExists10(repoRoot, "Gemfile")) {
7994
- const installed = await fileExists10(repoRoot, "Gemfile.lock") && (await fileExists10(repoRoot, "vendor/bundle") || await fileExists10(repoRoot, ".bundle/config"));
8805
+ if (await fileExists15(repoRoot, "Gemfile")) {
8806
+ const installed = await fileExists15(repoRoot, "Gemfile.lock") && (await fileExists15(repoRoot, "vendor/bundle") || await fileExists15(repoRoot, ".bundle/config"));
7995
8807
  if (!installed) {
7996
8808
  missing.push({
7997
8809
  ecosystem: "bundler",
@@ -8001,7 +8813,7 @@ async function detectWorkspaceDeps(repoRoot) {
8001
8813
  });
8002
8814
  }
8003
8815
  }
8004
- if (await fileExists10(repoRoot, "Cargo.toml")) {
8816
+ if (await fileExists15(repoRoot, "Cargo.toml")) {
8005
8817
  missing.push({
8006
8818
  ecosystem: "cargo",
8007
8819
  artefactName: "cargo registry cache",
@@ -8009,8 +8821,8 @@ async function detectWorkspaceDeps(repoRoot) {
8009
8821
  command: ["cargo", "metadata", "--format-version", "1", "--offline=false"]
8010
8822
  });
8011
8823
  }
8012
- if (await fileExists10(repoRoot, "pubspec.yaml")) {
8013
- if (!await fileExists10(repoRoot, ".dart_tool/package_config.json")) {
8824
+ if (await fileExists15(repoRoot, "pubspec.yaml")) {
8825
+ if (!await fileExists15(repoRoot, ".dart_tool/package_config.json")) {
8014
8826
  missing.push({
8015
8827
  ecosystem: "dart-pub",
8016
8828
  artefactName: ".dart_tool/",
@@ -8019,7 +8831,7 @@ async function detectWorkspaceDeps(repoRoot) {
8019
8831
  });
8020
8832
  }
8021
8833
  }
8022
- if (await fileExists10(repoRoot, "pom.xml")) {
8834
+ if (await fileExists15(repoRoot, "pom.xml")) {
8023
8835
  missing.push({
8024
8836
  ecosystem: "maven",
8025
8837
  artefactName: "maven (manual)",
@@ -8027,7 +8839,7 @@ async function detectWorkspaceDeps(repoRoot) {
8027
8839
  command: []
8028
8840
  });
8029
8841
  }
8030
- if (await fileExists10(repoRoot, "build.gradle") || await fileExists10(repoRoot, "build.gradle.kts")) {
8842
+ if (await fileExists15(repoRoot, "build.gradle") || await fileExists15(repoRoot, "build.gradle.kts")) {
8031
8843
  missing.push({
8032
8844
  ecosystem: "gradle",
8033
8845
  artefactName: "gradle (manual)",
@@ -8035,8 +8847,8 @@ async function detectWorkspaceDeps(repoRoot) {
8035
8847
  command: []
8036
8848
  });
8037
8849
  }
8038
- if (await fileExists10(repoRoot, "build.sbt")) {
8039
- const hasBloop = await fileExists10(repoRoot, ".bloop");
8850
+ if (await fileExists15(repoRoot, "build.sbt")) {
8851
+ const hasBloop = await fileExists15(repoRoot, ".bloop");
8040
8852
  if (hasBloop) {
8041
8853
  missing.push({
8042
8854
  ecosystem: "sbt",
@@ -8059,8 +8871,8 @@ async function runWorkspacePreps(opts) {
8059
8871
  if (opts.missing.length === 0)
8060
8872
  return [];
8061
8873
  const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
8062
- const logPath2 = join31(getConfigDir(), `install-log.${safeRepoId}.txt`);
8063
- await fs15.mkdir(getConfigDir(), { recursive: true });
8874
+ const logPath2 = join37(getConfigDir(), `install-log.${safeRepoId}.txt`);
8875
+ await fs20.mkdir(getConfigDir(), { recursive: true });
8064
8876
  const stream = opts.logStream ?? createWriteStream2(logPath2, { flags: "a" });
8065
8877
  stream.on("error", () => {
8066
8878
  });
@@ -8206,11 +9018,104 @@ async function prepareWorkspaceDepsForRepos(repos) {
8206
9018
  }
8207
9019
  }
8208
9020
 
9021
+ // ../listener/dist/lsp/build-orchestrator.js
9022
+ init_registry();
9023
+ import { execFile as execFileCb, spawn as spawn10 } from "child_process";
9024
+ import { access as access3, readdir as readdir4 } from "fs/promises";
9025
+ import { join as join38 } from "path";
9026
+ import { promisify as promisify4 } from "util";
9027
+ var execFile6 = promisify4(execFileCb);
9028
+ var DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1e3;
9029
+ async function isCommandOnPath3(command) {
9030
+ try {
9031
+ await execFile6(process.platform === "win32" ? "where" : "which", [command], {
9032
+ timeout: 5e3
9033
+ });
9034
+ return true;
9035
+ } catch {
9036
+ return false;
9037
+ }
9038
+ }
9039
+ async function hasBuildableManifest(root, language) {
9040
+ const spec = getBuildSpec(language);
9041
+ if (!spec)
9042
+ return false;
9043
+ let entries;
9044
+ try {
9045
+ entries = await readdir4(root);
9046
+ } catch {
9047
+ return false;
9048
+ }
9049
+ return spec.manifests.some((m) => m.startsWith("*.") ? entries.some((e) => e.endsWith(m.slice(1))) : entries.includes(m));
9050
+ }
9051
+ async function ensureIndexable(indexRoot, language, options = {}) {
9052
+ const spec = getBuildSpec(language);
9053
+ if (!spec)
9054
+ return { ready: true };
9055
+ if (!await hasBuildableManifest(indexRoot, language)) {
9056
+ return { ready: true };
9057
+ }
9058
+ if (spec.platform && (options.platform ?? process.platform) !== spec.platform) {
9059
+ return { ready: false, reason: "no-toolchain" };
9060
+ }
9061
+ const probe = options.isOnPath ?? isCommandOnPath3;
9062
+ for (const cmd of spec.gateCommands) {
9063
+ if (!await probe(cmd)) {
9064
+ console.warn(`[build-orchestrator] ${language}: '${cmd}' not on PATH \u2014 skipping build (AST-only for this language)`);
9065
+ return { ready: false, reason: "no-toolchain" };
9066
+ }
9067
+ }
9068
+ const exec2 = options.execImpl ?? execFile6;
9069
+ for (const gate of spec.gateExecs) {
9070
+ try {
9071
+ await exec2(gate.command, [...gate.args], { timeout: 15e3 });
9072
+ } catch {
9073
+ console.warn(`[build-orchestrator] ${language}: '${gate.command} ${gate.args.join(" ")}' failed \u2014 skipping build`);
9074
+ return { ready: false, reason: "no-toolchain" };
9075
+ }
9076
+ }
9077
+ try {
9078
+ await access3(join38(indexRoot, spec.indexStoreProbe));
9079
+ return { ready: true, built: false };
9080
+ } catch {
9081
+ }
9082
+ const timeoutMs = options.timeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS;
9083
+ const spawnImpl = options.spawnImpl ?? spawn10;
9084
+ const ok = await new Promise((resolve5) => {
9085
+ const child = spawnImpl(spec.command, [...spec.args], {
9086
+ cwd: indexRoot,
9087
+ stdio: ["ignore", "pipe", "pipe"]
9088
+ });
9089
+ const killTimer = setTimeout(() => {
9090
+ console.warn(`[build-orchestrator] ${language}: build exceeded ${(timeoutMs / 1e3).toString()}s \u2014 killing (AST-only)`);
9091
+ child.kill("SIGKILL");
9092
+ }, timeoutMs);
9093
+ const onChunk = () => {
9094
+ options.onActivity?.();
9095
+ };
9096
+ child.stdout?.on("data", onChunk);
9097
+ child.stderr?.on("data", onChunk);
9098
+ child.on("error", () => {
9099
+ clearTimeout(killTimer);
9100
+ resolve5(false);
9101
+ });
9102
+ child.on("close", (code) => {
9103
+ clearTimeout(killTimer);
9104
+ resolve5(code === 0);
9105
+ });
9106
+ });
9107
+ if (!ok) {
9108
+ return { ready: false, reason: "build-failed" };
9109
+ }
9110
+ console.log(`[build-orchestrator] ${language}: cold build complete at ${indexRoot}`);
9111
+ return { ready: true, built: true };
9112
+ }
9113
+
8209
9114
  // ../listener/dist/typed-resolution/resolver-loop.js
8210
9115
  init_src();
8211
9116
  init_registry();
8212
- import { execFile as execFileCb } from "child_process";
8213
- import { promisify as promisify4 } from "util";
9117
+ import { execFile as execFileCb2 } from "child_process";
9118
+ import { promisify as promisify5 } from "util";
8214
9119
 
8215
9120
  // ../listener/dist/typed-resolution/lsp-upgrade-strategy.js
8216
9121
  var goStrategy = {
@@ -8686,13 +9591,13 @@ async function resolveReceiverViaStrategy(session, uri, receiver, repoRoot, opts
8686
9591
  init_sidecar_cache();
8687
9592
  init_sidecar_client();
8688
9593
  var BREAKER_TRIP_THRESHOLD = 3;
8689
- var execFile5 = promisify4(execFileCb);
8690
- async function isCommandOnPath3(command) {
9594
+ var execFile7 = promisify5(execFileCb2);
9595
+ async function isCommandOnPath4(command) {
8691
9596
  if (/[^\w./+-]/.test(command))
8692
9597
  return false;
8693
9598
  const probeCmd = process.platform === "win32" ? "where" : "which";
8694
9599
  try {
8695
- await execFile5(probeCmd, [command], { timeout: 5e3 });
9600
+ await execFile7(probeCmd, [command], { timeout: 5e3 });
8696
9601
  return true;
8697
9602
  } catch {
8698
9603
  return false;
@@ -8700,7 +9605,7 @@ async function isCommandOnPath3(command) {
8700
9605
  }
8701
9606
  async function isWorkingTreeAtCommit(repoRoot, commitSha) {
8702
9607
  try {
8703
- const { stdout } = await execFile5("git", ["-C", repoRoot, "rev-parse", "HEAD"], {
9608
+ const { stdout } = await execFile7("git", ["-C", repoRoot, "rev-parse", "HEAD"], {
8704
9609
  timeout: 5e3
8705
9610
  });
8706
9611
  const head = stdout.trim();
@@ -8733,85 +9638,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
8733
9638
  const breakerOpenEvents = {};
8734
9639
  const pathProbeCache = /* @__PURE__ */ new Map();
8735
9640
  const missingBinaryLanguages = /* @__PURE__ */ new Set();
8736
- const sidecar = await buildSidecar({
8737
- receivers,
8738
- producer: input.producer,
8739
- languages: [],
8740
- // populated below by mutating after observation
8741
- // Item 14 — fan out across languages so pyright + gopls + tsserver
8742
- // run in parallel. The groupKey is the detected language; serial
8743
- // within a language so we don't overwhelm a single LSP server.
8744
- groupKey: (receiver) => detectLanguage(receiver.filePath),
8745
- resolve: async (receiver) => {
8746
- const cached = warmCacheIndex.get(typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column));
8747
- if (cached) {
8748
- warmHits += 1;
8749
- return cached.targetSymbolId;
8750
- }
8751
- const language = detectLanguage(receiver.filePath);
8752
- if (!language)
8753
- return null;
8754
- seenLanguages.add(language);
8755
- if (trippedLanguages.has(language))
8756
- return null;
8757
- if (missingBinaryLanguages.has(language))
8758
- return null;
8759
- const config2 = getEffectiveConfig(language, process.env, input.lspOverrides);
8760
- if (!config2)
8761
- return null;
8762
- let onPath = pathProbeCache.get(language);
8763
- if (onPath === void 0) {
8764
- const probe = input.isOnPath ?? isCommandOnPath3;
8765
- onPath = await probe(config2.command);
8766
- pathProbeCache.set(language, onPath);
8767
- if (!onPath) {
8768
- missingBinaryLanguages.add(language);
8769
- console.warn(`[typed-resolution] ${language}: ${config2.command} not on PATH \u2014 skipping LSP upgrade for this cycle. install: ${config2.installHint}`);
8770
- }
9641
+ const resolveOneReceiver = async (receiver) => {
9642
+ const cached = warmCacheIndex.get(typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column));
9643
+ if (cached) {
9644
+ warmHits += 1;
9645
+ return cached.targetSymbolId;
9646
+ }
9647
+ const language = detectLanguage(receiver.filePath);
9648
+ if (!language)
9649
+ return null;
9650
+ seenLanguages.add(language);
9651
+ if (trippedLanguages.has(language))
9652
+ return null;
9653
+ if (missingBinaryLanguages.has(language))
9654
+ return null;
9655
+ const config2 = getEffectiveConfig(language, process.env, input.lspOverrides);
9656
+ if (!config2)
9657
+ return null;
9658
+ let onPath = pathProbeCache.get(language);
9659
+ if (onPath === void 0) {
9660
+ const probe = input.isOnPath ?? isCommandOnPath4;
9661
+ onPath = await probe(config2.command);
9662
+ pathProbeCache.set(language, onPath);
9663
+ if (!onPath) {
9664
+ missingBinaryLanguages.add(language);
9665
+ console.warn(`[typed-resolution] ${language}: ${config2.command} not on PATH \u2014 skipping LSP upgrade for this cycle. install: ${config2.installHint}`);
8771
9666
  }
8772
- if (!onPath)
8773
- return null;
8774
- try {
8775
- const session = await input.workspaces.getOrOpen({
8776
- repoRoot: input.repoRoot,
8777
- language,
8778
- config: config2
8779
- });
8780
- const uri = await input.workspaces.ensureOpen(session, receiver.filePath);
8781
- let methodErrorsForReceiver = 0;
8782
- const attempt = await resolveReceiverViaStrategy(session, uri, receiver, input.repoRoot, {
8783
- onMethodError: () => {
8784
- methodErrorsForReceiver += 1;
8785
- }
8786
- });
8787
- const hasResult = attempt !== null && (attempt.targetSymbolId !== null || attempt.targets.length > 0);
8788
- if (hasResult) {
8789
- consecutiveFailures.set(language, 0);
8790
- } else if (methodErrorsForReceiver > 0) {
8791
- const next = (consecutiveFailures.get(language) ?? 0) + 1;
8792
- consecutiveFailures.set(language, next);
8793
- if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
8794
- trippedLanguages.add(language);
8795
- breakerOpenEvents[language] = (breakerOpenEvents[language] ?? 0) + 1;
8796
- console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
8797
- }
8798
- return null;
8799
- } else {
8800
- consecutiveFailures.set(language, 0);
8801
- return null;
9667
+ }
9668
+ if (!onPath)
9669
+ return null;
9670
+ try {
9671
+ const session = await input.workspaces.getOrOpen({
9672
+ repoRoot: input.repoRoot,
9673
+ language,
9674
+ config: config2
9675
+ });
9676
+ const uri = await input.workspaces.ensureOpen(session, receiver.filePath);
9677
+ let methodErrorsForReceiver = 0;
9678
+ const attempt = await resolveReceiverViaStrategy(session, uri, receiver, input.repoRoot, {
9679
+ onMethodError: () => {
9680
+ methodErrorsForReceiver += 1;
8802
9681
  }
8803
- const lookupKey = typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column);
8804
- const schemaKind = attempt.kind === "definition" || attempt.kind === "implementation" || attempt.kind === "typeDefinition" || attempt.kind === "workspace-symbol-fallback" ? attempt.kind : void 0;
8805
- telemetryByKey.set(lookupKey, {
8806
- kind: schemaKind,
8807
- lspMethod: attempt.method,
8808
- targets: attempt.targets.length > 0 ? attempt.targets : void 0,
8809
- lspLatencyMs: attempt.lspLatencyMs,
8810
- confidence: attempt.targets.length > 1 ? attempt.baseConfidence / attempt.targets.length : attempt.baseConfidence
8811
- });
8812
- return attempt.targetSymbolId;
8813
- } catch (err) {
8814
- console.warn(`[typed-resolution] LSP query failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
9682
+ });
9683
+ const hasResult = attempt !== null && (attempt.targetSymbolId !== null || attempt.targets.length > 0);
9684
+ if (hasResult) {
9685
+ consecutiveFailures.set(language, 0);
9686
+ } else if (methodErrorsForReceiver > 0) {
8815
9687
  const next = (consecutiveFailures.get(language) ?? 0) + 1;
8816
9688
  consecutiveFailures.set(language, next);
8817
9689
  if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
@@ -8820,7 +9692,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
8820
9692
  console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
8821
9693
  }
8822
9694
  return null;
9695
+ } else {
9696
+ consecutiveFailures.set(language, 0);
9697
+ return null;
9698
+ }
9699
+ const lookupKey = typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column);
9700
+ const schemaKind = attempt.kind === "definition" || attempt.kind === "implementation" || attempt.kind === "typeDefinition" || attempt.kind === "workspace-symbol-fallback" ? attempt.kind : void 0;
9701
+ telemetryByKey.set(lookupKey, {
9702
+ kind: schemaKind,
9703
+ lspMethod: attempt.method,
9704
+ targets: attempt.targets.length > 0 ? attempt.targets : void 0,
9705
+ lspLatencyMs: attempt.lspLatencyMs,
9706
+ confidence: attempt.targets.length > 1 ? attempt.baseConfidence / attempt.targets.length : attempt.baseConfidence
9707
+ });
9708
+ return attempt.targetSymbolId;
9709
+ } catch (err) {
9710
+ console.warn(`[typed-resolution] LSP query failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
9711
+ const next = (consecutiveFailures.get(language) ?? 0) + 1;
9712
+ consecutiveFailures.set(language, next);
9713
+ if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
9714
+ trippedLanguages.add(language);
9715
+ breakerOpenEvents[language] = (breakerOpenEvents[language] ?? 0) + 1;
9716
+ console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
8823
9717
  }
9718
+ return null;
9719
+ }
9720
+ };
9721
+ let processedCount = 0;
9722
+ let resolvedOkCount = 0;
9723
+ const sidecar = await buildSidecar({
9724
+ receivers,
9725
+ producer: input.producer,
9726
+ languages: [],
9727
+ // populated below by mutating after observation
9728
+ // Item 14 — fan out across languages so pyright + gopls + tsserver
9729
+ // run in parallel. The groupKey is the detected language; serial
9730
+ // within a language so we don't overwhelm a single LSP server.
9731
+ groupKey: (receiver) => detectLanguage(receiver.filePath),
9732
+ // Phase 4 — instrument per-receiver progress for the wait-status
9733
+ // reporter (resolved deltas drive the server's stall verdict).
9734
+ resolve: async (receiver) => {
9735
+ const out = await resolveOneReceiver(receiver);
9736
+ processedCount += 1;
9737
+ if (out !== null)
9738
+ resolvedOkCount += 1;
9739
+ input.onReceiverProcessed?.(processedCount, receivers.length, resolvedOkCount);
9740
+ return out;
8824
9741
  }
8825
9742
  });
8826
9743
  const enriched = sidecar.resolutions.map((entry) => {
@@ -8879,6 +9796,234 @@ async function runDownloaderCycle(input, fetchImpl = fetch) {
8879
9796
  return { downloaded: true, edgesUpgraded: result.edgesUpgraded };
8880
9797
  }
8881
9798
 
9799
+ // ../listener/dist/typed-resolution/committed-checkout.js
9800
+ init_config_dir();
9801
+ import { execFile as execFileCb3 } from "child_process";
9802
+ import { createHash as createHash4 } from "crypto";
9803
+ import { mkdir as mkdir15, readdir as readdir5, rm, stat as stat6 } from "fs/promises";
9804
+ import { join as join39 } from "path";
9805
+ import { promisify as promisify6 } from "util";
9806
+ var execFile8 = promisify6(execFileCb3);
9807
+ var GIT_OP_TIMEOUT_MS = 6e4;
9808
+ var MAX_CONCURRENT_WORKTREES = 3;
9809
+ function worktreeBaseDir() {
9810
+ return join39(getConfigDir(), "lsp-worktrees");
9811
+ }
9812
+ async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktreeBaseDir()) {
9813
+ let reaped = 0;
9814
+ let entries;
9815
+ try {
9816
+ entries = await readdir5(base);
9817
+ } catch {
9818
+ return 0;
9819
+ }
9820
+ const cutoff = Date.now() - maxAgeMs;
9821
+ for (const entry of entries) {
9822
+ const dir = join39(base, entry);
9823
+ try {
9824
+ const info = await stat6(dir);
9825
+ if (!info.isDirectory() || info.mtimeMs > cutoff)
9826
+ continue;
9827
+ await rm(dir, { recursive: true, force: true });
9828
+ reaped += 1;
9829
+ } catch {
9830
+ }
9831
+ }
9832
+ if (reaped > 0) {
9833
+ console.log(`[typed-resolution] reaped ${reaped.toString()} orphaned LSP worktree(s)`);
9834
+ }
9835
+ return reaped;
9836
+ }
9837
+ async function prepareCommittedTree(repoRoot, commitSha, options = {}) {
9838
+ const exec2 = options.execImpl ?? execFile8;
9839
+ const git = (args) => exec2("git", args, { timeout: GIT_OP_TIMEOUT_MS });
9840
+ try {
9841
+ const { stdout: headRaw } = await git(["-C", repoRoot, "rev-parse", "HEAD"]);
9842
+ const head = headRaw.trim();
9843
+ const atCommit = head === commitSha || head.startsWith(commitSha) || commitSha.startsWith(head);
9844
+ if (atCommit) {
9845
+ const { stdout: status2 } = await git(["-C", repoRoot, "status", "--porcelain"]);
9846
+ if (status2.trim().length === 0) {
9847
+ return { indexRoot: repoRoot, reused: true, cleanup: () => Promise.resolve() };
9848
+ }
9849
+ }
9850
+ } catch {
9851
+ return null;
9852
+ }
9853
+ const base = options.worktreeBase ?? worktreeBaseDir();
9854
+ const dirName = createHash4("sha256").update(`${repoRoot}\0${commitSha}`).digest("hex").slice(0, 16) + "-" + commitSha.slice(0, 12);
9855
+ const worktreePath = join39(base, dirName);
9856
+ try {
9857
+ await mkdir15(base, { recursive: true });
9858
+ await git(["-C", repoRoot, "worktree", "prune"]).catch(() => void 0);
9859
+ const existing = await readdir5(base).catch(() => []);
9860
+ const max = options.maxConcurrentWorktrees ?? MAX_CONCURRENT_WORKTREES;
9861
+ if (!existing.includes(dirName) && existing.length >= max) {
9862
+ console.warn(`[typed-resolution] worktree budget exceeded (${existing.length.toString()}/${max.toString()}) \u2014 proceeding AST-only`);
9863
+ return null;
9864
+ }
9865
+ if (!existing.includes(dirName)) {
9866
+ await git(["-C", repoRoot, "worktree", "add", "--detach", worktreePath, commitSha]);
9867
+ }
9868
+ const cleanup = async () => {
9869
+ try {
9870
+ await exec2("git", ["-C", repoRoot, "worktree", "remove", "--force", worktreePath], {
9871
+ timeout: GIT_OP_TIMEOUT_MS
9872
+ });
9873
+ } catch {
9874
+ }
9875
+ await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
9876
+ await exec2("git", ["-C", repoRoot, "worktree", "prune"], {
9877
+ timeout: GIT_OP_TIMEOUT_MS
9878
+ }).catch(() => void 0);
9879
+ };
9880
+ return { indexRoot: worktreePath, reused: false, cleanup };
9881
+ } catch (err) {
9882
+ console.warn(`[typed-resolution] committed checkout failed \u2014 proceeding AST-only: ${err instanceof Error ? err.message : String(err)}`);
9883
+ await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
9884
+ return null;
9885
+ }
9886
+ }
9887
+
9888
+ // ../listener/dist/typed-resolution/status-reporter.js
9889
+ var REQUEST_TIMEOUT_MS = 3e4;
9890
+ var DEFAULT_INTERVAL_MS = 6e4;
9891
+ var NO_WAIT_STOP_THRESHOLD = 2;
9892
+ function createStatusReporter(options) {
9893
+ const fetchImpl = options.fetchImpl ?? fetch;
9894
+ const url = `${options.apiUrl.replace(/\/+$/, "")}/v1/listeners/typed-resolution-status`;
9895
+ let timer = null;
9896
+ let pollSeq = 0;
9897
+ let lastResolvedCount = 0;
9898
+ let consecutiveNoWait = 0;
9899
+ let stopped = false;
9900
+ let lastPostFailed = false;
9901
+ async function post(state, snapshot, resolvedDelta) {
9902
+ pollSeq += 1;
9903
+ const body = JSON.stringify({
9904
+ repoId: options.repoId,
9905
+ commitSha: options.commitSha,
9906
+ state,
9907
+ resolvedDelta,
9908
+ remainingReceivers: snapshot.remainingReceivers,
9909
+ indexingActive: snapshot.indexingActive,
9910
+ pollSeq
9911
+ });
9912
+ const attempt = async (token) => {
9913
+ const ctrl = new AbortController();
9914
+ const t = setTimeout(() => {
9915
+ ctrl.abort();
9916
+ }, REQUEST_TIMEOUT_MS);
9917
+ try {
9918
+ return await fetchImpl(url, {
9919
+ method: "POST",
9920
+ headers: {
9921
+ "Content-Type": "application/json",
9922
+ Authorization: `Bearer ${token}`
9923
+ },
9924
+ body,
9925
+ signal: ctrl.signal
9926
+ });
9927
+ } finally {
9928
+ clearTimeout(t);
9929
+ }
9930
+ };
9931
+ lastPostFailed = false;
9932
+ try {
9933
+ let res = await attempt(await options.getAuthToken());
9934
+ if ((res.status === 401 || res.status === 403) && options.getAuthTokenForceRefresh) {
9935
+ res = await attempt(await options.getAuthTokenForceRefresh());
9936
+ }
9937
+ if (!res.ok) {
9938
+ lastPostFailed = true;
9939
+ console.warn(`[typed-resolution] status push failed: HTTP ${res.status.toString()} (server silence window is the backstop)`);
9940
+ return;
9941
+ }
9942
+ const payload = await res.json().catch(() => null);
9943
+ const data = payload?.data;
9944
+ if (data?.accepted === false && data.reason === "disabled") {
9945
+ consecutiveNoWait += 1;
9946
+ if (consecutiveNoWait >= NO_WAIT_STOP_THRESHOLD && timer) {
9947
+ clearInterval(timer);
9948
+ timer = null;
9949
+ }
9950
+ } else {
9951
+ consecutiveNoWait = 0;
9952
+ }
9953
+ } catch (err) {
9954
+ lastPostFailed = true;
9955
+ console.warn(`[typed-resolution] status push error (ignored): ${err instanceof Error ? err.message : String(err)}`);
9956
+ }
9957
+ }
9958
+ function tick() {
9959
+ const snapshot = options.getSnapshot();
9960
+ const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
9961
+ lastResolvedCount = snapshot.resolvedCount;
9962
+ void post(delta > 0 ? "progressing" : "stalled", snapshot, delta);
9963
+ }
9964
+ return {
9965
+ start() {
9966
+ if (timer || stopped)
9967
+ return;
9968
+ tick();
9969
+ timer = setInterval(tick, options.intervalMs ?? DEFAULT_INTERVAL_MS);
9970
+ },
9971
+ async reportCompleted() {
9972
+ this.stop();
9973
+ const snapshot = options.getSnapshot();
9974
+ const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
9975
+ await post("completed", snapshot, delta);
9976
+ if (lastPostFailed)
9977
+ await post("completed", snapshot, delta);
9978
+ },
9979
+ async reportCrashed() {
9980
+ this.stop();
9981
+ await post("crashed", options.getSnapshot(), 0);
9982
+ if (lastPostFailed)
9983
+ await post("crashed", options.getSnapshot(), 0);
9984
+ },
9985
+ stop() {
9986
+ stopped = true;
9987
+ if (timer) {
9988
+ clearInterval(timer);
9989
+ timer = null;
9990
+ }
9991
+ }
9992
+ };
9993
+ }
9994
+
9995
+ // ../listener/dist/typed-resolution/graph-fetch.js
9996
+ var REQUEST_TIMEOUT_MS2 = 3e4;
9997
+ async function fetchProducerGraph(apiUrl, authToken, repoId, fetchImpl = fetch) {
9998
+ const url = `${apiUrl.replace(/\/+$/, "")}/v1/repos/${encodeURIComponent(repoId)}/graph`;
9999
+ const ctrl = new AbortController();
10000
+ const timer = setTimeout(() => {
10001
+ ctrl.abort();
10002
+ }, REQUEST_TIMEOUT_MS2);
10003
+ try {
10004
+ const res = await fetchImpl(url, {
10005
+ headers: { Authorization: `Bearer ${authToken}` },
10006
+ signal: ctrl.signal
10007
+ });
10008
+ if (!res.ok) {
10009
+ console.warn(`[typed-resolution] graph fetch failed: HTTP ${res.status.toString()}`);
10010
+ return null;
10011
+ }
10012
+ const parsed = await res.json();
10013
+ if (typeof parsed !== "object" || parsed === null)
10014
+ return null;
10015
+ const graph = parsed;
10016
+ if (typeof graph.commitSha !== "string" || graph.commitSha.length === 0)
10017
+ return null;
10018
+ return graph;
10019
+ } catch (err) {
10020
+ console.warn(`[typed-resolution] graph fetch error: ${err instanceof Error ? err.message : String(err)}`);
10021
+ return null;
10022
+ } finally {
10023
+ clearTimeout(timer);
10024
+ }
10025
+ }
10026
+
8882
10027
  // ../listener/dist/typed-resolution/hooks.js
8883
10028
  function buildTypedResolutionHooks(options) {
8884
10029
  const producer = options.producer ?? "repowise-listener/0.0.0";
@@ -8921,6 +10066,102 @@ function buildTypedResolutionHooks(options) {
8921
10066
  ...lspOverrides ? { lspOverrides } : {}
8922
10067
  });
8923
10068
  },
10069
+ async onSidecarRequested(repoId, commitSha, localPath) {
10070
+ if (!await hasBuildableManifest(localPath, "swift")) {
10071
+ return;
10072
+ }
10073
+ console.log(`[typed-resolution] sidecar.requested for ${repoId}@${commitSha.slice(0, 12)} \u2014 preparing Swift build`);
10074
+ const prepared = await prepareCommittedTree(localPath, commitSha);
10075
+ if (!prepared)
10076
+ return;
10077
+ const result = await ensureIndexable(prepared.indexRoot, "swift");
10078
+ if (!result.ready) {
10079
+ console.warn(`[typed-resolution] Swift prep for ${repoId} skipped: ${result.reason ?? "unknown"} (AST-only for swift)`);
10080
+ await prepared.cleanup().catch(() => void 0);
10081
+ }
10082
+ },
10083
+ async onGraphReady(repoId, commitSha, localPath) {
10084
+ const apiUrl = options.repoApiUrls.get(repoId);
10085
+ if (!apiUrl)
10086
+ return;
10087
+ const token = await resolveToken();
10088
+ if (!token)
10089
+ return;
10090
+ const graph = await fetchProducerGraph(apiUrl, token, repoId);
10091
+ const shaMatches = graph !== null && (graph.commitSha === commitSha || graph.commitSha.startsWith(commitSha) || commitSha.startsWith(graph.commitSha));
10092
+ if (!graph || !shaMatches) {
10093
+ console.warn(`[typed-resolution] graph.ready for ${repoId}@${commitSha.slice(0, 12)} but fetched graph is ${graph ? `at ${graph.commitSha.slice(0, 12)}` : "missing"} \u2014 skipping`);
10094
+ return;
10095
+ }
10096
+ const receivers = graph.unresolvedReceivers ?? [];
10097
+ const snapshot = {
10098
+ resolvedCount: 0,
10099
+ remainingReceivers: receivers.length,
10100
+ // Until the first receiver completes, session spawn + server
10101
+ // indexing is the activity signal that keeps the wait alive.
10102
+ indexingActive: true
10103
+ };
10104
+ const reporter = createStatusReporter({
10105
+ apiUrl,
10106
+ repoId,
10107
+ commitSha: graph.commitSha,
10108
+ getAuthToken: options.getAuthToken,
10109
+ ...options.getAuthTokenForceRefresh ? { getAuthTokenForceRefresh: options.getAuthTokenForceRefresh } : {},
10110
+ getSnapshot: () => ({ ...snapshot })
10111
+ });
10112
+ reporter.start();
10113
+ let prepared = null;
10114
+ try {
10115
+ if (receivers.length === 0) {
10116
+ await reporter.reportCompleted();
10117
+ return;
10118
+ }
10119
+ prepared = await prepareCommittedTree(localPath, graph.commitSha);
10120
+ if (!prepared) {
10121
+ await reporter.reportCrashed();
10122
+ return;
10123
+ }
10124
+ const buildResult = await ensureIndexable(prepared.indexRoot, "swift", {
10125
+ onActivity: () => {
10126
+ snapshot.indexingActive = true;
10127
+ }
10128
+ });
10129
+ if (!buildResult.ready) {
10130
+ console.warn(`[typed-resolution] swift build unavailable for ${repoId}: ${buildResult.reason ?? "unknown"} \u2014 continuing without it`);
10131
+ }
10132
+ const lspOverrides = options.getLspOverrides?.();
10133
+ const result = await runProducerCycle({
10134
+ apiUrl,
10135
+ authToken: token,
10136
+ repoId,
10137
+ repoRoot: prepared.indexRoot,
10138
+ graph,
10139
+ workspaces: options.workspaces,
10140
+ producer,
10141
+ ...lspOverrides ? { lspOverrides } : {},
10142
+ onReceiverProcessed: (processed, total, resolvedCount) => {
10143
+ snapshot.resolvedCount = resolvedCount;
10144
+ snapshot.remainingReceivers = Math.max(0, total - processed);
10145
+ snapshot.indexingActive = false;
10146
+ }
10147
+ });
10148
+ if (result) {
10149
+ await reporter.reportCompleted();
10150
+ } else {
10151
+ await reporter.reportCrashed();
10152
+ }
10153
+ } catch (err) {
10154
+ console.warn(`[typed-resolution] graph.ready producer failed for ${repoId}: ${err instanceof Error ? err.message : String(err)}`);
10155
+ await reporter.reportCrashed().catch(() => void 0);
10156
+ } finally {
10157
+ reporter.stop();
10158
+ if (prepared) {
10159
+ await prepared.cleanup().catch((cleanupErr) => {
10160
+ console.warn(`[typed-resolution] worktree cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`);
10161
+ });
10162
+ }
10163
+ }
10164
+ },
8924
10165
  async onSidecarMerged(repoId, commitSha) {
8925
10166
  const apiUrl = options.repoApiUrls.get(repoId);
8926
10167
  if (!apiUrl)
@@ -8947,8 +10188,8 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
8947
10188
  var CRASH_LOOP_THRESHOLD = 3;
8948
10189
  async function readRawToolConfig() {
8949
10190
  try {
8950
- const configPath = join32(getConfigDir(), "config.json");
8951
- const data = await readFile12(configPath, "utf-8");
10191
+ const configPath = join40(getConfigDir(), "config.json");
10192
+ const data = await readFile13(configPath, "utf-8");
8952
10193
  const raw = JSON.parse(data);
8953
10194
  return {
8954
10195
  aiTools: Array.isArray(raw["aiTools"]) ? raw["aiTools"] : void 0,
@@ -8967,10 +10208,20 @@ async function updateToolConfigsForRepo(localPath, config2, state, repoId) {
8967
10208
  }
8968
10209
  if (tools.length === 0)
8969
10210
  return;
10211
+ if (tools.includes("roo-code")) {
10212
+ tools = tools.map((t) => t === "roo-code" ? "kilo" : t);
10213
+ tools = tools.filter((t, i) => tools.indexOf(t) === i);
10214
+ }
10215
+ await migrateRooToKilo(localPath).catch(() => {
10216
+ });
8970
10217
  const contextFiles = await scanLocalContextFiles(localPath, contextFolder);
8971
10218
  if (contextFiles.length === 0)
8972
10219
  return;
8973
- const hash = JSON.stringify({ tools, files: contextFiles.map((f) => f.fileName) });
10220
+ const hash = JSON.stringify({
10221
+ rev: AI_TOOLS_REFERENCE_REV,
10222
+ tools,
10223
+ files: contextFiles.map((f) => f.fileName)
10224
+ });
8974
10225
  if (state.repos[repoId]?.lastToolConfigHash === hash)
8975
10226
  return;
8976
10227
  const written = /* @__PURE__ */ new Set();
@@ -9010,8 +10261,8 @@ function resolveAuditRoots() {
9010
10261
  const out = /* @__PURE__ */ new Set();
9011
10262
  let dir = dirname15(process.execPath);
9012
10263
  for (let i = 0; i < 8; i += 1) {
9013
- out.add(join32(dir, "node_modules"));
9014
- out.add(join32(dir, "lib", "node_modules"));
10264
+ out.add(join40(dir, "node_modules"));
10265
+ out.add(join40(dir, "lib", "node_modules"));
9015
10266
  const parent = dirname15(dir);
9016
10267
  if (parent === dir)
9017
10268
  break;
@@ -9141,6 +10392,47 @@ async function runProducerHook(notif, localPath, ctx) {
9141
10392
  console.warn(`[typed-resolution] producer failed for ${notif.repoId}: ${prodErr instanceof Error ? prodErr.message : String(prodErr)}`);
9142
10393
  }
9143
10394
  }
10395
+ var processedSideEffectNotifs = /* @__PURE__ */ new Set();
10396
+ var MAX_PROCESSED_SIDE_EFFECT_NOTIFS = 512;
10397
+ function alreadyProcessedSideEffect(notificationId) {
10398
+ if (processedSideEffectNotifs.has(notificationId))
10399
+ return true;
10400
+ processedSideEffectNotifs.add(notificationId);
10401
+ if (processedSideEffectNotifs.size > MAX_PROCESSED_SIDE_EFFECT_NOTIFS) {
10402
+ const oldest = processedSideEffectNotifs.values().next().value;
10403
+ if (oldest !== void 0)
10404
+ processedSideEffectNotifs.delete(oldest);
10405
+ }
10406
+ return false;
10407
+ }
10408
+ async function handleSidecarRequested(notif, ctx) {
10409
+ if (!ctx.typedResolutionHooks?.onSidecarRequested || !notif.commitSha)
10410
+ return;
10411
+ if (alreadyProcessedSideEffect(notif.notificationId))
10412
+ return;
10413
+ const localPath = ctx.repoLocalPaths.get(notif.repoId);
10414
+ if (!localPath)
10415
+ return;
10416
+ try {
10417
+ await ctx.typedResolutionHooks.onSidecarRequested(notif.repoId, notif.commitSha, localPath);
10418
+ } catch (prepErr) {
10419
+ console.warn(`[typed-resolution] sidecar.requested prep failed for ${notif.repoId}: ${prepErr instanceof Error ? prepErr.message : String(prepErr)}`);
10420
+ }
10421
+ }
10422
+ async function handleGraphReady(notif, ctx) {
10423
+ if (!ctx.typedResolutionHooks?.onGraphReady || !notif.commitSha)
10424
+ return;
10425
+ if (alreadyProcessedSideEffect(notif.notificationId))
10426
+ return;
10427
+ const localPath = ctx.repoLocalPaths.get(notif.repoId);
10428
+ if (!localPath)
10429
+ return;
10430
+ try {
10431
+ await ctx.typedResolutionHooks.onGraphReady(notif.repoId, notif.commitSha, localPath);
10432
+ } catch (resolveErr) {
10433
+ console.warn(`[typed-resolution] graph.ready resolve failed for ${notif.repoId}: ${resolveErr instanceof Error ? resolveErr.message : String(resolveErr)}`);
10434
+ }
10435
+ }
9144
10436
  async function handleSidecarMerged(notif, ctx) {
9145
10437
  if (!ctx.typedResolutionHooks || !notif.commitSha)
9146
10438
  return;
@@ -9171,6 +10463,10 @@ async function processNotifications(notifications, state, repoLocalPaths, apiUrl
9171
10463
  updateCount++;
9172
10464
  } else if (notif.type === "sidecar.merged") {
9173
10465
  await handleSidecarMerged(notif, ctx);
10466
+ } else if (notif.type === "sidecar.requested") {
10467
+ await handleSidecarRequested(notif, ctx);
10468
+ } else if (notif.type === "graph.ready") {
10469
+ await handleGraphReady(notif, ctx);
9174
10470
  } else {
9175
10471
  state.repos[notif.repoId] = {
9176
10472
  ...state.repos[notif.repoId],
@@ -9231,7 +10527,7 @@ async function checkStaleContext(repos, state, groups) {
9231
10527
  if (group?.offline.isOffline)
9232
10528
  continue;
9233
10529
  const { statSync: statSync2, readdirSync: readdirSync2 } = await import("fs");
9234
- const contextPath = join32(repo.localPath, "repowise-context");
10530
+ const contextPath = join40(repo.localPath, "repowise-context");
9235
10531
  let isMissingOrEmpty = false;
9236
10532
  try {
9237
10533
  const s = statSync2(contextPath);
@@ -9262,6 +10558,7 @@ async function checkStaleContext(repos, state, groups) {
9262
10558
  }
9263
10559
  async function reconcileMcpConfigs(repos, packageName) {
9264
10560
  const shimCmd = packageName;
10561
+ const { contextFolder, aiTools } = await readRawToolConfig();
9265
10562
  for (const repo of repos) {
9266
10563
  if (!repo.localPath)
9267
10564
  continue;
@@ -9276,7 +10573,9 @@ async function reconcileMcpConfigs(repos, packageName) {
9276
10573
  const results = await runAutoConfig({
9277
10574
  repoRoot: repo.localPath,
9278
10575
  repoId: repo.repoId,
9279
- shimCmd
10576
+ shimCmd,
10577
+ contextFolder,
10578
+ selectedTools: aiTools
9280
10579
  });
9281
10580
  const written = results.filter((r) => r.detected && r.outcome?.status === "written");
9282
10581
  if (written.length > 0) {
@@ -9289,10 +10588,10 @@ async function reconcileMcpConfigs(repos, packageName) {
9289
10588
  }
9290
10589
  async function reconcileAgentInstructions(repos) {
9291
10590
  for (const repo of repos) {
9292
- const path = join32(repo.localPath, "repowise-context", "project-overview.md");
10591
+ const path = join40(repo.localPath, "repowise-context", "project-overview.md");
9293
10592
  let content;
9294
10593
  try {
9295
- content = await readFile12(path, "utf-8");
10594
+ content = await readFile13(path, "utf-8");
9296
10595
  } catch (err) {
9297
10596
  const code = err?.code;
9298
10597
  if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "EPERM" || code === "EISDIR") {
@@ -9305,7 +10604,7 @@ async function reconcileAgentInstructions(repos) {
9305
10604
  if (injected === content)
9306
10605
  continue;
9307
10606
  try {
9308
- await writeFile14(path, injected, "utf-8");
10607
+ await writeFile15(path, injected, "utf-8");
9309
10608
  console.log(`[reconcile] Reconciled agent instructions for ${repo.repoId}`);
9310
10609
  } catch (err) {
9311
10610
  console.warn(`[reconcile] write failed for ${repo.repoId}:`, err instanceof Error ? err.message : String(err));
@@ -9350,9 +10649,9 @@ async function startListener() {
9350
10649
  console.warn(`[integrity] audit failed: ${err instanceof Error ? err.message : String(err)}`);
9351
10650
  }
9352
10651
  const configDir = getConfigDir();
9353
- await mkdir14(configDir, { recursive: true });
9354
- const lockPath = join32(configDir, "listener.lock");
9355
- await writeFile14(lockPath, "", { flag: "a" });
10652
+ await mkdir16(configDir, { recursive: true });
10653
+ const lockPath = join40(configDir, "listener.lock");
10654
+ await writeFile15(lockPath, "", { flag: "a" });
9356
10655
  let lockIsHeld = false;
9357
10656
  try {
9358
10657
  lockIsHeld = await lockfile4.check(lockPath, { stale: 3e4, realpath: false });
@@ -9394,7 +10693,7 @@ async function startListener() {
9394
10693
  return;
9395
10694
  }
9396
10695
  if (config2.repos.length === 0 && !config2.autoDiscoverRepos) {
9397
- console.error(`No repos configured. Add repos to ${join32(configDir, "config.json")}`);
10696
+ console.error(`No repos configured. Add repos to ${join40(configDir, "config.json")}`);
9398
10697
  await releaseLockAndExit();
9399
10698
  process.exitCode = 1;
9400
10699
  return;
@@ -9462,8 +10761,8 @@ async function startListener() {
9462
10761
  let currentVersion = "";
9463
10762
  try {
9464
10763
  const selfDir = dirname15(fileURLToPath3(import.meta.url));
9465
- const pkgJsonPath = join32(selfDir, "..", "..", "package.json");
9466
- const pkgJson = JSON.parse(await readFile12(pkgJsonPath, "utf-8"));
10764
+ const pkgJsonPath = join40(selfDir, "..", "..", "package.json");
10765
+ const pkgJson = JSON.parse(await readFile13(pkgJsonPath, "utf-8"));
9467
10766
  currentVersion = pkgJson.version;
9468
10767
  } catch (err) {
9469
10768
  console.log(`[auto-update] Version detection failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -9591,6 +10890,9 @@ async function startListener() {
9591
10890
  console.warn("[workspace-prep] Pre-install scan failed:", err instanceof Error ? err.message : String(err));
9592
10891
  }
9593
10892
  })();
10893
+ void reapOrphanWorktrees().catch((reapErr) => {
10894
+ console.warn(`[typed-resolution] worktree reap failed: ${reapErr instanceof Error ? reapErr.message : String(reapErr)}`);
10895
+ });
9594
10896
  const typedResolutionHooks = mcpRuntime?.lspWorkspaces ? buildTypedResolutionHooks({
9595
10897
  workspaces: mcpRuntime.lspWorkspaces,
9596
10898
  graphCache: mcpRuntime.graphCache,
@@ -9887,12 +11189,12 @@ async function startListener() {
9887
11189
  } catch {
9888
11190
  }
9889
11191
  }
9890
- const credentialsPath = join32(getConfigDir(), "credentials.json");
11192
+ const credentialsPath = join40(getConfigDir(), "credentials.json");
9891
11193
  let credentialsChanged = false;
9892
11194
  let watcher = null;
9893
11195
  try {
9894
- const fs25 = await import("fs");
9895
- watcher = fs25.watch(credentialsPath, () => {
11196
+ const fs30 = await import("fs");
11197
+ watcher = fs30.watch(credentialsPath, () => {
9896
11198
  credentialsChanged = true;
9897
11199
  });
9898
11200
  } catch {
@@ -9933,7 +11235,7 @@ if (isDirectRun) {
9933
11235
 
9934
11236
  // src/lib/env.ts
9935
11237
  import { homedir as homedir6 } from "os";
9936
- import { join as join33 } from "path";
11238
+ import { join as join41 } from "path";
9937
11239
  var IS_STAGING2 = true ? true : false;
9938
11240
  var PRODUCTION = {
9939
11241
  apiUrl: "https://api.repowise.ai",
@@ -9953,7 +11255,7 @@ function getEnvConfig() {
9953
11255
  return IS_STAGING2 ? STAGING : PRODUCTION;
9954
11256
  }
9955
11257
  function getConfigDir2() {
9956
- return join33(homedir6(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
11258
+ return join41(homedir6(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
9957
11259
  }
9958
11260
  function getPackageName() {
9959
11261
  return true ? "repowisestage" : "repowise";
@@ -9963,12 +11265,12 @@ function getPackageName() {
9963
11265
  import chalk from "chalk";
9964
11266
 
9965
11267
  // src/lib/config.ts
9966
- import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir15, rename as rename4, unlink as unlink9 } from "fs/promises";
9967
- import { join as join34 } from "path";
11268
+ import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir17, rename as rename4, unlink as unlink10 } from "fs/promises";
11269
+ import { join as join42 } from "path";
9968
11270
  import lockfile5 from "proper-lockfile";
9969
11271
  async function getConfig() {
9970
11272
  try {
9971
- const data = await readFile13(join34(getConfigDir2(), "config.json"), "utf-8");
11273
+ const data = await readFile14(join42(getConfigDir2(), "config.json"), "utf-8");
9972
11274
  return JSON.parse(data);
9973
11275
  } catch {
9974
11276
  return {};
@@ -9976,15 +11278,15 @@ async function getConfig() {
9976
11278
  }
9977
11279
  async function saveConfig(config2) {
9978
11280
  const dir = getConfigDir2();
9979
- const path = join34(dir, "config.json");
9980
- await mkdir15(dir, { recursive: true });
11281
+ const path = join42(dir, "config.json");
11282
+ await mkdir17(dir, { recursive: true });
9981
11283
  const tmpPath = path + ".tmp";
9982
11284
  try {
9983
- await writeFile15(tmpPath, JSON.stringify(config2, null, 2));
11285
+ await writeFile16(tmpPath, JSON.stringify(config2, null, 2));
9984
11286
  await rename4(tmpPath, path);
9985
11287
  } catch (err) {
9986
11288
  try {
9987
- await unlink9(tmpPath);
11289
+ await unlink10(tmpPath);
9988
11290
  } catch {
9989
11291
  }
9990
11292
  throw err;
@@ -9992,10 +11294,10 @@ async function saveConfig(config2) {
9992
11294
  }
9993
11295
  async function mergeAndSaveConfig(updates) {
9994
11296
  const dir = getConfigDir2();
9995
- const path = join34(dir, "config.json");
9996
- await mkdir15(dir, { recursive: true });
11297
+ const path = join42(dir, "config.json");
11298
+ await mkdir17(dir, { recursive: true });
9997
11299
  try {
9998
- await writeFile15(path, "", { flag: "a" });
11300
+ await writeFile16(path, "", { flag: "a" });
9999
11301
  } catch {
10000
11302
  }
10001
11303
  let release = null;
@@ -10003,7 +11305,7 @@ async function mergeAndSaveConfig(updates) {
10003
11305
  release = await lockfile5.lock(path, { stale: 1e4, retries: 3, realpath: false });
10004
11306
  let raw = {};
10005
11307
  try {
10006
- raw = JSON.parse(await readFile13(path, "utf-8"));
11308
+ raw = JSON.parse(await readFile14(path, "utf-8"));
10007
11309
  } catch {
10008
11310
  }
10009
11311
  const merged = { ...raw, ...updates };
@@ -10063,16 +11365,16 @@ async function showWelcome(currentVersion) {
10063
11365
 
10064
11366
  // src/commands/create.ts
10065
11367
  import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
10066
- import { dirname as dirname16, join as join40 } from "path";
11368
+ import { dirname as dirname16, join as join47 } from "path";
10067
11369
  init_src();
10068
11370
  import chalk8 from "chalk";
10069
11371
  import ora from "ora";
10070
11372
 
10071
11373
  // src/lib/auth.ts
10072
- import { createHash as createHash4, randomBytes as randomBytes3 } from "crypto";
10073
- import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir16, chmod as chmod4, unlink as unlink10 } from "fs/promises";
11374
+ import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
11375
+ import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir18, chmod as chmod4, unlink as unlink11 } from "fs/promises";
10074
11376
  import http from "http";
10075
- import { join as join35 } from "path";
11377
+ import { join as join43 } from "path";
10076
11378
  var CLI_CALLBACK_PORT = 19876;
10077
11379
  var CALLBACK_TIMEOUT_MS = 12e4;
10078
11380
  function getCognitoConfigForStorage() {
@@ -10096,7 +11398,7 @@ function generateCodeVerifier() {
10096
11398
  return randomBytes3(32).toString("base64url");
10097
11399
  }
10098
11400
  function generateCodeChallenge(verifier) {
10099
- return createHash4("sha256").update(verifier).digest("base64url");
11401
+ return createHash5("sha256").update(verifier).digest("base64url");
10100
11402
  }
10101
11403
  function generateState() {
10102
11404
  return randomBytes3(32).toString("hex");
@@ -10238,8 +11540,8 @@ async function refreshTokens2(refreshToken) {
10238
11540
  }
10239
11541
  async function getStoredCredentials2() {
10240
11542
  try {
10241
- const credPath = join35(getConfigDir2(), "credentials.json");
10242
- const data = await readFile14(credPath, "utf-8");
11543
+ const credPath = join43(getConfigDir2(), "credentials.json");
11544
+ const data = await readFile15(credPath, "utf-8");
10243
11545
  return JSON.parse(data);
10244
11546
  } catch (err) {
10245
11547
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -10250,14 +11552,14 @@ async function getStoredCredentials2() {
10250
11552
  }
10251
11553
  async function storeCredentials2(credentials) {
10252
11554
  const dir = getConfigDir2();
10253
- const credPath = join35(dir, "credentials.json");
10254
- await mkdir16(dir, { recursive: true, mode: 448 });
10255
- await writeFile16(credPath, JSON.stringify(credentials, null, 2));
11555
+ const credPath = join43(dir, "credentials.json");
11556
+ await mkdir18(dir, { recursive: true, mode: 448 });
11557
+ await writeFile17(credPath, JSON.stringify(credentials, null, 2));
10256
11558
  await chmod4(credPath, 384);
10257
11559
  }
10258
11560
  async function clearCredentials() {
10259
11561
  try {
10260
- await unlink10(join35(getConfigDir2(), "credentials.json"));
11562
+ await unlink11(join43(getConfigDir2(), "credentials.json"));
10261
11563
  } catch (err) {
10262
11564
  if (err.code !== "ENOENT") throw err;
10263
11565
  }
@@ -10432,7 +11734,8 @@ async function selectAiTools() {
10432
11734
  new Separator(chalk2.dim("\u2500\u2500 More Tools \u2500\u2500")),
10433
11735
  { name: "Cline", value: "cline" },
10434
11736
  { name: "Codex", value: "codex" },
10435
- { name: "Roo Code", value: "roo-code" },
11737
+ // Roo Code shut down 2026-05-15; Kilo Code is the community successor.
11738
+ { name: "Kilo Code", value: "kilo" },
10436
11739
  { name: "Gemini CLI", value: "gemini" },
10437
11740
  new Separator(chalk2.dim("\u2500\u2500 Cloud Agents \u2500\u2500")),
10438
11741
  { name: "Warp", value: "warp" },
@@ -10462,55 +11765,11 @@ async function selectAiTools() {
10462
11765
  }
10463
11766
  }
10464
11767
 
10465
- // src/lib/ai-tools.ts
10466
- import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir17 } from "fs/promises";
10467
- import { join as join36 } from "path";
10468
- var REPOWISE_HOOK_MARKER = "repowise-context";
10469
- async function writeClaudeSubagentHook(repoRoot, contextFolder) {
10470
- const settingsPath = join36(repoRoot, ".claude", "settings.json");
10471
- let settings = {};
10472
- try {
10473
- const raw = await readFile15(settingsPath, "utf-8");
10474
- settings = JSON.parse(raw);
10475
- } catch {
10476
- }
10477
- if (!settings["hooks"] || typeof settings["hooks"] !== "object") {
10478
- settings["hooks"] = {};
10479
- }
10480
- const hooks = settings["hooks"];
10481
- const repoWiseHook = {
10482
- matcher: "",
10483
- hooks: [
10484
- {
10485
- type: "command",
10486
- command: `echo '{"additionalContext":"IMPORTANT: Read ${contextFolder}/project-overview.md before performing any work. This file maps every context file to its domain."}'`
10487
- }
10488
- ]
10489
- };
10490
- const subagentStart = Array.isArray(hooks["SubagentStart"]) ? hooks["SubagentStart"] : [];
10491
- const existingIdx = subagentStart.findIndex((entry) => {
10492
- const entryHooks = entry["hooks"];
10493
- return entryHooks?.some((h) => {
10494
- const cmd = h["command"];
10495
- return typeof cmd === "string" && cmd.includes(REPOWISE_HOOK_MARKER);
10496
- });
10497
- });
10498
- if (existingIdx >= 0) {
10499
- subagentStart[existingIdx] = repoWiseHook;
10500
- } else {
10501
- subagentStart.push(repoWiseHook);
10502
- }
10503
- hooks["SubagentStart"] = subagentStart;
10504
- settings["hooks"] = hooks;
10505
- await mkdir17(join36(repoRoot, ".claude"), { recursive: true });
10506
- await writeFile17(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
10507
- }
10508
-
10509
11768
  // src/lib/gitignore.ts
10510
11769
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
10511
- import { join as join37 } from "path";
11770
+ import { join as join44 } from "path";
10512
11771
  function ensureGitignore(repoRoot, entry) {
10513
- const gitignorePath = join37(repoRoot, ".gitignore");
11772
+ const gitignorePath = join44(repoRoot, ".gitignore");
10514
11773
  if (existsSync2(gitignorePath)) {
10515
11774
  const content = readFileSync2(gitignorePath, "utf-8");
10516
11775
  const lines = content.split("\n").map((l) => l.trim());
@@ -11076,8 +12335,8 @@ async function promptDepInstallConsent(missing, opts) {
11076
12335
  import chalk6 from "chalk";
11077
12336
 
11078
12337
  // src/lib/dep-installer.ts
11079
- import { promises as fs16 } from "fs";
11080
- import { join as join38 } from "path";
12338
+ import { promises as fs21 } from "fs";
12339
+ import { join as join45 } from "path";
11081
12340
  var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
11082
12341
  "typescript",
11083
12342
  "javascript",
@@ -11087,26 +12346,26 @@ var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
11087
12346
  ]);
11088
12347
  var exists = async (p) => {
11089
12348
  try {
11090
- await fs16.access(p);
12349
+ await fs21.access(p);
11091
12350
  return true;
11092
12351
  } catch {
11093
12352
  return false;
11094
12353
  }
11095
12354
  };
11096
- async function fileExists11(repoRoot, name) {
11097
- return exists(join38(repoRoot, name));
12355
+ async function fileExists16(repoRoot, name) {
12356
+ return exists(join45(repoRoot, name));
11098
12357
  }
11099
12358
  async function detectNodePackageManager(repoRoot) {
11100
- if (await fileExists11(repoRoot, "pnpm-lock.yaml")) {
12359
+ if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
11101
12360
  return { ecosystem: "pnpm", command: ["pnpm", "install", "--frozen-lockfile"] };
11102
12361
  }
11103
- if (await fileExists11(repoRoot, "yarn.lock")) {
12362
+ if (await fileExists16(repoRoot, "yarn.lock")) {
11104
12363
  return { ecosystem: "yarn", command: ["yarn", "install", "--frozen-lockfile"] };
11105
12364
  }
11106
- if (await fileExists11(repoRoot, "bun.lock") || await fileExists11(repoRoot, "bun.lockb")) {
12365
+ if (await fileExists16(repoRoot, "bun.lock") || await fileExists16(repoRoot, "bun.lockb")) {
11107
12366
  return { ecosystem: "bun", command: ["bun", "install", "--frozen-lockfile"] };
11108
12367
  }
11109
- if (await fileExists11(repoRoot, "package-lock.json")) {
12368
+ if (await fileExists16(repoRoot, "package-lock.json")) {
11110
12369
  return { ecosystem: "npm", command: ["npm", "ci"] };
11111
12370
  }
11112
12371
  return { ecosystem: "npm", command: ["npm", "install", "--no-audit", "--no-fund"] };
@@ -11114,8 +12373,8 @@ async function detectNodePackageManager(repoRoot) {
11114
12373
  async function detectMissingDeps(repoRoot, scopedLanguages) {
11115
12374
  const missing = [];
11116
12375
  const wantsNode = scopedLanguages.has("typescript") || scopedLanguages.has("javascript");
11117
- if (wantsNode && await fileExists11(repoRoot, "package.json")) {
11118
- const nodeModulesComplete = await fileExists11(repoRoot, "node_modules/.package-lock.json") || await fileExists11(repoRoot, "node_modules/.modules.yaml") || await fileExists11(repoRoot, "node_modules/.yarn-integrity") || await fileExists11(repoRoot, "node_modules/.bun-tag");
12376
+ if (wantsNode && await fileExists16(repoRoot, "package.json")) {
12377
+ const nodeModulesComplete = await fileExists16(repoRoot, "node_modules/.package-lock.json") || await fileExists16(repoRoot, "node_modules/.modules.yaml") || await fileExists16(repoRoot, "node_modules/.yarn-integrity") || await fileExists16(repoRoot, "node_modules/.bun-tag");
11119
12378
  if (!nodeModulesComplete) {
11120
12379
  const pm = await detectNodePackageManager(repoRoot);
11121
12380
  missing.push({
@@ -11128,9 +12387,9 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11128
12387
  }
11129
12388
  }
11130
12389
  if (scopedLanguages.has("python")) {
11131
- if (await fileExists11(repoRoot, "requirements.txt")) {
12390
+ if (await fileExists16(repoRoot, "requirements.txt")) {
11132
12391
  const venvPipPath = process.platform === "win32" ? "Scripts/pip.exe" : "bin/pip";
11133
- const venvPresent = await fileExists11(repoRoot, `.venv/${venvPipPath}`) || await fileExists11(repoRoot, `venv/${venvPipPath}`);
12392
+ const venvPresent = await fileExists16(repoRoot, `.venv/${venvPipPath}`) || await fileExists16(repoRoot, `venv/${venvPipPath}`);
11134
12393
  if (!venvPresent) {
11135
12394
  missing.push({
11136
12395
  kind: "pip-venv",
@@ -11142,7 +12401,7 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11142
12401
  }
11143
12402
  }
11144
12403
  if (scopedLanguages.has("go")) {
11145
- if (await fileExists11(repoRoot, "go.mod")) {
12404
+ if (await fileExists16(repoRoot, "go.mod")) {
11146
12405
  missing.push({
11147
12406
  kind: "simple",
11148
12407
  ecosystem: "go",
@@ -11153,8 +12412,8 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11153
12412
  }
11154
12413
  }
11155
12414
  if (scopedLanguages.has("php")) {
11156
- if (await fileExists11(repoRoot, "composer.json")) {
11157
- if (!await fileExists11(repoRoot, "vendor")) {
12415
+ if (await fileExists16(repoRoot, "composer.json")) {
12416
+ if (!await fileExists16(repoRoot, "vendor")) {
11158
12417
  missing.push({
11159
12418
  kind: "simple",
11160
12419
  ecosystem: "composer",
@@ -11169,15 +12428,15 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11169
12428
  }
11170
12429
 
11171
12430
  // src/lib/dep-installer-runner.ts
11172
- import { spawn as spawn10 } from "child_process";
12431
+ import { spawn as spawn11 } from "child_process";
11173
12432
  import { createWriteStream as createWriteStream3 } from "fs";
11174
- import { promises as fs17 } from "fs";
11175
- import { join as join39 } from "path";
12433
+ import { promises as fs22 } from "fs";
12434
+ import { join as join46 } from "path";
11176
12435
  var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
11177
12436
  async function runMissingDepInstalls(opts) {
11178
12437
  const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
11179
- const logPath2 = join39(getConfigDir2(), `install-log.${safeRepoId}.txt`);
11180
- await fs17.mkdir(getConfigDir2(), { recursive: true });
12438
+ const logPath2 = join46(getConfigDir2(), `install-log.${safeRepoId}.txt`);
12439
+ await fs22.mkdir(getConfigDir2(), { recursive: true });
11181
12440
  const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
11182
12441
  stream.on("error", () => {
11183
12442
  });
@@ -11223,7 +12482,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
11223
12482
  }
11224
12483
  async function runPipFlow(repoRoot, stream, timeoutMs) {
11225
12484
  await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
11226
- const venvPip = process.platform === "win32" ? join39(".venv", "Scripts", "pip.exe") : join39(".venv", "bin", "pip");
12485
+ const venvPip = process.platform === "win32" ? join46(".venv", "Scripts", "pip.exe") : join46(".venv", "bin", "pip");
11227
12486
  await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
11228
12487
  }
11229
12488
  function runSimple(cmd, repoRoot, stream, timeoutMs) {
@@ -11236,7 +12495,7 @@ $ ${cmd.join(" ")}
11236
12495
  reject(new Error("runSimple called with empty command"));
11237
12496
  return;
11238
12497
  }
11239
- const child = spawn10(head, cmd.slice(1), {
12498
+ const child = spawn11(head, cmd.slice(1), {
11240
12499
  cwd: repoRoot,
11241
12500
  stdio: ["ignore", "pipe", "pipe"]
11242
12501
  });
@@ -11591,91 +12850,142 @@ async function create() {
11591
12850
  const progressRenderer = new ProgressRenderer();
11592
12851
  let depInstallShown = false;
11593
12852
  let seenAwaitingInput = false;
12853
+ let lspWaitStartedAt = null;
12854
+ let lspFellBackNoticeShown = false;
11594
12855
  let depInstallPromise = null;
11595
12856
  let lspInstallPromise = null;
11596
12857
  if (repoRoot) {
11597
12858
  const lspResult = await maybeInstallLspServers({ repoRoot, spinner });
11598
12859
  lspInstallPromise = lspResult.installPromise;
11599
12860
  }
11600
- while (true) {
11601
- if (++pollAttempts > MAX_POLL_ATTEMPTS) {
11602
- spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
11603
- process.exitCode = 1;
11604
- return;
11605
- }
11606
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
11607
- let syncResult;
11608
- try {
11609
- syncResult = await apiRequest(`/v1/sync/${syncId}/status`);
11610
- pollErrors = 0;
11611
- } catch (pollErr) {
11612
- pollErrors++;
11613
- if (pollErrors >= MAX_POLL_ERRORS) {
11614
- throw pollErr;
11615
- }
11616
- continue;
11617
- }
11618
- progressRenderer.update(syncResult, spinner);
11619
- if (syncResult.status === "awaiting_input") {
11620
- seenAwaitingInput = true;
11621
- }
11622
- if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
12861
+ let sigintBusy = false;
12862
+ const onSigint = () => {
12863
+ if (sigintBusy) return;
12864
+ sigintBusy = true;
12865
+ void (async () => {
11623
12866
  spinner.stop();
11624
- await handleInterview(
11625
- syncId,
11626
- syncResult.questionId,
11627
- syncResult.questionText,
11628
- syncResult.questionContext ?? void 0,
11629
- syncResult.discoveryResult?.estimatedInterviewQuestions
11630
- );
11631
- spinner.start("Resuming pipeline...");
11632
- continue;
11633
- }
11634
- const interviewPhaseDone = seenAwaitingInput || syncResult.discoveryResult?.estimatedInterviewQuestions === 0;
11635
- if (!depInstallShown && repoRoot && repoId && syncResult.discoveryResult?.languages && syncResult.discoveryResult.languages.length > 0 && syncResult.status !== "awaiting_input" && !syncResult.scanProgress && interviewPhaseDone) {
11636
- depInstallShown = true;
11637
- const scopedLanguages = new Set(
11638
- syncResult.discoveryResult.languages.map((l) => l.name.toLowerCase())
11639
- );
11640
- const result = await maybePromptAndInstallDeps({
11641
- repoRoot,
11642
- repoId,
11643
- scopedLanguages,
11644
- spinner,
11645
- resumeText: "Resuming pipeline..."
11646
- });
11647
- depInstallPromise = result.installPromise;
11648
- }
11649
- if (syncResult.status === "completed") {
11650
- progressRenderer.finalize();
11651
- const generatedFiles = syncResult.filesGenerated ?? [];
11652
- const fileCount = generatedFiles.length;
11653
- if (fileCount > 0) {
11654
- const coreCount = generatedFiles.filter(
11655
- (f) => CORE_FILES.has(f.split("/").pop() ?? f)
11656
- ).length;
11657
- const tailoredCount = fileCount - coreCount;
11658
- spinner.succeed(
11659
- `Context generation complete \u2014 ${coreCount} core + ${tailoredCount} tailored files`
12867
+ try {
12868
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
12869
+ const abort = await confirm3({
12870
+ message: "Scan is still running. Abort it? (No = continue in background)",
12871
+ default: false
12872
+ });
12873
+ if (!abort) {
12874
+ console.log(
12875
+ chalk8.dim(
12876
+ "Scan continues on the server \u2014 watch progress in the dashboard; context arrives via the listener."
12877
+ )
12878
+ );
12879
+ process.exit(0);
12880
+ }
12881
+ await apiRequest(`/v1/sync/${syncId}/cancel`, { method: "POST" });
12882
+ console.log(chalk8.yellow("Scan aborted."));
12883
+ process.exit(1);
12884
+ } catch {
12885
+ process.exit(130);
12886
+ }
12887
+ })();
12888
+ };
12889
+ process.on("SIGINT", onSigint);
12890
+ try {
12891
+ while (true) {
12892
+ if (++pollAttempts > MAX_POLL_ATTEMPTS) {
12893
+ spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
12894
+ process.exitCode = 1;
12895
+ return;
12896
+ }
12897
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
12898
+ let syncResult;
12899
+ try {
12900
+ syncResult = await apiRequest(`/v1/sync/${syncId}/status`);
12901
+ pollErrors = 0;
12902
+ } catch (pollErr) {
12903
+ pollErrors++;
12904
+ if (pollErrors >= MAX_POLL_ERRORS) {
12905
+ throw pollErr;
12906
+ }
12907
+ continue;
12908
+ }
12909
+ if (sigintBusy) {
12910
+ continue;
12911
+ }
12912
+ progressRenderer.update(syncResult, spinner);
12913
+ if (!graphOnly && syncResult.status === "in_progress") {
12914
+ if (syncResult.lspWaitState === "awaiting" || syncResult.lspWaitState === "analyzing") {
12915
+ lspWaitStartedAt ??= Date.now();
12916
+ spinner.text = `Analyzing repo for accurate dependency graph\u2026 (${formatElapsed(Date.now() - lspWaitStartedAt)})`;
12917
+ } else if (syncResult.lspWaitState === "fell-back" && !lspFellBackNoticeShown) {
12918
+ lspFellBackNoticeShown = true;
12919
+ spinner.stop();
12920
+ console.log(
12921
+ chalk8.dim(" Typed analysis unavailable \u2014 using fast static analysis for this scan.")
12922
+ );
12923
+ spinner.start();
12924
+ }
12925
+ }
12926
+ if (syncResult.status === "awaiting_input") {
12927
+ seenAwaitingInput = true;
12928
+ }
12929
+ if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
12930
+ spinner.stop();
12931
+ await handleInterview(
12932
+ syncId,
12933
+ syncResult.questionId,
12934
+ syncResult.questionText,
12935
+ syncResult.questionContext ?? void 0,
12936
+ syncResult.discoveryResult?.estimatedInterviewQuestions
11660
12937
  );
11661
- } else if (graphOnly) {
11662
- spinner.succeed("Knowledge graph built \u2014 MCP ready (Free plan).");
11663
- } else {
11664
- spinner.warn(chalk8.yellow("Pipeline completed but no context files were generated."));
11665
- console.log(
11666
- chalk8.yellow(
11667
- " This may be due to AI throttling or a parsing issue. Try running `repowise create` again."
11668
- )
12938
+ spinner.start("Resuming pipeline...");
12939
+ continue;
12940
+ }
12941
+ const interviewPhaseDone = seenAwaitingInput || syncResult.discoveryResult?.estimatedInterviewQuestions === 0;
12942
+ if (!depInstallShown && repoRoot && repoId && syncResult.discoveryResult?.languages && syncResult.discoveryResult.languages.length > 0 && syncResult.status !== "awaiting_input" && interviewPhaseDone) {
12943
+ depInstallShown = true;
12944
+ const scopedLanguages = new Set(
12945
+ syncResult.discoveryResult.languages.map((l) => l.name.toLowerCase())
11669
12946
  );
12947
+ const result = await maybePromptAndInstallDeps({
12948
+ repoRoot,
12949
+ repoId,
12950
+ scopedLanguages,
12951
+ spinner,
12952
+ resumeText: "Resuming pipeline..."
12953
+ });
12954
+ depInstallPromise = result.installPromise;
12955
+ }
12956
+ if (syncResult.status === "completed") {
12957
+ progressRenderer.finalize();
12958
+ const generatedFiles = syncResult.filesGenerated ?? [];
12959
+ const fileCount = generatedFiles.length;
12960
+ if (fileCount > 0) {
12961
+ const coreCount = generatedFiles.filter(
12962
+ (f) => CORE_FILES.has(f.split("/").pop() ?? f)
12963
+ ).length;
12964
+ const tailoredCount = fileCount - coreCount;
12965
+ spinner.succeed(
12966
+ `Context generation complete \u2014 ${coreCount} core + ${tailoredCount} tailored files`
12967
+ );
12968
+ } else if (graphOnly) {
12969
+ spinner.succeed("Knowledge graph built \u2014 MCP ready (Free plan).");
12970
+ } else {
12971
+ spinner.warn(chalk8.yellow("Pipeline completed but no context files were generated."));
12972
+ console.log(
12973
+ chalk8.yellow(
12974
+ " This may be due to AI throttling or a parsing issue. Try running `repowise create` again."
12975
+ )
12976
+ );
12977
+ }
12978
+ break;
12979
+ }
12980
+ if (syncResult.status === "failed") {
12981
+ progressRenderer.finalize();
12982
+ spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
12983
+ process.exitCode = 1;
12984
+ return;
11670
12985
  }
11671
- break;
11672
- }
11673
- if (syncResult.status === "failed") {
11674
- progressRenderer.finalize();
11675
- spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
11676
- process.exitCode = 1;
11677
- return;
11678
12986
  }
12987
+ } finally {
12988
+ process.removeListener("SIGINT", onSigint);
11679
12989
  }
11680
12990
  if (repoRoot && !graphOnly) {
11681
12991
  spinner.start("Downloading context files from server...");
@@ -11683,7 +12993,7 @@ async function create() {
11683
12993
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
11684
12994
  const files = listResult.data?.files ?? listResult.files ?? [];
11685
12995
  if (files.length > 0) {
11686
- const contextDir = join40(repoRoot, DEFAULT_CONTEXT_FOLDER);
12996
+ const contextDir = join47(repoRoot, DEFAULT_CONTEXT_FOLDER);
11687
12997
  mkdirSync(contextDir, { recursive: true });
11688
12998
  let downloadedCount = 0;
11689
12999
  let failedCount = 0;
@@ -11697,7 +13007,7 @@ async function create() {
11697
13007
  const response = await fetch(presignedUrl);
11698
13008
  if (response.ok) {
11699
13009
  const content = await response.text();
11700
- const filePath = join40(contextDir, file.fileName);
13010
+ const filePath = join47(contextDir, file.fileName);
11701
13011
  mkdirSync(dirname16(filePath), { recursive: true });
11702
13012
  writeFileSync2(filePath, content, "utf-8");
11703
13013
  downloadedCount++;
@@ -11773,8 +13083,8 @@ Files are stored on our servers (not in git). Retry when online.`
11773
13083
  results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
11774
13084
  }
11775
13085
  if (tools.includes("claude-code")) {
11776
- await writeClaudeSubagentHook(repoRoot, contextFolder);
11777
- results.push(" Configured .claude/settings.json (SubagentStart hook)");
13086
+ const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder);
13087
+ results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
11778
13088
  }
11779
13089
  spinner.succeed("AI tools configured");
11780
13090
  console.log(chalk8.dim(results.join("\n")));
@@ -11900,7 +13210,7 @@ Files are stored on our servers (not in git). Retry when online.`
11900
13210
 
11901
13211
  // src/commands/member.ts
11902
13212
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
11903
- import { dirname as dirname17, join as join41, resolve, sep } from "path";
13213
+ import { dirname as dirname17, join as join48, resolve, sep } from "path";
11904
13214
  import chalk9 from "chalk";
11905
13215
  import ora2 from "ora";
11906
13216
  var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
@@ -12054,7 +13364,7 @@ async function member() {
12054
13364
  spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
12055
13365
  const { tools } = await selectAiTools();
12056
13366
  spinner.start("Downloading context files...");
12057
- const contextDir = join41(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13367
+ const contextDir = join48(repoRoot, DEFAULT_CONTEXT_FOLDER2);
12058
13368
  mkdirSync2(contextDir, { recursive: true });
12059
13369
  let downloadedCount = 0;
12060
13370
  let failedCount = 0;
@@ -12125,8 +13435,8 @@ async function member() {
12125
13435
  configured.push(`${created ? "Created" : "Updated"} AGENTS.md`);
12126
13436
  }
12127
13437
  if (tools.includes("claude-code")) {
12128
- await writeClaudeSubagentHook(repoRoot, DEFAULT_CONTEXT_FOLDER2);
12129
- configured.push("Configured .claude/settings.json (SubagentStart hook)");
13438
+ const { relPath } = await writeClaudeHooksToRepo(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13439
+ configured.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
12130
13440
  }
12131
13441
  spinner.succeed("AI tools configured");
12132
13442
  for (const msg of configured) {
@@ -12252,15 +13562,15 @@ import chalk10 from "chalk";
12252
13562
  import ora3 from "ora";
12253
13563
 
12254
13564
  // src/lib/tenant-graph-purge.ts
12255
- import { promises as fs18 } from "fs";
13565
+ import { promises as fs23 } from "fs";
12256
13566
  import { homedir as homedir7 } from "os";
12257
- import { join as join42 } from "path";
13567
+ import { join as join49 } from "path";
12258
13568
  async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
12259
- const graphsDir = join42(home, ".repowise", "graphs");
13569
+ const graphsDir = join49(home, ".repowise", "graphs");
12260
13570
  const result = { kept: [], removed: [] };
12261
13571
  let entries;
12262
13572
  try {
12263
- entries = await fs18.readdir(graphsDir);
13573
+ entries = await fs23.readdir(graphsDir);
12264
13574
  } catch (err) {
12265
13575
  if (err.code === "ENOENT") return result;
12266
13576
  throw err;
@@ -12274,15 +13584,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
12274
13584
  result.kept.push(entry);
12275
13585
  continue;
12276
13586
  }
12277
- const path = join42(graphsDir, entry);
13587
+ const path = join49(graphsDir, entry);
12278
13588
  try {
12279
- const stat7 = await fs18.lstat(path);
12280
- if (stat7.isSymbolicLink()) {
12281
- await fs18.unlink(path);
13589
+ const stat8 = await fs23.lstat(path);
13590
+ if (stat8.isSymbolicLink()) {
13591
+ await fs23.unlink(path);
12282
13592
  result.removed.push(entry);
12283
13593
  continue;
12284
13594
  }
12285
- await fs18.rm(path, { recursive: true, force: true });
13595
+ await fs23.rm(path, { recursive: true, force: true });
12286
13596
  result.removed.push(entry);
12287
13597
  } catch {
12288
13598
  }
@@ -12367,11 +13677,11 @@ async function logout() {
12367
13677
 
12368
13678
  // src/commands/status.ts
12369
13679
  import { readFile as readFile16 } from "fs/promises";
12370
- import { basename as basename3, join as join43 } from "path";
13680
+ import { basename as basename4, join as join50 } from "path";
12371
13681
  async function status() {
12372
13682
  const configDir = getConfigDir2();
12373
- const STATE_PATH = join43(configDir, "listener-state.json");
12374
- const CONFIG_PATH = join43(configDir, "config.json");
13683
+ const STATE_PATH = join50(configDir, "listener-state.json");
13684
+ const CONFIG_PATH = join50(configDir, "config.json");
12375
13685
  let state = null;
12376
13686
  try {
12377
13687
  const data = await readFile16(STATE_PATH, "utf-8");
@@ -12403,7 +13713,7 @@ async function status() {
12403
13713
  const configData = await readFile16(CONFIG_PATH, "utf-8");
12404
13714
  const config2 = JSON.parse(configData);
12405
13715
  for (const repo of config2.repos ?? []) {
12406
- repoNames.set(repo.repoId, basename3(repo.localPath));
13716
+ repoNames.set(repo.repoId, basename4(repo.localPath));
12407
13717
  }
12408
13718
  } catch {
12409
13719
  }
@@ -12419,7 +13729,7 @@ async function status() {
12419
13729
 
12420
13730
  // src/commands/sync.ts
12421
13731
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
12422
- import { dirname as dirname18, join as join44 } from "path";
13732
+ import { dirname as dirname18, join as join51 } from "path";
12423
13733
  import chalk12 from "chalk";
12424
13734
  import ora4 from "ora";
12425
13735
  var POLL_INTERVAL_MS2 = 3e3;
@@ -12568,7 +13878,7 @@ async function sync() {
12568
13878
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
12569
13879
  const files = listResult.data?.files ?? listResult.files ?? [];
12570
13880
  if (files.length > 0) {
12571
- const contextDir = join44(repoRoot, DEFAULT_CONTEXT_FOLDER3);
13881
+ const contextDir = join51(repoRoot, DEFAULT_CONTEXT_FOLDER3);
12572
13882
  mkdirSync3(contextDir, { recursive: true });
12573
13883
  let downloadedCount = 0;
12574
13884
  let failedCount = 0;
@@ -12582,7 +13892,7 @@ async function sync() {
12582
13892
  const response = await fetch(presignedUrl);
12583
13893
  if (response.ok) {
12584
13894
  const content = await response.text();
12585
- const filePath = join44(contextDir, file.fileName);
13895
+ const filePath = join51(contextDir, file.fileName);
12586
13896
  mkdirSync3(dirname18(filePath), { recursive: true });
12587
13897
  writeFileSync4(filePath, content, "utf-8");
12588
13898
  downloadedCount++;
@@ -12603,7 +13913,7 @@ async function sync() {
12603
13913
  }
12604
13914
  try {
12605
13915
  const existingConfig = await getConfig();
12606
- const aiTools = existingConfig.aiTools ?? [];
13916
+ const aiTools = (existingConfig.aiTools ?? []).map((t) => t === "roo-code" ? "kilo" : t).filter((t, i, arr) => arr.indexOf(t) === i);
12607
13917
  if (aiTools.length > 0) {
12608
13918
  const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER3);
12609
13919
  if (contextFiles.length > 0) {
@@ -12834,8 +14144,8 @@ async function config() {
12834
14144
 
12835
14145
  // src/commands/mcp-log.ts
12836
14146
  import { createDecipheriv as createDecipheriv2 } from "crypto";
12837
- import { mkdir as mkdir18, readFile as readFile17, stat as stat6, writeFile as writeFile18 } from "fs/promises";
12838
- import { dirname as dirname19, join as join45 } from "path";
14147
+ import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
14148
+ import { dirname as dirname19, join as join52 } from "path";
12839
14149
  var FLAG_FILE = "mcp-log.flag";
12840
14150
  var LOG_FILE = "mcp-log.jsonl.enc";
12841
14151
  var KEY_FILE = "mcp-log.key";
@@ -12843,14 +14153,14 @@ var ENDPOINT_FILE = "listener.endpoint";
12843
14153
  var IV_BYTES2 = 12;
12844
14154
  var TAG_BYTES2 = 16;
12845
14155
  function flagPath() {
12846
- return join45(getConfigDir2(), FLAG_FILE);
14156
+ return join52(getConfigDir2(), FLAG_FILE);
12847
14157
  }
12848
14158
  function logPath() {
12849
- return join45(getConfigDir2(), LOG_FILE);
14159
+ return join52(getConfigDir2(), LOG_FILE);
12850
14160
  }
12851
14161
  async function writeFlag(flag) {
12852
14162
  const path = flagPath();
12853
- await mkdir18(dirname19(path), { recursive: true });
14163
+ await mkdir19(dirname19(path), { recursive: true });
12854
14164
  await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
12855
14165
  }
12856
14166
  async function mcpLogOn() {
@@ -12887,14 +14197,14 @@ async function trySendConsentToServer() {
12887
14197
  let apiUrl = null;
12888
14198
  let token = null;
12889
14199
  try {
12890
- const body = await readFile17(join45(getConfigDir2(), "config.json"), "utf-8");
14200
+ const body = await readFile17(join52(getConfigDir2(), "config.json"), "utf-8");
12891
14201
  const parsed = JSON.parse(body);
12892
14202
  apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
12893
14203
  } catch {
12894
14204
  return false;
12895
14205
  }
12896
14206
  try {
12897
- const body = await readFile17(join45(getConfigDir2(), "credentials.json"), "utf-8");
14207
+ const body = await readFile17(join52(getConfigDir2(), "credentials.json"), "utf-8");
12898
14208
  const parsed = JSON.parse(body);
12899
14209
  token = parsed.idToken ?? null;
12900
14210
  } catch {
@@ -12955,7 +14265,7 @@ async function mcpLogStatus() {
12955
14265
  process.stderr.write(`Log file: ${logPath()}
12956
14266
  `);
12957
14267
  try {
12958
- const s = await stat6(logPath());
14268
+ const s = await stat7(logPath());
12959
14269
  process.stderr.write(
12960
14270
  `Log size: ${formatBytes(s.size)} (last modified ${s.mtime.toISOString()})
12961
14271
  `
@@ -12964,7 +14274,7 @@ async function mcpLogStatus() {
12964
14274
  process.stderr.write("Log size: no file yet\n");
12965
14275
  }
12966
14276
  try {
12967
- const endpointBody = await readFile17(join45(getConfigDir2(), ENDPOINT_FILE), "utf-8");
14277
+ const endpointBody = await readFile17(join52(getConfigDir2(), ENDPOINT_FILE), "utf-8");
12968
14278
  const match = /endpoint=([^\n]+)/.exec(endpointBody);
12969
14279
  process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
12970
14280
  `);
@@ -12995,7 +14305,7 @@ async function mcpLogViewingFlags(flags = {}) {
12995
14305
  const key = await readKey();
12996
14306
  if (!key) {
12997
14307
  process.stderr.write(
12998
- `No encryption key at ${join45(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
14308
+ `No encryption key at ${join52(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
12999
14309
  `
13000
14310
  );
13001
14311
  return;
@@ -13041,11 +14351,11 @@ async function mcpLogViewingFlags(flags = {}) {
13041
14351
  }
13042
14352
  }
13043
14353
  if (flags.follow) {
13044
- let lastSize = (await stat6(path)).size;
14354
+ let lastSize = (await stat7(path)).size;
13045
14355
  while (true) {
13046
14356
  await new Promise((resolve5) => setTimeout(resolve5, 1e3));
13047
14357
  try {
13048
- const s = await stat6(path);
14358
+ const s = await stat7(path);
13049
14359
  if (s.size > lastSize) {
13050
14360
  const fresh = await readFile17(path, "utf-8");
13051
14361
  const tail = fresh.slice(lastSize);
@@ -13071,7 +14381,7 @@ async function mcpLogViewingFlags(flags = {}) {
13071
14381
  }
13072
14382
  async function readKey() {
13073
14383
  try {
13074
- const body = await readFile17(join45(getConfigDir2(), KEY_FILE), "utf-8");
14384
+ const body = await readFile17(join52(getConfigDir2(), KEY_FILE), "utf-8");
13075
14385
  const parsed = Buffer.from(body.trim(), "base64");
13076
14386
  if (parsed.length !== 32) return null;
13077
14387
  return parsed;
@@ -13114,8 +14424,8 @@ async function mcpLog(subcommand, flags = {}) {
13114
14424
  import chalk14 from "chalk";
13115
14425
 
13116
14426
  // src/lib/graph-loader.ts
13117
- import { promises as fs19 } from "fs";
13118
- import { join as join46, resolve as resolve2 } from "path";
14427
+ import { promises as fs24 } from "fs";
14428
+ import { join as join53, resolve as resolve2 } from "path";
13119
14429
  import { gunzipSync } from "zlib";
13120
14430
  var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
13121
14431
  var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
@@ -13132,8 +14442,8 @@ var GraphNotFoundError = class extends Error {
13132
14442
  var cache = /* @__PURE__ */ new Map();
13133
14443
  async function loadGraph(repoRoot = process.cwd()) {
13134
14444
  const root = resolve2(repoRoot);
13135
- const gzPath = join46(root, GZIPPED_GRAPH_PATH);
13136
- const plainPath = join46(root, RELATIVE_GRAPH_PATH);
14445
+ const gzPath = join53(root, GZIPPED_GRAPH_PATH);
14446
+ const plainPath = join53(root, RELATIVE_GRAPH_PATH);
13137
14447
  const cached = cache.get(root);
13138
14448
  if (cached) {
13139
14449
  return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
@@ -13141,14 +14451,14 @@ async function loadGraph(repoRoot = process.cwd()) {
13141
14451
  let graphPath = null;
13142
14452
  let raw = null;
13143
14453
  try {
13144
- raw = await fs19.readFile(gzPath);
14454
+ raw = await fs24.readFile(gzPath);
13145
14455
  graphPath = gzPath;
13146
14456
  } catch (err) {
13147
14457
  if (err.code !== "ENOENT") throw err;
13148
14458
  }
13149
14459
  if (!raw) {
13150
14460
  try {
13151
- raw = await fs19.readFile(plainPath);
14461
+ raw = await fs24.readFile(plainPath);
13152
14462
  graphPath = plainPath;
13153
14463
  } catch (err) {
13154
14464
  if (err.code === "ENOENT") {
@@ -13547,14 +14857,14 @@ function registerQueryCommand(program2) {
13547
14857
  }
13548
14858
 
13549
14859
  // src/commands/uninstall.ts
13550
- import { promises as fs23 } from "fs";
14860
+ import { promises as fs28 } from "fs";
13551
14861
  import { homedir as homedir9 } from "os";
13552
- import { join as join50 } from "path";
14862
+ import { join as join57 } from "path";
13553
14863
  import chalk15 from "chalk";
13554
14864
 
13555
14865
  // src/lib/cleanup/marker-blocks.ts
13556
- import { promises as fs20 } from "fs";
13557
- import { join as join47 } from "path";
14866
+ import { promises as fs25 } from "fs";
14867
+ import { join as join54 } from "path";
13558
14868
  var MARKER_START = "<!-- repowise-start -->";
13559
14869
  var MARKER_END = "<!-- repowise-end -->";
13560
14870
  var CONTEXT_FILES = [
@@ -13570,7 +14880,7 @@ var CONTEXT_FILES = [
13570
14880
  async function stripMarkerBlock(filePath) {
13571
14881
  let raw;
13572
14882
  try {
13573
- raw = await fs20.readFile(filePath, "utf-8");
14883
+ raw = await fs25.readFile(filePath, "utf-8");
13574
14884
  } catch (err) {
13575
14885
  if (err.code === "ENOENT")
13576
14886
  return { path: filePath, status: "missing" };
@@ -13585,16 +14895,16 @@ async function stripMarkerBlock(filePath) {
13585
14895
  const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
13586
14896
  const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
13587
14897
  if (stripped.length === 0) {
13588
- await fs20.unlink(filePath);
14898
+ await fs25.unlink(filePath);
13589
14899
  return { path: filePath, status: "deleted" };
13590
14900
  }
13591
- await fs20.writeFile(filePath, stripped + "\n", "utf-8");
14901
+ await fs25.writeFile(filePath, stripped + "\n", "utf-8");
13592
14902
  return { path: filePath, status: "stripped" };
13593
14903
  }
13594
14904
  async function stripAllMarkerBlocks(repoRoot) {
13595
14905
  const out = [];
13596
14906
  for (const relative of CONTEXT_FILES) {
13597
- const full = join47(repoRoot, relative);
14907
+ const full = join54(repoRoot, relative);
13598
14908
  const result = await stripMarkerBlock(full).catch((err) => ({
13599
14909
  path: full,
13600
14910
  status: "untouched",
@@ -13606,25 +14916,25 @@ async function stripAllMarkerBlocks(repoRoot) {
13606
14916
  }
13607
14917
 
13608
14918
  // src/lib/cleanup/mcp-configs.ts
13609
- import { promises as fs21 } from "fs";
13610
- import { join as join48 } from "path";
14919
+ import { promises as fs26 } from "fs";
14920
+ import { join as join55 } from "path";
13611
14921
  function mcpConfigPaths(repoRoot, home) {
13612
14922
  return [
13613
- join48(repoRoot, ".mcp.json"),
13614
- join48(repoRoot, ".cursor", "mcp.json"),
13615
- join48(repoRoot, ".vscode", "mcp.json"),
13616
- join48(repoRoot, ".roo", "mcp.json"),
13617
- join48(home, ".cline", "mcp.json"),
13618
- join48(home, ".codeium", "windsurf", "mcp_config.json"),
13619
- join48(home, ".gemini", "settings.json"),
13620
- join48(home, ".codex", "mcp.json"),
13621
- join48(home, ".roo", "mcp.json")
14923
+ join55(repoRoot, ".mcp.json"),
14924
+ join55(repoRoot, ".cursor", "mcp.json"),
14925
+ join55(repoRoot, ".vscode", "mcp.json"),
14926
+ join55(repoRoot, ".roo", "mcp.json"),
14927
+ join55(home, ".cline", "mcp.json"),
14928
+ join55(home, ".codeium", "windsurf", "mcp_config.json"),
14929
+ join55(home, ".gemini", "settings.json"),
14930
+ join55(home, ".codex", "mcp.json"),
14931
+ join55(home, ".roo", "mcp.json")
13622
14932
  ];
13623
14933
  }
13624
14934
  async function removeRepowiseFromConfig(path, serverName) {
13625
14935
  let raw;
13626
14936
  try {
13627
- raw = await fs21.readFile(path, "utf-8");
14937
+ raw = await fs26.readFile(path, "utf-8");
13628
14938
  } catch (err) {
13629
14939
  if (err.code === "ENOENT") return { path, status: "not-found" };
13630
14940
  return { path, status: "error", error: err.message };
@@ -13644,7 +14954,7 @@ async function removeRepowiseFromConfig(path, serverName) {
13644
14954
  } else {
13645
14955
  next.mcpServers = servers;
13646
14956
  }
13647
- await fs21.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
14957
+ await fs26.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
13648
14958
  return { path, status: "removed" };
13649
14959
  }
13650
14960
  async function removeAllMcpEntries(repoRoot, home, repoId) {
@@ -13657,17 +14967,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
13657
14967
  }
13658
14968
 
13659
14969
  // src/lib/cleanup/local-state.ts
13660
- import { promises as fs22 } from "fs";
14970
+ import { promises as fs27 } from "fs";
13661
14971
  import { homedir as homedir8 } from "os";
13662
- import { join as join49, resolve as resolve3 } from "path";
14972
+ import { join as join56, resolve as resolve3 } from "path";
13663
14973
  async function clearLocalState(homeOverride) {
13664
14974
  const home = homeOverride ?? homedir8();
13665
- const target = resolve3(join49(home, ".repowise"));
14975
+ const target = resolve3(join56(home, ".repowise"));
13666
14976
  if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
13667
14977
  return { path: target, status: "error", error: "refused: not under home" };
13668
14978
  }
13669
14979
  try {
13670
- await fs22.rm(target, { recursive: true, force: false });
14980
+ await fs27.rm(target, { recursive: true, force: false });
13671
14981
  return { path: target, status: "removed" };
13672
14982
  } catch (err) {
13673
14983
  if (err.code === "ENOENT")
@@ -13706,7 +15016,7 @@ async function uninstall2(opts = {}) {
13706
15016
  else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
13707
15017
  if (tier === "stop") return report;
13708
15018
  try {
13709
- await fs23.unlink(join50(home, ".repowise", "credentials.json"));
15019
+ await fs28.unlink(join57(home, ".repowise", "credentials.json"));
13710
15020
  report.removed.push("credentials");
13711
15021
  } catch (err) {
13712
15022
  if (err.code !== "ENOENT") {
@@ -13733,7 +15043,7 @@ async function uninstall2(opts = {}) {
13733
15043
  const allPaths = mcpConfigPaths(repoRoot, home);
13734
15044
  for (const p of allPaths) {
13735
15045
  try {
13736
- await fs23.access(p);
15046
+ await fs28.access(p);
13737
15047
  } catch {
13738
15048
  }
13739
15049
  }
@@ -13745,7 +15055,7 @@ async function uninstall2(opts = {}) {
13745
15055
  }
13746
15056
  async function defaultLoadRepoIds(home) {
13747
15057
  try {
13748
- const raw = await fs23.readFile(join50(home, ".repowise", "config.json"), "utf-8");
15058
+ const raw = await fs28.readFile(join57(home, ".repowise", "config.json"), "utf-8");
13749
15059
  const parsed = JSON.parse(raw);
13750
15060
  return (parsed.repos ?? []).map((r) => r.repoId);
13751
15061
  } catch {
@@ -13792,12 +15102,12 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
13792
15102
 
13793
15103
  // src/commands/mcp-shim.ts
13794
15104
  init_config_dir();
13795
- import { promises as fs24 } from "fs";
15105
+ import { promises as fs29 } from "fs";
13796
15106
  import { createInterface as createInterface2 } from "readline";
13797
- import { join as join51 } from "path";
15107
+ import { join as join58 } from "path";
13798
15108
  var DEFAULT_MAX = 200 * 1024;
13799
15109
  async function mcpShim(opts) {
13800
- const endpointPath = opts.endpointFile ?? join51(getConfigDir(), "listener.endpoint");
15110
+ const endpointPath = opts.endpointFile ?? join58(getConfigDir(), "listener.endpoint");
13801
15111
  const stdin = opts.stdin ?? process.stdin;
13802
15112
  const stdout = opts.stdout ?? process.stdout;
13803
15113
  const stderr = opts.stderr ?? process.stderr;
@@ -13870,7 +15180,7 @@ async function mcpShim(opts) {
13870
15180
  }
13871
15181
  async function readEndpoint(path) {
13872
15182
  try {
13873
- const raw = (await fs24.readFile(path, "utf-8")).trim();
15183
+ const raw = (await fs29.readFile(path, "utf-8")).trim();
13874
15184
  if (!raw) return null;
13875
15185
  if (raw.startsWith("http://") || raw.startsWith("https://")) {
13876
15186
  return { endpoint: raw.split("\n")[0].trim(), secret: null };
@@ -14110,7 +15420,7 @@ init_registry();
14110
15420
  init_native_installer();
14111
15421
  init_coursier_installer();
14112
15422
  init_toolchain_installer();
14113
- import { spawn as spawn11 } from "child_process";
15423
+ import { spawn as spawn12 } from "child_process";
14114
15424
  import { resolve as resolve4 } from "path";
14115
15425
  import chalk16 from "chalk";
14116
15426
  async function isOnPath(command) {
@@ -14118,7 +15428,7 @@ async function isOnPath(command) {
14118
15428
  const isWin = process.platform === "win32";
14119
15429
  const probeCmd = isWin ? "where" : "which";
14120
15430
  return new Promise((resolve5) => {
14121
- const child = spawn11(probeCmd, [command], { stdio: "ignore" });
15431
+ const child = spawn12(probeCmd, [command], { stdio: "ignore" });
14122
15432
  child.on("close", (code) => {
14123
15433
  resolve5(code === 0);
14124
15434
  });
@@ -14285,13 +15595,13 @@ function formatInstallLine(lang, outcome) {
14285
15595
  }
14286
15596
  var MAX_KEEP_WARM_MINUTES = 240;
14287
15597
  async function autoDetectLanguages(cwd) {
14288
- const { readdir: readdir4 } = await import("fs/promises");
15598
+ const { readdir: readdir6 } = await import("fs/promises");
14289
15599
  const seen = /* @__PURE__ */ new Set();
14290
15600
  async function walk(dir, depth) {
14291
15601
  if (depth > 1) return;
14292
15602
  let entries;
14293
15603
  try {
14294
- entries = await readdir4(dir, { withFileTypes: true });
15604
+ entries = await readdir6(dir, { withFileTypes: true });
14295
15605
  } catch {
14296
15606
  return;
14297
15607
  }
@@ -14546,7 +15856,7 @@ async function lspOn() {
14546
15856
  // bin/repowise.ts
14547
15857
  var __filename = fileURLToPath4(import.meta.url);
14548
15858
  var __dirname = dirname20(__filename);
14549
- var pkg = JSON.parse(readFileSync3(join52(__dirname, "..", "..", "package.json"), "utf-8"));
15859
+ var pkg = JSON.parse(readFileSync3(join59(__dirname, "..", "..", "package.json"), "utf-8"));
14550
15860
  var program = new Command();
14551
15861
  program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
14552
15862
  await showWelcome(pkg.version);