repowisestage 0.0.58 → 0.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/repowise.js +1937 -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,353 @@ 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
+ function assertShellSafe(payload) {
3134
+ if (payload.includes("'")) {
3135
+ throw new Error("hook payload must not contain single quotes");
3136
+ }
3137
+ if (/[\r\n]/.test(payload)) {
3138
+ throw new Error("hook payload must be a single line");
3139
+ }
3140
+ }
3141
+ function buildHookEchoCommand(eventName, additionalContext) {
3142
+ const json = JSON.stringify({
3143
+ hookSpecificOutput: { hookEventName: eventName, additionalContext }
3144
+ });
3145
+ assertShellSafe(json);
3146
+ return `echo '${json}' ${REPOWISE_MANAGED_COMMENT}`;
3147
+ }
3148
+ function buildPlainJsonEchoCommand(payload) {
3149
+ const json = JSON.stringify(payload);
3150
+ assertShellSafe(json);
3151
+ return `echo '${json}' ${REPOWISE_MANAGED_COMMENT}`;
3152
+ }
3153
+ function upsertRepoWiseEntry(entries, next) {
3154
+ const list = Array.isArray(entries) ? entries : [];
3155
+ const idx = list.findIndex((entry) => entry?.hooks?.some?.((h) => typeof h?.command === "string" && isRepoWiseHookCommand(h.command)));
3156
+ if (idx >= 0) {
3157
+ list[idx] = next;
3158
+ } else {
3159
+ list.push(next);
3160
+ }
3161
+ return list;
3162
+ }
3163
+ function removeRepoWiseEntries(entries) {
3164
+ const list = Array.isArray(entries) ? entries : [];
3165
+ return list.filter((entry) => !entry?.hooks?.some?.((h) => typeof h?.command === "string" && isRepoWiseHookCommand(h.command)));
3166
+ }
3167
+ function mergeHookEvents(existingContent, events) {
3168
+ let settings = {};
3169
+ if (existingContent) {
3170
+ let parsed;
3171
+ try {
3172
+ parsed = JSON.parse(existingContent);
3173
+ } catch {
3174
+ return existingContent;
3175
+ }
3176
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3177
+ return existingContent;
3178
+ }
3179
+ settings = parsed;
3180
+ }
3181
+ if (settings["hooks"] !== void 0 && !isPlainObject(settings["hooks"])) {
3182
+ return existingContent ?? JSON.stringify(settings, null, 2) + "\n";
3183
+ }
3184
+ const hooks = isPlainObject(settings["hooks"]) ? settings["hooks"] : {};
3185
+ for (const [event, entry] of Object.entries(events)) {
3186
+ if (hooks[event] !== void 0 && !Array.isArray(hooks[event])) {
3187
+ continue;
3188
+ }
3189
+ hooks[event] = upsertRepoWiseEntry(hooks[event], entry);
3190
+ }
3191
+ settings["hooks"] = hooks;
3192
+ const next = JSON.stringify(settings, null, 2) + "\n";
3193
+ return existingContent === next ? existingContent : next;
3194
+ }
3195
+ function isPlainObject(value) {
3196
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3197
+ }
3198
+ function removeHookEvents(existingContent, eventNames) {
3199
+ let settings;
3200
+ try {
3201
+ const parsed = JSON.parse(existingContent);
3202
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
3203
+ return existingContent;
3204
+ settings = parsed;
3205
+ } catch {
3206
+ return existingContent;
3207
+ }
3208
+ const hooks = settings["hooks"] && typeof settings["hooks"] === "object" && !Array.isArray(settings["hooks"]) ? settings["hooks"] : null;
3209
+ if (!hooks)
3210
+ return existingContent;
3211
+ for (const event of eventNames) {
3212
+ if (hooks[event] !== void 0 && !Array.isArray(hooks[event])) {
3213
+ continue;
3214
+ }
3215
+ const remaining = removeRepoWiseEntries(hooks[event]);
3216
+ if (remaining.length > 0) {
3217
+ hooks[event] = remaining;
3218
+ } else {
3219
+ delete hooks[event];
3220
+ }
3221
+ }
3222
+ if (Object.keys(hooks).length === 0) {
3223
+ delete settings["hooks"];
3224
+ }
3225
+ if (Object.keys(settings).length === 0)
3226
+ return null;
3227
+ const next = JSON.stringify(settings, null, 2) + "\n";
3228
+ return existingContent === next ? existingContent : next;
3229
+ }
3230
+ function mergeClaudeHookSettings(existingContent, params) {
3231
+ return mergeHookEvents(existingContent, {
3232
+ SubagentStart: {
3233
+ matcher: "",
3234
+ hooks: [
3235
+ {
3236
+ type: "command",
3237
+ command: buildHookEchoCommand("SubagentStart", buildSubagentDirective(params.contextFolder))
3238
+ }
3239
+ ]
3240
+ },
3241
+ UserPromptSubmit: {
3242
+ hooks: [
3243
+ {
3244
+ type: "command",
3245
+ command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "claude"))
3246
+ }
3247
+ ]
3248
+ }
3249
+ });
3250
+ }
3251
+ function removeClaudeHookSettings(existingContent) {
3252
+ return removeHookEvents(existingContent, ["SubagentStart", "UserPromptSubmit"]);
3253
+ }
3254
+ function mergeGeminiHookSettings(existingContent, params) {
3255
+ return mergeHookEvents(existingContent, {
3256
+ BeforeAgent: {
3257
+ hooks: [
3258
+ {
3259
+ type: "command",
3260
+ name: "repowise-first",
3261
+ command: buildPlainJsonEchoCommand({
3262
+ hookSpecificOutput: {
3263
+ additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3264
+ }
3265
+ })
3266
+ }
3267
+ ]
3268
+ }
3269
+ });
3270
+ }
3271
+ function removeGeminiHookSettings(existingContent) {
3272
+ return removeHookEvents(existingContent, ["BeforeAgent"]);
3273
+ }
3274
+ function mergeCodexHookSettings(existingContent, params) {
3275
+ return mergeHookEvents(existingContent, {
3276
+ UserPromptSubmit: {
3277
+ hooks: [
3278
+ {
3279
+ type: "command",
3280
+ command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder))
3281
+ }
3282
+ ]
3283
+ }
3284
+ });
3285
+ }
3286
+ function removeCodexHookSettings(existingContent) {
3287
+ return removeHookEvents(existingContent, ["UserPromptSubmit"]);
3288
+ }
3289
+ function buildCopilotHooksFile(params) {
3290
+ const command = buildPlainJsonEchoCommand({
3291
+ additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3292
+ });
3293
+ return JSON.stringify({
3294
+ version: 1,
3295
+ hooks: {
3296
+ sessionStart: [{ type: "command", bash: command, powershell: command, timeoutSec: 10 }]
3297
+ }
3298
+ }, null, 2) + "\n";
3299
+ }
3300
+ function buildClineHookScript(params) {
3301
+ const command = buildPlainJsonEchoCommand({
3302
+ contextModification: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
3303
+ });
3304
+ return [
3305
+ "#!/bin/sh",
3306
+ "# repowise-context hook managed by RepoWise. Do not edit; regenerated on sync.",
3307
+ command,
3308
+ ""
3309
+ ].join("\n");
3310
+ }
3311
+ var GITIGNORE_START = "# repowise:start (managed by RepoWise, do not edit between markers)";
3312
+ var GITIGNORE_END = "# repowise:end";
3313
+ function updateGitignoreBlock(existingContent, changes) {
3314
+ let content = existingContent ?? "";
3315
+ const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
3316
+ let startIdx = content.indexOf(GITIGNORE_START);
3317
+ let endIdx = startIdx === -1 ? -1 : content.indexOf(GITIGNORE_END, startIdx);
3318
+ let hasBlock = startIdx !== -1 && endIdx > startIdx;
3319
+ if (!hasBlock && (content.includes(GITIGNORE_START) || content.includes(GITIGNORE_END))) {
3320
+ content = content.split(/\r?\n/).filter((line) => {
3321
+ const t = line.trim();
3322
+ return t !== GITIGNORE_START && t !== GITIGNORE_END;
3323
+ }).join(lineEnding);
3324
+ startIdx = -1;
3325
+ endIdx = -1;
3326
+ hasBlock = false;
3327
+ }
3328
+ const current = /* @__PURE__ */ new Set();
3329
+ if (hasBlock) {
3330
+ const inner = content.slice(startIdx + GITIGNORE_START.length, endIdx);
3331
+ for (const line of inner.split(/\r?\n/)) {
3332
+ const trimmed = line.trim();
3333
+ if (trimmed.length > 0 && !trimmed.startsWith("#"))
3334
+ current.add(trimmed);
3335
+ }
3336
+ }
3337
+ for (const p of changes.add ?? [])
3338
+ current.add(p);
3339
+ for (const p of changes.remove ?? [])
3340
+ current.delete(p);
3341
+ const entries = [...current].sort();
3342
+ const block = entries.length === 0 ? "" : [GITIGNORE_START, ...entries, GITIGNORE_END].join(lineEnding);
3343
+ let next;
3344
+ if (hasBlock) {
3345
+ const before = content.slice(0, startIdx);
3346
+ const after = content.slice(endIdx + GITIGNORE_END.length);
3347
+ if (block === "") {
3348
+ next = before.replace(/(\r?\n){2,}$/, lineEnding) + after.replace(/^\r?\n/, "");
3349
+ if (next.trim() === "")
3350
+ return "";
3351
+ } else {
3352
+ next = before + block + after;
3353
+ }
3354
+ } else if (block === "") {
3355
+ next = content;
3356
+ } else {
3357
+ const separator = content.length === 0 ? "" : content.endsWith(lineEnding) ? lineEnding : lineEnding + lineEnding;
3358
+ next = content + separator + block + lineEnding;
3359
+ }
3360
+ return next === (existingContent ?? "") ? existingContent ?? "" : next;
3361
+ }
3362
+ var CLAUDE_PROJECT_SETTINGS = ".claude/settings.json";
3363
+ var CLAUDE_LOCAL_SETTINGS = ".claude/settings.local.json";
3364
+ async function isGitTracked(repoRoot, relPath) {
3365
+ return new Promise((resolve5) => {
3366
+ execFile("git", ["ls-files", "--error-unmatch", relPath], { cwd: repoRoot }, (err) => {
3367
+ if (!err)
3368
+ return resolve5(true);
3369
+ if (err.code === 1 || err.code === 128)
3370
+ return resolve5(false);
3371
+ resolve5(true);
3372
+ });
3373
+ });
3374
+ }
3375
+ async function applyGitignoreChanges(repoRoot, changes) {
3376
+ const path = join2(repoRoot, ".gitignore");
3377
+ let existing = null;
3378
+ try {
3379
+ existing = await readFile(path, "utf-8");
3380
+ } catch {
3381
+ }
3382
+ const next = updateGitignoreBlock(existing, changes);
3383
+ if (next !== (existing ?? "")) {
3384
+ await writeFile(path, next, "utf-8");
3385
+ }
3386
+ }
3387
+ async function writeClaudeHooksToRepo(repoRoot, contextFolder) {
3388
+ const repoName = basename(repoRoot);
3389
+ const projectTracked = await isGitTracked(repoRoot, CLAUDE_PROJECT_SETTINGS);
3390
+ if (projectTracked) {
3391
+ let trackedContent = "";
3392
+ try {
3393
+ trackedContent = await readFile(join2(repoRoot, CLAUDE_PROJECT_SETTINGS), "utf-8");
3394
+ } catch {
3395
+ }
3396
+ const trackedHasOurHooks = trackedContent.includes(REPOWISE_MANAGED_COMMENT) || trackedContent.includes("additionalContext") && trackedContent.includes("/project-overview.md");
3397
+ if (trackedHasOurHooks) {
3398
+ await applyGitignoreChanges(repoRoot, {
3399
+ remove: [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS]
3400
+ });
3401
+ return { status: "unchanged", relPath: CLAUDE_PROJECT_SETTINGS };
3402
+ }
3403
+ }
3404
+ const targetRel = projectTracked ? CLAUDE_LOCAL_SETTINGS : CLAUDE_PROJECT_SETTINGS;
3405
+ const targetPath = join2(repoRoot, targetRel);
3406
+ let existing = null;
3407
+ try {
3408
+ existing = await readFile(targetPath, "utf-8");
3409
+ } catch {
3410
+ }
3411
+ const merged = mergeClaudeHookSettings(existing, { repoName, contextFolder });
3412
+ let status2 = "unchanged";
3413
+ if (merged !== existing) {
3414
+ await mkdir(join2(repoRoot, ".claude"), { recursive: true });
3415
+ await writeFile(targetPath, merged, "utf-8");
3416
+ status2 = "written";
3417
+ }
3418
+ if (status2 === "written") {
3419
+ await applyGitignoreChanges(repoRoot, {
3420
+ add: [targetRel],
3421
+ remove: [projectTracked ? CLAUDE_PROJECT_SETTINGS : CLAUDE_LOCAL_SETTINGS]
3422
+ });
3423
+ }
3424
+ return { status: status2, relPath: targetRel };
3425
+ }
3426
+ async function removeClaudeHooksFromRepo(repoRoot) {
3427
+ for (const rel of [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS]) {
3428
+ const path = join2(repoRoot, rel);
3429
+ let existing;
3430
+ try {
3431
+ existing = await readFile(path, "utf-8");
3432
+ } catch {
3433
+ continue;
3434
+ }
3435
+ const next = removeClaudeHookSettings(existing);
3436
+ if (next === null) {
3437
+ await unlink(path);
3438
+ } else if (next !== existing) {
3439
+ await writeFile(path, next, "utf-8");
3440
+ }
3441
+ }
3442
+ await applyGitignoreChanges(repoRoot, {
3443
+ remove: [CLAUDE_PROJECT_SETTINGS, CLAUDE_LOCAL_SETTINGS]
3444
+ });
3445
+ }
3446
+
3087
3447
  // ../../packages/shared/dist/lib/ai-tools.js
3448
+ var AI_TOOLS_REFERENCE_REV = 2;
3088
3449
  var AI_TOOL_CONFIG = {
3089
3450
  cursor: {
3090
3451
  label: "Cursor",
@@ -3155,6 +3516,15 @@ var AI_TOOL_CONFIG = {
3155
3516
  format: "markdown",
3156
3517
  owned: true
3157
3518
  },
3519
+ kilo: {
3520
+ label: "Kilo Code",
3521
+ fileName: "repowise.md",
3522
+ filePath: ".kilocode/rules/repowise.md",
3523
+ markerStart: "<!-- repowise-start -->",
3524
+ markerEnd: "<!-- repowise-end -->",
3525
+ format: "markdown",
3526
+ owned: true
3527
+ },
3158
3528
  gemini: {
3159
3529
  label: "Gemini CLI",
3160
3530
  fileName: "GEMINI.md",
@@ -3227,6 +3597,8 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3227
3597
  return { path: f.relativePath, desc: isOverview ? `${desc} (full index of all files)` : desc };
3228
3598
  });
3229
3599
  const hasFiles = fileLines.length > 0;
3600
+ const mcpName = repoName.replace(/`/g, "");
3601
+ 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
3602
  const contentLines = [
3231
3603
  `## Project Context \u2014 ${safeName}`,
3232
3604
  "",
@@ -3235,6 +3607,8 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3235
3607
  AI_AGENT_USAGE_INSTRUCTIONS,
3236
3608
  "",
3237
3609
  `**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that maps every context file to its domain.`,
3610
+ "",
3611
+ mcpNamingNote,
3238
3612
  ""
3239
3613
  ];
3240
3614
  if (hasFiles) {
@@ -3252,10 +3626,10 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
3252
3626
  }
3253
3627
  async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
3254
3628
  const config2 = AI_TOOL_CONFIG[tool];
3255
- const fullPath = join2(repoRoot, config2.filePath);
3629
+ const fullPath = join3(repoRoot, config2.filePath);
3256
3630
  const dir = dirname(fullPath);
3257
3631
  if (dir !== repoRoot) {
3258
- await mkdir(dir, { recursive: true });
3632
+ await mkdir2(dir, { recursive: true });
3259
3633
  }
3260
3634
  const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
3261
3635
  if (config2.owned) {
@@ -3265,13 +3639,13 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
3265
3639
  created2 = false;
3266
3640
  } catch {
3267
3641
  }
3268
- await writeFile(fullPath, referenceBlock, "utf-8");
3642
+ await writeFile2(fullPath, referenceBlock, "utf-8");
3269
3643
  return { created: created2 };
3270
3644
  }
3271
3645
  let existing = "";
3272
3646
  let created = true;
3273
3647
  try {
3274
- existing = await readFile(fullPath, "utf-8");
3648
+ existing = await readFile2(fullPath, "utf-8");
3275
3649
  created = false;
3276
3650
  } catch (err) {
3277
3651
  if (err.code !== "ENOENT")
@@ -3288,14 +3662,43 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
3288
3662
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
3289
3663
  content = existing + separator + referenceBlock + "\n";
3290
3664
  }
3291
- await writeFile(fullPath, content, "utf-8");
3665
+ await writeFile2(fullPath, content, "utf-8");
3292
3666
  return { created };
3293
3667
  }
3668
+ async function removeToolConfig(repoRoot, tool) {
3669
+ const config2 = AI_TOOL_CONFIG[tool];
3670
+ const fullPath = join3(repoRoot, config2.filePath);
3671
+ if (config2.owned) {
3672
+ try {
3673
+ await unlink2(fullPath);
3674
+ } catch (err) {
3675
+ if (err.code !== "ENOENT")
3676
+ throw err;
3677
+ }
3678
+ return;
3679
+ }
3680
+ let existing;
3681
+ try {
3682
+ existing = await readFile2(fullPath, "utf-8");
3683
+ } catch (err) {
3684
+ if (err.code === "ENOENT")
3685
+ return;
3686
+ throw err;
3687
+ }
3688
+ const startIdx = existing.indexOf(config2.markerStart);
3689
+ const endIdx = existing.indexOf(config2.markerEnd);
3690
+ if (startIdx === -1 || endIdx === -1)
3691
+ return;
3692
+ const before = existing.slice(0, startIdx);
3693
+ const after = existing.slice(endIdx + config2.markerEnd.length);
3694
+ const content = (before + after).replace(/\n{3,}/g, "\n\n").trim() + "\n";
3695
+ await writeFile2(fullPath, content, "utf-8");
3696
+ }
3294
3697
  async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
3295
3698
  const config2 = AI_TOOL_CONFIG[tool];
3296
3699
  if (!config2.legacyFilePath)
3297
3700
  return { migrated: false, legacyRemoved: false };
3298
- const legacyPath = join2(repoRoot, config2.legacyFilePath);
3701
+ const legacyPath = join3(repoRoot, config2.legacyFilePath);
3299
3702
  let legacyIsFile = false;
3300
3703
  try {
3301
3704
  const s = await stat(legacyPath);
@@ -3308,7 +3711,7 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
3308
3711
  await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
3309
3712
  return { migrated: false, legacyRemoved: false };
3310
3713
  }
3311
- const legacyContent = await readFile(legacyPath, "utf-8");
3714
+ const legacyContent = await readFile2(legacyPath, "utf-8");
3312
3715
  let cleaned = legacyContent;
3313
3716
  const oldMarkers = [
3314
3717
  { start: "# --- repowise-start ---", end: "# --- repowise-end ---" },
@@ -3324,16 +3727,16 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
3324
3727
  cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
3325
3728
  if (tool === "cline") {
3326
3729
  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");
3730
+ await unlink2(legacyPath);
3731
+ await mkdir2(join3(repoRoot, ".clinerules"), { recursive: true });
3732
+ await writeFile2(join3(repoRoot, ".clinerules/user-rules.md"), cleaned + "\n", "utf-8");
3330
3733
  } else {
3331
- await unlink(legacyPath);
3734
+ await unlink2(legacyPath);
3332
3735
  }
3333
3736
  } else if (cleaned.length > 0) {
3334
- await writeFile(legacyPath, cleaned + "\n", "utf-8");
3737
+ await writeFile2(legacyPath, cleaned + "\n", "utf-8");
3335
3738
  } else {
3336
- await unlink(legacyPath);
3739
+ await unlink2(legacyPath);
3337
3740
  }
3338
3741
  await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
3339
3742
  return { migrated: true, legacyRemoved: cleaned.length === 0 };
@@ -3351,16 +3754,44 @@ async function detectInstalledTools(repoRoot) {
3351
3754
  for (const [tool, config2] of Object.entries(AI_TOOL_CONFIG)) {
3352
3755
  if (tool !== "codex" && config2.filePath === "AGENTS.md")
3353
3756
  continue;
3354
- if (await fileExists(join2(repoRoot, config2.filePath))) {
3757
+ if (await fileExists(join3(repoRoot, config2.filePath))) {
3355
3758
  detected.push(tool);
3356
- } else if (config2.legacyFilePath && await fileExists(join2(repoRoot, config2.legacyFilePath))) {
3759
+ } else if (config2.legacyFilePath && await fileExists(join3(repoRoot, config2.legacyFilePath))) {
3357
3760
  detected.push(tool);
3358
3761
  }
3359
3762
  }
3360
- return detected;
3763
+ return detected.map((t) => t === "roo-code" ? "kilo" : t).filter(dedupe);
3764
+ }
3765
+ function dedupe(value, index, arr) {
3766
+ return arr.indexOf(value) === index;
3767
+ }
3768
+ async function migrateRooToKilo(repoRoot) {
3769
+ let migrated = false;
3770
+ if (await fileExists(join3(repoRoot, ".roo/rules/repowise.md"))) {
3771
+ await removeToolConfig(repoRoot, "roo-code");
3772
+ migrated = true;
3773
+ }
3774
+ const legacyPath = join3(repoRoot, ".roo/rules.md");
3775
+ try {
3776
+ const legacy = await readFile2(legacyPath, "utf-8");
3777
+ const config2 = AI_TOOL_CONFIG["roo-code"];
3778
+ const si = legacy.indexOf(config2.markerStart);
3779
+ const ei = legacy.indexOf(config2.markerEnd);
3780
+ if (si !== -1 && ei > si) {
3781
+ const cleaned = (legacy.slice(0, si) + legacy.slice(ei + config2.markerEnd.length)).replace(/\n{3,}/g, "\n\n").trim();
3782
+ if (cleaned.length > 0) {
3783
+ await writeFile2(legacyPath, cleaned + "\n", "utf-8");
3784
+ } else {
3785
+ await unlink2(legacyPath);
3786
+ }
3787
+ migrated = true;
3788
+ }
3789
+ } catch {
3790
+ }
3791
+ return migrated;
3361
3792
  }
3362
3793
  async function scanLocalContextFiles(repoRoot, contextFolder) {
3363
- const folderPath = join2(repoRoot, contextFolder);
3794
+ const folderPath = join3(repoRoot, contextFolder);
3364
3795
  try {
3365
3796
  const entries = await readdir(folderPath, { withFileTypes: true, recursive: true });
3366
3797
  const results = [];
@@ -3368,7 +3799,7 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
3368
3799
  if (!entry.isFile() || !entry.name.endsWith(".md"))
3369
3800
  continue;
3370
3801
  const parentDir = entry.parentPath ?? folderPath;
3371
- const fullPath = join2(parentDir, entry.name);
3802
+ const fullPath = join3(parentDir, entry.name);
3372
3803
  const relFromContext = fullPath.slice(folderPath.length + 1);
3373
3804
  results.push({
3374
3805
  fileName: relFromContext,
@@ -3385,8 +3816,8 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
3385
3816
 
3386
3817
  // ../listener/dist/lib/config.js
3387
3818
  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";
3819
+ import { readFile as readFile3, writeFile as writeFile3, rename, unlink as unlink3, mkdir as mkdir3, chmod, open } from "fs/promises";
3820
+ import { join as join4 } from "path";
3390
3821
  import lockfile from "proper-lockfile";
3391
3822
  function sanitizeLspOverrides(raw) {
3392
3823
  if (raw === void 0 || raw === null)
@@ -3404,10 +3835,10 @@ function sanitizeLspOverrides(raw) {
3404
3835
  var DEFAULT_API_URL = true ? "https://staging-api.repowise.ai" : "https://api.repowise.ai";
3405
3836
  async function getListenerConfig() {
3406
3837
  const configDir = getConfigDir();
3407
- const configPath = join3(configDir, "config.json");
3838
+ const configPath = join4(configDir, "config.json");
3408
3839
  const apiUrl = process.env["REPOWISE_API_URL"] ?? DEFAULT_API_URL;
3409
3840
  try {
3410
- const data = await readFile2(configPath, "utf-8");
3841
+ const data = await readFile3(configPath, "utf-8");
3411
3842
  const raw = JSON.parse(data);
3412
3843
  const validRepos = (raw.repos ?? []).filter((r) => typeof r === "object" && r !== null && typeof r.repoId === "string" && typeof r.localPath === "string");
3413
3844
  const lspOverrides = sanitizeLspOverrides(raw.lspOverrides);
@@ -3433,10 +3864,10 @@ async function getListenerConfig() {
3433
3864
  }
3434
3865
  async function saveListenerConfig(config2) {
3435
3866
  const configDir = getConfigDir();
3436
- const configPath = join3(configDir, "config.json");
3437
- await mkdir2(configDir, { recursive: true, mode: 448 });
3867
+ const configPath = join4(configDir, "config.json");
3868
+ await mkdir3(configDir, { recursive: true, mode: 448 });
3438
3869
  try {
3439
- await writeFile2(configPath, "", { flag: "a" });
3870
+ await writeFile3(configPath, "", { flag: "a" });
3440
3871
  } catch {
3441
3872
  }
3442
3873
  let release = null;
@@ -3444,7 +3875,7 @@ async function saveListenerConfig(config2) {
3444
3875
  release = await lockfile.lock(configPath, { stale: 1e4, retries: 3, realpath: false });
3445
3876
  let raw = {};
3446
3877
  try {
3447
- const data = await readFile2(configPath, "utf-8");
3878
+ const data = await readFile3(configPath, "utf-8");
3448
3879
  raw = JSON.parse(data);
3449
3880
  } catch {
3450
3881
  }
@@ -3462,7 +3893,7 @@ async function saveListenerConfig(config2) {
3462
3893
  };
3463
3894
  const tmpPath = configPath + ".tmp";
3464
3895
  try {
3465
- await writeFile2(tmpPath, JSON.stringify(output, null, 2));
3896
+ await writeFile3(tmpPath, JSON.stringify(output, null, 2));
3466
3897
  await chmod(tmpPath, 384);
3467
3898
  let fh;
3468
3899
  try {
@@ -3474,7 +3905,7 @@ async function saveListenerConfig(config2) {
3474
3905
  await rename(tmpPath, configPath);
3475
3906
  } catch (err) {
3476
3907
  try {
3477
- await unlink2(tmpPath);
3908
+ await unlink3(tmpPath);
3478
3909
  } catch {
3479
3910
  }
3480
3911
  throw err;
@@ -3491,16 +3922,16 @@ async function saveListenerConfig(config2) {
3491
3922
 
3492
3923
  // ../listener/dist/lib/state.js
3493
3924
  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";
3925
+ 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";
3926
+ import { join as join5 } from "path";
3496
3927
  import lockfile2 from "proper-lockfile";
3497
3928
  function emptyState() {
3498
3929
  return { repos: {} };
3499
3930
  }
3500
3931
  async function loadState() {
3501
- const statePath = join4(getConfigDir(), "listener-state.json");
3932
+ const statePath = join5(getConfigDir(), "listener-state.json");
3502
3933
  try {
3503
- const data = await readFile3(statePath, "utf-8");
3934
+ const data = await readFile4(statePath, "utf-8");
3504
3935
  const state = JSON.parse(data);
3505
3936
  if (typeof state !== "object" || state === null || typeof state.repos !== "object" || state.repos === null) {
3506
3937
  return emptyState();
@@ -3515,10 +3946,10 @@ async function loadState() {
3515
3946
  }
3516
3947
  async function saveState(state) {
3517
3948
  const configDir = getConfigDir();
3518
- const statePath = join4(configDir, "listener-state.json");
3519
- await mkdir3(configDir, { recursive: true, mode: 448 });
3949
+ const statePath = join5(configDir, "listener-state.json");
3950
+ await mkdir4(configDir, { recursive: true, mode: 448 });
3520
3951
  try {
3521
- await writeFile3(statePath, "", { flag: "a" });
3952
+ await writeFile4(statePath, "", { flag: "a" });
3522
3953
  } catch {
3523
3954
  }
3524
3955
  let release = null;
@@ -3529,7 +3960,7 @@ async function saveState(state) {
3529
3960
  }
3530
3961
  const tmpPath = statePath + ".tmp";
3531
3962
  try {
3532
- await writeFile3(tmpPath, JSON.stringify(state, null, 2));
3963
+ await writeFile4(tmpPath, JSON.stringify(state, null, 2));
3533
3964
  await chmod2(tmpPath, 384);
3534
3965
  let fh;
3535
3966
  try {
@@ -3541,7 +3972,7 @@ async function saveState(state) {
3541
3972
  await rename2(tmpPath, statePath);
3542
3973
  } catch (err) {
3543
3974
  try {
3544
- await unlink3(tmpPath);
3975
+ await unlink4(tmpPath);
3545
3976
  } catch {
3546
3977
  }
3547
3978
  throw err;
@@ -3555,7 +3986,7 @@ async function saveState(state) {
3555
3986
 
3556
3987
  // ../listener/dist/lib/reconcile.js
3557
3988
  import { statSync, readdirSync } from "fs";
3558
- import { basename, dirname as dirname2, join as join5 } from "path";
3989
+ import { basename as basename2, dirname as dirname2, join as join6 } from "path";
3559
3990
  function reconcileRepos(configRepos, activeRepos, state, apiUrl, options) {
3560
3991
  if (activeRepos.length === 0) {
3561
3992
  return { updated: false, repos: configRepos, changes: [], addedRepos: [] };
@@ -3598,8 +4029,8 @@ function reconcileRepos(configRepos, activeRepos, state, apiUrl, options) {
3598
4029
  };
3599
4030
  }
3600
4031
  }
3601
- const dirName = basename(repo.localPath);
3602
- const parentDir = basename(dirname2(repo.localPath));
4032
+ const dirName = basename2(repo.localPath);
4033
+ const parentDir = basename2(dirname2(repo.localPath));
3603
4034
  const fullPathName = `${parentDir}/${dirName}`;
3604
4035
  let matches = activeRepos.filter((ar) => ar.fullName === fullPathName);
3605
4036
  if (matches.length === 0) {
@@ -3673,7 +4104,7 @@ function findLocalRepo(activeRepo, parentDirs, usedPaths) {
3673
4104
  const nameParts = activeRepo.fullName.split("/");
3674
4105
  const repoName = nameParts[nameParts.length - 1];
3675
4106
  for (const parentDir of parentDirs) {
3676
- const candidate = join5(parentDir, repoName);
4107
+ const candidate = join6(parentDir, repoName);
3677
4108
  if (!usedPaths.has(candidate) && hasContextFolder(candidate)) {
3678
4109
  return candidate;
3679
4110
  }
@@ -3682,7 +4113,7 @@ function findLocalRepo(activeRepo, parentDirs, usedPaths) {
3682
4113
  }
3683
4114
  function hasContextFolder(dirPath) {
3684
4115
  try {
3685
- const contextPath = join5(dirPath, "repowise-context");
4116
+ const contextPath = join6(dirPath, "repowise-context");
3686
4117
  const s = statSync(contextPath);
3687
4118
  if (!s.isDirectory())
3688
4119
  return false;
@@ -3713,8 +4144,8 @@ function migrateState(state, oldId, newId) {
3713
4144
 
3714
4145
  // ../listener/dist/lib/auth.js
3715
4146
  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";
4147
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, chmod as chmod3 } from "fs/promises";
4148
+ import { join as join7 } from "path";
3718
4149
  function getTokenUrl(creds) {
3719
4150
  const cognito = creds?.cognito;
3720
4151
  const domain = process.env["REPOWISE_COGNITO_DOMAIN"] ?? cognito?.domain ?? "auth-repowise-dev";
@@ -3753,8 +4184,8 @@ async function refreshTokens(refreshToken, creds) {
3753
4184
  }
3754
4185
  async function getStoredCredentials() {
3755
4186
  try {
3756
- const credPath = join6(getConfigDir(), "credentials.json");
3757
- const data = await readFile4(credPath, "utf-8");
4187
+ const credPath = join7(getConfigDir(), "credentials.json");
4188
+ const data = await readFile5(credPath, "utf-8");
3758
4189
  return JSON.parse(data);
3759
4190
  } catch (err) {
3760
4191
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -3765,9 +4196,9 @@ async function getStoredCredentials() {
3765
4196
  }
3766
4197
  async function storeCredentials(credentials) {
3767
4198
  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));
4199
+ const credPath = join7(dir, "credentials.json");
4200
+ await mkdir5(dir, { recursive: true, mode: 448 });
4201
+ await writeFile5(credPath, JSON.stringify(credentials, null, 2));
3771
4202
  await chmod3(credPath, 384);
3772
4203
  }
3773
4204
  async function getValidCredentials(options) {
@@ -3917,17 +4348,17 @@ function notifyContextUpdated(repoId, fileCount) {
3917
4348
  }
3918
4349
 
3919
4350
  // ../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";
4351
+ import { execFile as execFile2 } from "child_process";
4352
+ import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
4353
+ import { dirname as dirname3, join as join9 } from "path";
3923
4354
  import { promisify } from "util";
3924
4355
 
3925
4356
  // ../listener/dist/file-writer.js
3926
4357
  import { access } from "fs/promises";
3927
- import { join as join7 } from "path";
4358
+ import { join as join8 } from "path";
3928
4359
  async function verifyContextFolder(localPath) {
3929
4360
  try {
3930
- await access(join7(localPath, "repowise-context"));
4361
+ await access(join8(localPath, "repowise-context"));
3931
4362
  return true;
3932
4363
  } catch {
3933
4364
  return false;
@@ -3935,7 +4366,7 @@ async function verifyContextFolder(localPath) {
3935
4366
  }
3936
4367
 
3937
4368
  // ../listener/dist/context-fetcher.js
3938
- var execFileAsync = promisify(execFile);
4369
+ var execFileAsync = promisify(execFile2);
3939
4370
  async function fetchContextUpdates(localPath) {
3940
4371
  try {
3941
4372
  const { stdout: beforeSha } = await execFileAsync("git", [
@@ -4015,8 +4446,8 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4015
4446
  if (files.length === 0) {
4016
4447
  return { success: true, updatedFiles: [] };
4017
4448
  }
4018
- const contextDir = join8(localPath, "repowise-context");
4019
- await mkdir5(contextDir, { recursive: true });
4449
+ const contextDir = join9(localPath, "repowise-context");
4450
+ await mkdir6(contextDir, { recursive: true });
4020
4451
  const updatedFiles = [];
4021
4452
  for (const file of files) {
4022
4453
  if (file.fileName.includes(".."))
@@ -4040,10 +4471,10 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4040
4471
  continue;
4041
4472
  }
4042
4473
  const content = await contentRes.text();
4043
- const filePath = join8(contextDir, file.fileName);
4044
- await mkdir5(dirname3(filePath), { recursive: true });
4474
+ const filePath = join9(contextDir, file.fileName);
4475
+ await mkdir6(dirname3(filePath), { recursive: true });
4045
4476
  const finalContent = file.fileName === "project-overview.md" ? injectAgentInstructions(content) : content;
4046
- await writeFile5(filePath, finalContent, "utf-8");
4477
+ await writeFile6(filePath, finalContent, "utf-8");
4047
4478
  updatedFiles.push(file.fileName);
4048
4479
  }
4049
4480
  console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
@@ -4059,12 +4490,12 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
4059
4490
  init_process_manager();
4060
4491
 
4061
4492
  // ../listener/dist/lib/auto-updater.js
4062
- import { execFile as execFile2 } from "child_process";
4493
+ import { execFile as execFile3 } from "child_process";
4063
4494
  import { readFileSync } from "fs";
4064
4495
  import { access as access2, constants, realpath } from "fs/promises";
4065
- import { dirname as dirname4, join as join10 } from "path";
4496
+ import { dirname as dirname4, join as join11 } from "path";
4066
4497
  import { promisify as promisify2 } from "util";
4067
- var execFileAsync2 = promisify2(execFile2);
4498
+ var execFileAsync2 = promisify2(execFile3);
4068
4499
  var installInFlight = false;
4069
4500
  async function installUpdate(currentVersion, packageName, targetVersion) {
4070
4501
  if (process.env["CI"] === "true")
@@ -4086,12 +4517,12 @@ async function installUpdate(currentVersion, packageName, targetVersion) {
4086
4517
  }
4087
4518
  installInFlight = true;
4088
4519
  try {
4089
- const npmWrapper = join10(dirname4(process.execPath), "npm");
4520
+ const npmWrapper = join11(dirname4(process.execPath), "npm");
4090
4521
  const npmScript = await realpath(npmWrapper);
4091
4522
  const runNpm = (args) => execFileAsync2(process.execPath, [npmScript, ...args], { timeout: 6e4 });
4092
4523
  try {
4093
4524
  const { stdout: prefix } = await runNpm(["prefix", "-g"]);
4094
- const npmDir = join10(prefix.trim(), "lib", "node_modules");
4525
+ const npmDir = join11(prefix.trim(), "lib", "node_modules");
4095
4526
  const checkDir = process.platform === "win32" ? prefix.trim() : npmDir;
4096
4527
  await access2(checkDir, constants.W_OK);
4097
4528
  } catch (err) {
@@ -4109,7 +4540,7 @@ async function installUpdate(currentVersion, packageName, targetVersion) {
4109
4540
  }
4110
4541
  try {
4111
4542
  const { stdout: prefix } = await runNpm(["prefix", "-g"]);
4112
- const installedPkgJson = join10(prefix.trim(), "lib", "node_modules", packageName, "package.json");
4543
+ const installedPkgJson = join11(prefix.trim(), "lib", "node_modules", packageName, "package.json");
4113
4544
  const installedVersion = JSON.parse(readFileSync(installedPkgJson, "utf-8")).version;
4114
4545
  if (installedVersion !== targetVersion) {
4115
4546
  const msg = `post-install check failed: expected ${targetVersion}, found ${installedVersion ?? "unknown"}`;
@@ -4180,7 +4611,7 @@ function comparePrerelease(a, b) {
4180
4611
  import { createHash } from "crypto";
4181
4612
  import { lstat, open as open3, readdir as readdir2 } from "fs/promises";
4182
4613
  import { constants as fsConstants } from "fs";
4183
- import { join as join11, resolve as pathResolve } from "path";
4614
+ import { join as join12, resolve as pathResolve } from "path";
4184
4615
  async function verifyBinary(check) {
4185
4616
  const fh = await open3(check.path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
4186
4617
  const hash = createHash("sha256");
@@ -4230,7 +4661,7 @@ async function auditNativeBindings(rootDir, expected) {
4230
4661
  return;
4231
4662
  }
4232
4663
  for (const entryName of entries) {
4233
- const full = pathResolve(join11(dir, entryName));
4664
+ const full = pathResolve(join12(dir, entryName));
4234
4665
  const entryStat = await lstat(full).catch(() => null);
4235
4666
  if (!entryStat)
4236
4667
  continue;
@@ -4271,8 +4702,8 @@ async function auditNativeBindings(rootDir, expected) {
4271
4702
  init_config_dir();
4272
4703
  init_process_manager();
4273
4704
  init_service_installer();
4274
- import { unlink as unlink6 } from "fs/promises";
4275
- import { join as join13 } from "path";
4705
+ import { unlink as unlink7 } from "fs/promises";
4706
+ import { join as join14 } from "path";
4276
4707
  async function getListenerStatus() {
4277
4708
  const pid = await readPid();
4278
4709
  if (pid !== null) {
@@ -4281,7 +4712,7 @@ async function getListenerStatus() {
4281
4712
  return { running: true, method: "pid", pid, serviceInstalled: serviceInstalled2 };
4282
4713
  }
4283
4714
  try {
4284
- await unlink6(join13(getConfigDir(), "listener.pid"));
4715
+ await unlink7(join14(getConfigDir(), "listener.pid"));
4285
4716
  } catch {
4286
4717
  }
4287
4718
  }
@@ -4326,11 +4757,11 @@ async function stopListener() {
4326
4757
 
4327
4758
  // ../listener/dist/mcp/bootstrap.js
4328
4759
  init_config_dir();
4329
- import { join as join21 } from "path";
4760
+ import { join as join22 } from "path";
4330
4761
 
4331
4762
  // ../listener/dist/lsp/workspace-session.js
4332
4763
  import { pathToFileURL } from "url";
4333
- import { readFile as readFile6 } from "fs/promises";
4764
+ import { readFile as readFile7 } from "fs/promises";
4334
4765
  import { resolve as pathResolve2, sep as pathSep, dirname as dirname7 } from "path";
4335
4766
 
4336
4767
  // ../listener/dist/lsp/lsp-client.js
@@ -4776,7 +5207,7 @@ var WorkspaceManager = class {
4776
5207
  }
4777
5208
  const absolute = this.joinPath(session.repoRoot, repoRelativePath);
4778
5209
  const p = (async () => {
4779
- const text = await readFile6(absolute, "utf-8");
5210
+ const text = await readFile7(absolute, "utf-8");
4780
5211
  session.client.notify("textDocument/didOpen", {
4781
5212
  textDocument: {
4782
5213
  uri,
@@ -5046,8 +5477,8 @@ function buildLspSpawnPath(basePath, nodeDir, lspBinDirs) {
5046
5477
 
5047
5478
  // ../listener/dist/mcp/graph-cache.js
5048
5479
  init_config_dir();
5049
- import { readFile as readFile8, stat as stat3 } from "fs/promises";
5050
- import { join as join19 } from "path";
5480
+ import { readFile as readFile9, stat as stat3 } from "fs/promises";
5481
+ import { join as join20 } from "path";
5051
5482
  var EVICT_DEBOUNCE_MS = 6e4;
5052
5483
  function assertSafeRepoId(repoId) {
5053
5484
  if (!repoId || typeof repoId !== "string") {
@@ -5062,13 +5493,13 @@ function assertSafeRepoId(repoId) {
5062
5493
  }
5063
5494
  function createGraphCache(options = {}) {
5064
5495
  const evictMs = options.evictDebounceMs ?? EVICT_DEBOUNCE_MS;
5065
- const resolvePath = options.resolveGraphPath ?? ((repoId) => join19(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5496
+ const resolvePath = options.resolveGraphPath ?? ((repoId) => join20(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
5066
5497
  const entries = /* @__PURE__ */ new Map();
5067
5498
  const inFlight = /* @__PURE__ */ new Map();
5068
5499
  async function loadFromDisk(repoId) {
5069
5500
  const graphPath = resolvePath(repoId);
5070
5501
  const s = await stat3(graphPath);
5071
- const body = await readFile8(graphPath, "utf-8");
5502
+ const body = await readFile9(graphPath, "utf-8");
5072
5503
  const graph = JSON.parse(body);
5073
5504
  if (graph.commitSha && Array.isArray(graph.edges)) {
5074
5505
  try {
@@ -5155,7 +5586,7 @@ function defaultRepoWiseHome() {
5155
5586
  }
5156
5587
 
5157
5588
  // ../listener/dist/mcp/graph-downloader.js
5158
- import { mkdir as mkdir9, rename as rename3, writeFile as writeFile9 } from "fs/promises";
5589
+ import { mkdir as mkdir10, rename as rename3, writeFile as writeFile10 } from "fs/promises";
5159
5590
  import { dirname as dirname9 } from "path";
5160
5591
  function createGraphDownloader(options) {
5161
5592
  const fetchFn = options.fetchImpl ?? fetch;
@@ -5207,9 +5638,9 @@ function createGraphDownloader(options) {
5207
5638
  message: `graph parse: ${err instanceof Error ? err.message : String(err)}`
5208
5639
  };
5209
5640
  }
5210
- await mkdir9(dirname9(options.targetPath), { recursive: true });
5641
+ await mkdir10(dirname9(options.targetPath), { recursive: true });
5211
5642
  const tmpPath = `${options.targetPath}.${Date.now()}.tmp`;
5212
- await writeFile9(tmpPath, body, { encoding: "utf-8", mode: 384 });
5643
+ await writeFile10(tmpPath, body, { encoding: "utf-8", mode: 384 });
5213
5644
  await rename3(tmpPath, options.targetPath);
5214
5645
  options.graphCache.invalidate(options.repoId);
5215
5646
  lastSha = serverSha;
@@ -5232,7 +5663,7 @@ function createGraphDownloader(options) {
5232
5663
 
5233
5664
  // ../listener/dist/mcp/log-encryption.js
5234
5665
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
5235
- import { mkdir as mkdir10, readFile as readFile9, stat as stat4, writeFile as writeFile10 } from "fs/promises";
5666
+ import { mkdir as mkdir11, readFile as readFile10, stat as stat4, writeFile as writeFile11 } from "fs/promises";
5236
5667
  import { dirname as dirname10 } from "path";
5237
5668
  var KEY_BYTES = 32;
5238
5669
  var IV_BYTES = 12;
@@ -5260,7 +5691,7 @@ function createFileKeyStore(keyPath) {
5260
5691
  return cached;
5261
5692
  let parsed = null;
5262
5693
  try {
5263
- const body = await readFile9(keyPath, "utf-8");
5694
+ const body = await readFile10(keyPath, "utf-8");
5264
5695
  const decoded = Buffer.from(body.trim(), "base64");
5265
5696
  if (decoded.length === KEY_BYTES)
5266
5697
  parsed = decoded;
@@ -5280,9 +5711,9 @@ function createFileKeyStore(keyPath) {
5280
5711
  return parsed;
5281
5712
  }
5282
5713
  const fresh = randomBytes(KEY_BYTES);
5283
- await mkdir10(dirname10(keyPath), { recursive: true });
5714
+ await mkdir11(dirname10(keyPath), { recursive: true });
5284
5715
  const tmp = `${keyPath}.tmp`;
5285
- await writeFile10(tmp, fresh.toString("base64") + "\n", {
5716
+ await writeFile11(tmp, fresh.toString("base64") + "\n", {
5286
5717
  encoding: "utf-8",
5287
5718
  mode: 384
5288
5719
  });
@@ -5321,7 +5752,7 @@ function decryptLine(encoded, key) {
5321
5752
  }
5322
5753
 
5323
5754
  // ../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";
5755
+ import { appendFile, mkdir as mkdir12, stat as stat5, unlink as unlink9, writeFile as writeFile12, readFile as readFile11 } from "fs/promises";
5325
5756
  import { dirname as dirname11 } from "path";
5326
5757
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
5327
5758
  function createMcpLogger(options) {
@@ -5336,7 +5767,7 @@ function createMcpLogger(options) {
5336
5767
  if (legacyPath === options.filePath)
5337
5768
  return;
5338
5769
  try {
5339
- await unlink8(legacyPath);
5770
+ await unlink9(legacyPath);
5340
5771
  console.log(`[mcp-logger] removed legacy plaintext log at ${legacyPath} (one-time migration)`);
5341
5772
  } catch {
5342
5773
  }
@@ -5345,7 +5776,7 @@ function createMcpLogger(options) {
5345
5776
  await migrateLegacyPlaintext();
5346
5777
  const key = await options.keyStore.getKey();
5347
5778
  const encoded = encryptLine(JSON.stringify(entry), key) + "\n";
5348
- await mkdir11(dirname11(options.filePath), { recursive: true });
5779
+ await mkdir12(dirname11(options.filePath), { recursive: true });
5349
5780
  await appendFile(options.filePath, encoded, { encoding: "utf-8", mode: 384 });
5350
5781
  try {
5351
5782
  const s = await stat5(options.filePath);
@@ -5367,18 +5798,18 @@ function createMcpLogger(options) {
5367
5798
  }
5368
5799
  async function rotate(path, size) {
5369
5800
  const dropAt = Math.floor(size * 0.25);
5370
- const body = await readFile10(path, "utf-8");
5801
+ const body = await readFile11(path, "utf-8");
5371
5802
  const idx = body.indexOf("\n", dropAt);
5372
5803
  if (idx < 0) {
5373
- await writeFile11(path, "", { encoding: "utf-8", mode: 384 });
5804
+ await writeFile12(path, "", { encoding: "utf-8", mode: 384 });
5374
5805
  return;
5375
5806
  }
5376
- await writeFile11(path, body.slice(idx + 1), { encoding: "utf-8", mode: 384 });
5807
+ await writeFile12(path, body.slice(idx + 1), { encoding: "utf-8", mode: 384 });
5377
5808
  }
5378
5809
 
5379
5810
  // ../listener/dist/mcp/mcp-log-uploader.js
5380
5811
  import { randomUUID } from "crypto";
5381
- import { readFile as readFile11, writeFile as writeFile12, mkdir as mkdir12 } from "fs/promises";
5812
+ import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir13 } from "fs/promises";
5382
5813
  import { dirname as dirname12 } from "path";
5383
5814
  var DEFAULT_MAX_ENTRIES = 5e3;
5384
5815
  var DEFAULT_MAX_BYTES2 = 1 * 1024 * 1024;
@@ -5404,7 +5835,7 @@ function createMcpLogUploader(options) {
5404
5835
  }
5405
5836
  let body;
5406
5837
  try {
5407
- body = await readFile11(options.logFilePath, "utf-8");
5838
+ body = await readFile12(options.logFilePath, "utf-8");
5408
5839
  } catch (err) {
5409
5840
  if (err.code === "ENOENT") {
5410
5841
  return { uploaded: 0, bytesAdvanced: 0 };
@@ -5521,7 +5952,7 @@ function pendingPath(watermarkPath) {
5521
5952
  }
5522
5953
  async function readPendingBatch(watermarkPath) {
5523
5954
  try {
5524
- const body = await readFile11(pendingPath(watermarkPath), "utf-8");
5955
+ const body = await readFile12(pendingPath(watermarkPath), "utf-8");
5525
5956
  const trimmed = body.trim();
5526
5957
  return /^[A-Za-z0-9-]{8,128}$/.test(trimmed) ? trimmed : null;
5527
5958
  } catch {
@@ -5529,19 +5960,19 @@ async function readPendingBatch(watermarkPath) {
5529
5960
  }
5530
5961
  }
5531
5962
  async function writePendingBatch(watermarkPath, batchId) {
5532
- await mkdir12(dirname12(watermarkPath), { recursive: true });
5533
- await writeFile12(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
5963
+ await mkdir13(dirname12(watermarkPath), { recursive: true });
5964
+ await writeFile13(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
5534
5965
  }
5535
5966
  async function clearPendingBatch(watermarkPath) {
5536
- const { unlink: unlink11 } = await import("fs/promises");
5967
+ const { unlink: unlink12 } = await import("fs/promises");
5537
5968
  try {
5538
- await unlink11(pendingPath(watermarkPath));
5969
+ await unlink12(pendingPath(watermarkPath));
5539
5970
  } catch {
5540
5971
  }
5541
5972
  }
5542
5973
  async function readWatermark(path) {
5543
5974
  try {
5544
- const body = await readFile11(path, "utf-8");
5975
+ const body = await readFile12(path, "utf-8");
5545
5976
  const parsed = Number.parseInt(body.trim(), 10);
5546
5977
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
5547
5978
  } catch (err) {
@@ -5551,15 +5982,15 @@ async function readWatermark(path) {
5551
5982
  }
5552
5983
  }
5553
5984
  async function writeWatermark(path, offset) {
5554
- await mkdir12(dirname12(path), { recursive: true });
5985
+ await mkdir13(dirname12(path), { recursive: true });
5555
5986
  const tmp = `${path}.tmp`;
5556
- await writeFile12(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
5987
+ await writeFile13(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
5557
5988
  const { rename: rename5 } = await import("fs/promises");
5558
5989
  await rename5(tmp, path);
5559
5990
  }
5560
5991
  async function isLocallyConsented(flagPath2) {
5561
5992
  try {
5562
- const body = await readFile11(flagPath2, "utf-8");
5993
+ const body = await readFile12(flagPath2, "utf-8");
5563
5994
  const parsed = JSON.parse(body);
5564
5995
  return parsed.enabled !== false;
5565
5996
  } catch {
@@ -5570,8 +6001,8 @@ async function isLocallyConsented(flagPath2) {
5570
6001
  // ../listener/dist/mcp/mcp-server.js
5571
6002
  init_config_dir();
5572
6003
  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";
6004
+ import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
6005
+ import { dirname as dirname13, join as join21 } from "path";
5575
6006
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
5576
6007
 
5577
6008
  // ../listener/dist/mcp/sanitize.js
@@ -7089,8 +7520,8 @@ async function readJson(req) {
7089
7520
  }
7090
7521
  }
7091
7522
  async function writeEndpointFile(path, endpoint, secret) {
7092
- await mkdir13(dirname13(path), { recursive: true });
7093
- await writeFile13(path, formatEndpointFile({ endpoint, secret }), {
7523
+ await mkdir14(dirname13(path), { recursive: true });
7524
+ await writeFile14(path, formatEndpointFile({ endpoint, secret }), {
7094
7525
  encoding: "utf-8",
7095
7526
  mode: 384
7096
7527
  });
@@ -7103,7 +7534,7 @@ function extractBearer(header) {
7103
7534
  return m ? m[1] : null;
7104
7535
  }
7105
7536
  function defaultEndpointFile() {
7106
- return join20(getConfigDir(), "listener.endpoint");
7537
+ return join21(getConfigDir(), "listener.endpoint");
7107
7538
  }
7108
7539
 
7109
7540
  // ../listener/dist/mcp/bootstrap.js
@@ -7111,7 +7542,7 @@ async function startMcp(options) {
7111
7542
  const disabled = process.env.REPOWISE_MCP_DISABLED === "true";
7112
7543
  const graphsDir = options.graphsDir ?? defaultGraphsDir();
7113
7544
  const graphCache = createGraphCache({
7114
- resolveGraphPath: (repoId) => join21(graphsDir, `${repoId}.json`)
7545
+ resolveGraphPath: (repoId) => join22(graphsDir, `${repoId}.json`)
7115
7546
  });
7116
7547
  if (disabled) {
7117
7548
  return {
@@ -7134,12 +7565,12 @@ async function startMcp(options) {
7134
7565
  }
7135
7566
  const firstRepoLocal = options.repos.find((r) => r.localPath)?.localPath;
7136
7567
  const mcpHome = defaultMcpHome();
7137
- const logFilePath = join21(mcpHome, "mcp-log.jsonl.enc");
7138
- const keyStore = createFileKeyStore(join21(mcpHome, "mcp-log.key"));
7568
+ const logFilePath = join22(mcpHome, "mcp-log.jsonl.enc");
7569
+ const keyStore = createFileKeyStore(join22(mcpHome, "mcp-log.key"));
7139
7570
  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");
7571
+ const flagFilePath = join22(mcpHome, "mcp-log.flag");
7572
+ const watermarkFilePath = join22(mcpHome, "mcp-log.watermark");
7573
+ const consentSentMarkerPath = join22(mcpHome, "mcp-log.consent-sent");
7143
7574
  let uploader = null;
7144
7575
  let lastConsentState = false;
7145
7576
  let serverConsentEnsuredThisProcess = false;
@@ -7186,7 +7617,7 @@ async function startMcp(options) {
7186
7617
  apiBaseUrl: repo.apiUrl,
7187
7618
  getAuthToken: options.getAuthToken,
7188
7619
  repoId: repo.repoId,
7189
- targetPath: join21(graphsDir, `${repo.repoId}.json`),
7620
+ targetPath: join22(graphsDir, `${repo.repoId}.json`),
7190
7621
  graphCache,
7191
7622
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7192
7623
  }));
@@ -7271,7 +7702,7 @@ async function startMcp(options) {
7271
7702
  apiBaseUrl: repo.apiUrl,
7272
7703
  getAuthToken: options.getAuthToken,
7273
7704
  repoId: repo.repoId,
7274
- targetPath: join21(graphsDir, `${repo.repoId}.json`),
7705
+ targetPath: join22(graphsDir, `${repo.repoId}.json`),
7275
7706
  graphCache,
7276
7707
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
7277
7708
  }));
@@ -7292,15 +7723,15 @@ async function startMcp(options) {
7292
7723
  };
7293
7724
  }
7294
7725
  function defaultGraphsDir() {
7295
- return join21(getConfigDir(), "graphs");
7726
+ return join22(getConfigDir(), "graphs");
7296
7727
  }
7297
7728
  function defaultMcpHome() {
7298
7729
  return getConfigDir();
7299
7730
  }
7300
7731
  async function logFileExists(path) {
7301
7732
  try {
7302
- const { stat: stat7 } = await import("fs/promises");
7303
- const s = await stat7(path);
7733
+ const { stat: stat8 } = await import("fs/promises");
7734
+ const s = await stat8(path);
7304
7735
  return s.size > 0;
7305
7736
  } catch {
7306
7737
  return false;
@@ -7333,10 +7764,10 @@ async function ensureServerConsent(opts) {
7333
7764
  });
7334
7765
  if (!res.ok)
7335
7766
  return;
7336
- const fs25 = await import("fs/promises");
7767
+ const fs30 = await import("fs/promises");
7337
7768
  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", {
7769
+ await fs30.mkdir(path.dirname(opts.markerPath), { recursive: true });
7770
+ await fs30.writeFile(opts.markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n", {
7340
7771
  encoding: "utf-8",
7341
7772
  mode: 384
7342
7773
  });
@@ -7346,11 +7777,11 @@ async function ensureServerConsent(opts) {
7346
7777
 
7347
7778
  // ../listener/dist/mcp/auto-config/index.js
7348
7779
  import { homedir as homedir5 } from "os";
7349
- import { basename as basename2 } from "path";
7780
+ import { basename as basename3 } from "path";
7350
7781
 
7351
7782
  // ../listener/dist/mcp/auto-config/writers/claude-code.js
7352
7783
  import { promises as fs6 } from "fs";
7353
- import { join as join22 } from "path";
7784
+ import { join as join23 } from "path";
7354
7785
 
7355
7786
  // ../listener/dist/mcp/auto-config/markers.js
7356
7787
  import { promises as fs5 } from "fs";
@@ -7430,10 +7861,10 @@ function safeExistingContent(path, current) {
7430
7861
  var claudeCodeWriter = {
7431
7862
  tool: "claude-code",
7432
7863
  async detect(repoRoot, home) {
7433
- return await fileExists2(join22(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
7864
+ return await fileExists2(join23(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
7434
7865
  },
7435
7866
  async write(ctx) {
7436
- const path = join22(ctx.repoRoot, ".mcp.json");
7867
+ const path = join23(ctx.repoRoot, ".mcp.json");
7437
7868
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7438
7869
  const status2 = await writeMergedConfig({
7439
7870
  path,
@@ -7443,7 +7874,7 @@ var claudeCodeWriter = {
7443
7874
  return { status: status2, path };
7444
7875
  },
7445
7876
  async remove(ctx) {
7446
- const path = join22(ctx.repoRoot, ".mcp.json");
7877
+ const path = join23(ctx.repoRoot, ".mcp.json");
7447
7878
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7448
7879
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7449
7880
  }
@@ -7457,31 +7888,23 @@ async function fileExists2(path) {
7457
7888
  }
7458
7889
  }
7459
7890
  async function hasClaudeBinary(home) {
7460
- return fileExists2(join22(home, ".claude", "claude.json"));
7891
+ return fileExists2(join23(home, ".claude", "claude.json"));
7461
7892
  }
7462
7893
 
7463
- // ../listener/dist/mcp/auto-config/writers/cline.js
7894
+ // ../listener/dist/mcp/auto-config/writers/claude-code-hooks.js
7464
7895
  import { promises as fs7 } from "fs";
7465
- import { join as join23 } from "path";
7466
- var clineWriter = {
7467
- tool: "cline",
7896
+ import { join as join24 } from "path";
7897
+ var claudeCodeHooksWriter = {
7898
+ tool: "claude-code-hooks",
7468
7899
  async detect(repoRoot, home) {
7469
- return await fileExists3(join23(repoRoot, ".clinerules")) || await fileExists3(join23(home, ".cline"));
7900
+ return await fileExists3(join24(repoRoot, "CLAUDE.md")) || await fileExists3(join24(home, ".claude.json"));
7470
7901
  },
7471
7902
  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 };
7903
+ const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context");
7904
+ return { status: result.status, path: join24(ctx.repoRoot, result.relPath) };
7480
7905
  },
7481
7906
  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}`);
7907
+ await removeClaudeHooksFromRepo(ctx.repoRoot);
7485
7908
  }
7486
7909
  };
7487
7910
  async function fileExists3(path) {
@@ -7493,16 +7916,16 @@ async function fileExists3(path) {
7493
7916
  }
7494
7917
  }
7495
7918
 
7496
- // ../listener/dist/mcp/auto-config/writers/codex.js
7919
+ // ../listener/dist/mcp/auto-config/writers/cline.js
7497
7920
  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"));
7921
+ import { join as join25 } from "path";
7922
+ var clineWriter = {
7923
+ tool: "cline",
7924
+ async detect(repoRoot, home) {
7925
+ return await fileExists4(join25(repoRoot, ".clinerules")) || await fileExists4(join25(home, ".cline"));
7503
7926
  },
7504
7927
  async write(ctx) {
7505
- const path = join24(ctx.home, ".codex", "mcp.json");
7928
+ const path = join25(ctx.home, ".cline", "mcp.json");
7506
7929
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7507
7930
  const status2 = await writeMergedConfig({
7508
7931
  path,
@@ -7512,7 +7935,7 @@ var codexWriter = {
7512
7935
  return { status: status2, path };
7513
7936
  },
7514
7937
  async remove(ctx) {
7515
- const path = join24(ctx.home, ".codex", "mcp.json");
7938
+ const path = join25(ctx.home, ".cline", "mcp.json");
7516
7939
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7517
7940
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7518
7941
  }
@@ -7526,30 +7949,64 @@ async function fileExists4(path) {
7526
7949
  }
7527
7950
  }
7528
7951
 
7529
- // ../listener/dist/mcp/auto-config/writers/copilot.js
7952
+ // ../listener/dist/mcp/auto-config/writers/cline-hooks.js
7530
7953
  import { promises as fs9 } from "fs";
7531
- import { join as join25 } from "path";
7532
- var copilotWriter = {
7533
- tool: "copilot",
7954
+ import { join as join26 } from "path";
7955
+ var SCRIPT_REL = ".clinerules/hooks/UserPromptSubmit";
7956
+ var OWNERSHIP_MARKER = "managed by RepoWise";
7957
+ var clineHooksWriter = {
7958
+ tool: "cline-hooks",
7534
7959
  async detect(repoRoot) {
7535
- return fileExists5(join25(repoRoot, ".vscode"));
7960
+ return fileExists5(join26(repoRoot, ".clinerules"));
7536
7961
  },
7537
7962
  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] }
7963
+ if (await isGitTracked(ctx.repoRoot, SCRIPT_REL)) {
7964
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SCRIPT_REL] });
7965
+ return {
7966
+ status: "skipped",
7967
+ reason: `${SCRIPT_REL} is git-tracked; update it manually to opt in`
7968
+ };
7969
+ }
7970
+ const path = join26(ctx.repoRoot, SCRIPT_REL);
7971
+ const next = buildClineHookScript({
7972
+ repoName: ctx.repoName,
7973
+ contextFolder: ctx.contextFolder ?? "repowise-context"
7544
7974
  });
7545
- return { status: status2, path };
7975
+ const existing = await readOrNull(path);
7976
+ if (existing !== null && !existing.includes(OWNERSHIP_MARKER)) {
7977
+ return {
7978
+ status: "skipped",
7979
+ reason: `${SCRIPT_REL} exists and is user-authored; not overwriting`
7980
+ };
7981
+ }
7982
+ if (existing === next) {
7983
+ await ensureExecutable(path);
7984
+ return { status: "unchanged", path };
7985
+ }
7986
+ await fs9.mkdir(join26(ctx.repoRoot, ".clinerules", "hooks"), { recursive: true });
7987
+ await fs9.writeFile(path, next, { encoding: "utf-8", mode: 493 });
7988
+ await ensureExecutable(path);
7989
+ await applyGitignoreChanges(ctx.repoRoot, { add: [SCRIPT_REL] });
7990
+ return { status: "written", path };
7546
7991
  },
7547
7992
  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}`);
7993
+ const path = join26(ctx.repoRoot, SCRIPT_REL);
7994
+ const existing = await readOrNull(path);
7995
+ if (existing !== null && existing.includes(OWNERSHIP_MARKER)) {
7996
+ await fs9.unlink(path);
7997
+ }
7998
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SCRIPT_REL] });
7551
7999
  }
7552
8000
  };
8001
+ async function ensureExecutable(path) {
8002
+ try {
8003
+ const stat8 = await fs9.stat(path);
8004
+ if ((stat8.mode & 73) === 0) {
8005
+ await fs9.chmod(path, 493);
8006
+ }
8007
+ } catch {
8008
+ }
8009
+ }
7553
8010
  async function fileExists5(path) {
7554
8011
  try {
7555
8012
  await fs9.access(path);
@@ -7558,17 +8015,24 @@ async function fileExists5(path) {
7558
8015
  return false;
7559
8016
  }
7560
8017
  }
8018
+ async function readOrNull(path) {
8019
+ try {
8020
+ return await fs9.readFile(path, "utf-8");
8021
+ } catch {
8022
+ return null;
8023
+ }
8024
+ }
7561
8025
 
7562
- // ../listener/dist/mcp/auto-config/writers/cursor.js
8026
+ // ../listener/dist/mcp/auto-config/writers/codex.js
7563
8027
  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"));
8028
+ import { join as join27 } from "path";
8029
+ var codexWriter = {
8030
+ tool: "codex",
8031
+ async detect(_repoRoot, home) {
8032
+ return fileExists6(join27(home, ".codex"));
7569
8033
  },
7570
8034
  async write(ctx) {
7571
- const path = join26(ctx.repoRoot, ".cursor", "mcp.json");
8035
+ const path = join27(ctx.home, ".codex", "mcp.json");
7572
8036
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7573
8037
  const status2 = await writeMergedConfig({
7574
8038
  path,
@@ -7578,7 +8042,7 @@ var cursorWriter = {
7578
8042
  return { status: status2, path };
7579
8043
  },
7580
8044
  async remove(ctx) {
7581
- const path = join26(ctx.repoRoot, ".cursor", "mcp.json");
8045
+ const path = join27(ctx.home, ".codex", "mcp.json");
7582
8046
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7583
8047
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7584
8048
  }
@@ -7592,28 +8056,54 @@ async function fileExists6(path) {
7592
8056
  }
7593
8057
  }
7594
8058
 
7595
- // ../listener/dist/mcp/auto-config/writers/gemini-cli.js
8059
+ // ../listener/dist/mcp/auto-config/writers/codex-hooks.js
7596
8060
  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"));
8061
+ import { join as join28 } from "path";
8062
+ var HOOKS_REL = ".codex/hooks.json";
8063
+ var codexHooksWriter = {
8064
+ tool: "codex-hooks",
8065
+ async detect(repoRoot, home) {
8066
+ return await fileExists7(join28(home, ".codex")) || await fileExists7(join28(repoRoot, ".codex"));
7602
8067
  },
7603
8068
  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] }
8069
+ const repoSignal = await fileExists7(join28(ctx.repoRoot, ".codex"));
8070
+ const selected = ctx.selectedTools?.includes("codex") ?? false;
8071
+ if (!repoSignal && !selected) {
8072
+ return { status: "skipped", reason: "no repo-scoped Codex signal and not selected" };
8073
+ }
8074
+ if (await isGitTracked(ctx.repoRoot, HOOKS_REL)) {
8075
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL] });
8076
+ return {
8077
+ status: "skipped",
8078
+ reason: `${HOOKS_REL} is git-tracked; add the RepoWise UserPromptSubmit hook manually to opt in`
8079
+ };
8080
+ }
8081
+ const path = join28(ctx.repoRoot, HOOKS_REL);
8082
+ const existing = await readOrNull2(path);
8083
+ const merged = mergeCodexHookSettings(existing, {
8084
+ repoName: ctx.repoName,
8085
+ contextFolder: ctx.contextFolder ?? "repowise-context"
7610
8086
  });
7611
- return { status: status2, path };
8087
+ if (merged === existing) {
8088
+ return { status: "unchanged", path };
8089
+ }
8090
+ await fs11.mkdir(join28(ctx.repoRoot, ".codex"), { recursive: true });
8091
+ await fs11.writeFile(path, merged, "utf-8");
8092
+ await applyGitignoreChanges(ctx.repoRoot, { add: [HOOKS_REL] });
8093
+ return { status: "written", path };
7612
8094
  },
7613
8095
  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}`);
8096
+ const path = join28(ctx.repoRoot, HOOKS_REL);
8097
+ const existing = await readOrNull2(path);
8098
+ if (existing !== null) {
8099
+ const next = removeCodexHookSettings(existing);
8100
+ if (next === null) {
8101
+ await fs11.unlink(path);
8102
+ } else if (next !== existing) {
8103
+ await fs11.writeFile(path, next, "utf-8");
8104
+ }
8105
+ }
8106
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL] });
7617
8107
  }
7618
8108
  };
7619
8109
  async function fileExists7(path) {
@@ -7624,19 +8114,24 @@ async function fileExists7(path) {
7624
8114
  return false;
7625
8115
  }
7626
8116
  }
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"));
8117
+ async function readOrNull2(path) {
8118
+ try {
8119
+ return await fs11.readFile(path, "utf-8");
8120
+ } catch {
8121
+ return null;
8122
+ }
8123
+ }
8124
+
8125
+ // ../listener/dist/mcp/auto-config/writers/copilot.js
8126
+ import { promises as fs12 } from "fs";
8127
+ import { join as join29 } from "path";
8128
+ var copilotWriter = {
8129
+ tool: "copilot",
8130
+ async detect(repoRoot) {
8131
+ return fileExists8(join29(repoRoot, ".vscode"));
7635
8132
  },
7636
8133
  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");
8134
+ const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
7640
8135
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7641
8136
  const status2 = await writeMergedConfig({
7642
8137
  path,
@@ -7646,12 +8141,9 @@ var rooWriter = {
7646
8141
  return { status: status2, path };
7647
8142
  },
7648
8143
  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}`);
8144
+ const path = join29(ctx.repoRoot, ".vscode", "mcp.json");
8145
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8146
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
7655
8147
  }
7656
8148
  };
7657
8149
  async function fileExists8(path) {
@@ -7663,16 +8155,254 @@ async function fileExists8(path) {
7663
8155
  }
7664
8156
  }
7665
8157
 
7666
- // ../listener/dist/mcp/auto-config/writers/windsurf.js
8158
+ // ../listener/dist/mcp/auto-config/writers/copilot-hooks.js
7667
8159
  import { promises as fs13 } from "fs";
7668
- import { join as join29 } from "path";
8160
+ import { join as join30 } from "path";
8161
+ var HOOKS_REL2 = ".github/hooks/repowise.json";
8162
+ var copilotHooksWriter = {
8163
+ tool: "copilot-hooks",
8164
+ async detect(repoRoot) {
8165
+ return fileExists9(join30(repoRoot, ".github", "copilot-instructions.md"));
8166
+ },
8167
+ async write(ctx) {
8168
+ const path = join30(ctx.repoRoot, HOOKS_REL2);
8169
+ const next = buildCopilotHooksFile({
8170
+ repoName: ctx.repoName,
8171
+ contextFolder: ctx.contextFolder ?? "repowise-context"
8172
+ });
8173
+ const existing = await readOrNull3(path);
8174
+ if (await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
8175
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL2] });
8176
+ if (existing === next) {
8177
+ return { status: "unchanged", path };
8178
+ }
8179
+ return {
8180
+ status: "skipped",
8181
+ reason: `${HOOKS_REL2} is committed; not modifying a tracked file`
8182
+ };
8183
+ }
8184
+ if (existing === next) {
8185
+ return { status: "unchanged", path };
8186
+ }
8187
+ await fs13.mkdir(join30(ctx.repoRoot, ".github", "hooks"), { recursive: true });
8188
+ await fs13.writeFile(path, next, "utf-8");
8189
+ const userSelected = ctx.selectedTools?.includes("copilot") ?? false;
8190
+ await applyGitignoreChanges(ctx.repoRoot, userSelected ? { remove: [HOOKS_REL2] } : { add: [HOOKS_REL2] });
8191
+ return { status: "written", path };
8192
+ },
8193
+ async remove(ctx) {
8194
+ const path = join30(ctx.repoRoot, HOOKS_REL2);
8195
+ if (!await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
8196
+ await fs13.unlink(path).catch(() => {
8197
+ });
8198
+ }
8199
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [HOOKS_REL2] });
8200
+ }
8201
+ };
8202
+ async function fileExists9(path) {
8203
+ try {
8204
+ await fs13.access(path);
8205
+ return true;
8206
+ } catch {
8207
+ return false;
8208
+ }
8209
+ }
8210
+ async function readOrNull3(path) {
8211
+ try {
8212
+ return await fs13.readFile(path, "utf-8");
8213
+ } catch {
8214
+ return null;
8215
+ }
8216
+ }
8217
+
8218
+ // ../listener/dist/mcp/auto-config/writers/cursor.js
8219
+ import { promises as fs14 } from "fs";
8220
+ import { join as join31 } from "path";
8221
+ var cursorWriter = {
8222
+ tool: "cursor",
8223
+ async detect(repoRoot) {
8224
+ return await fileExists10(join31(repoRoot, ".cursor")) || await fileExists10(join31(repoRoot, ".cursorrules"));
8225
+ },
8226
+ async write(ctx) {
8227
+ const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8228
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8229
+ const status2 = await writeMergedConfig({
8230
+ path,
8231
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
8232
+ spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8233
+ });
8234
+ return { status: status2, path };
8235
+ },
8236
+ async remove(ctx) {
8237
+ const path = join31(ctx.repoRoot, ".cursor", "mcp.json");
8238
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8239
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8240
+ }
8241
+ };
8242
+ async function fileExists10(path) {
8243
+ try {
8244
+ await fs14.access(path);
8245
+ return true;
8246
+ } catch {
8247
+ return false;
8248
+ }
8249
+ }
8250
+
8251
+ // ../listener/dist/mcp/auto-config/writers/gemini-cli.js
8252
+ import { promises as fs15 } from "fs";
8253
+ import { join as join32 } from "path";
8254
+ var geminiCliWriter = {
8255
+ tool: "gemini-cli",
8256
+ async detect(_repoRoot, home) {
8257
+ return fileExists11(join32(home, ".gemini"));
8258
+ },
8259
+ async write(ctx) {
8260
+ const path = join32(ctx.home, ".gemini", "settings.json");
8261
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8262
+ const status2 = await writeMergedConfig({
8263
+ path,
8264
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
8265
+ spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
8266
+ });
8267
+ return { status: status2, path };
8268
+ },
8269
+ async remove(ctx) {
8270
+ const path = join32(ctx.home, ".gemini", "settings.json");
8271
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
8272
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
8273
+ }
8274
+ };
8275
+ async function fileExists11(path) {
8276
+ try {
8277
+ await fs15.access(path);
8278
+ return true;
8279
+ } catch {
8280
+ return false;
8281
+ }
8282
+ }
8283
+
8284
+ // ../listener/dist/mcp/auto-config/writers/gemini-hooks.js
8285
+ import { promises as fs16 } from "fs";
8286
+ import { join as join33 } from "path";
8287
+ var SETTINGS_REL = ".gemini/settings.json";
8288
+ var geminiHooksWriter = {
8289
+ tool: "gemini-hooks",
8290
+ async detect(repoRoot, home) {
8291
+ return await fileExists12(join33(home, ".gemini")) || await fileExists12(join33(repoRoot, ".gemini"));
8292
+ },
8293
+ async write(ctx) {
8294
+ const repoSignal = await fileExists12(join33(ctx.repoRoot, ".gemini")) || await fileExists12(join33(ctx.repoRoot, "GEMINI.md"));
8295
+ const selected = ctx.selectedTools?.includes("gemini") ?? false;
8296
+ if (!repoSignal && !selected) {
8297
+ return { status: "skipped", reason: "no repo-scoped Gemini signal and not selected" };
8298
+ }
8299
+ if (await isGitTracked(ctx.repoRoot, SETTINGS_REL)) {
8300
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SETTINGS_REL] });
8301
+ return {
8302
+ status: "skipped",
8303
+ reason: `${SETTINGS_REL} is git-tracked; add the RepoWise BeforeAgent hook manually to opt in`
8304
+ };
8305
+ }
8306
+ const path = join33(ctx.repoRoot, SETTINGS_REL);
8307
+ const existing = await readOrNull4(path);
8308
+ const merged = mergeGeminiHookSettings(existing, {
8309
+ repoName: ctx.repoName,
8310
+ contextFolder: ctx.contextFolder ?? "repowise-context"
8311
+ });
8312
+ if (merged === existing) {
8313
+ return { status: "unchanged", path };
8314
+ }
8315
+ await fs16.mkdir(join33(ctx.repoRoot, ".gemini"), { recursive: true });
8316
+ await fs16.writeFile(path, merged, "utf-8");
8317
+ await applyGitignoreChanges(ctx.repoRoot, { add: [SETTINGS_REL] });
8318
+ return { status: "written", path };
8319
+ },
8320
+ async remove(ctx) {
8321
+ const path = join33(ctx.repoRoot, SETTINGS_REL);
8322
+ const existing = await readOrNull4(path);
8323
+ if (existing !== null) {
8324
+ const next = removeGeminiHookSettings(existing);
8325
+ if (next === null) {
8326
+ await fs16.unlink(path);
8327
+ } else if (next !== existing) {
8328
+ await fs16.writeFile(path, next, "utf-8");
8329
+ }
8330
+ }
8331
+ await applyGitignoreChanges(ctx.repoRoot, { remove: [SETTINGS_REL] });
8332
+ }
8333
+ };
8334
+ async function fileExists12(path) {
8335
+ try {
8336
+ await fs16.access(path);
8337
+ return true;
8338
+ } catch {
8339
+ return false;
8340
+ }
8341
+ }
8342
+ async function readOrNull4(path) {
8343
+ try {
8344
+ return await fs16.readFile(path, "utf-8");
8345
+ } catch {
8346
+ return null;
8347
+ }
8348
+ }
8349
+
8350
+ // ../listener/dist/mcp/auto-config/writers/roo.js
8351
+ import { promises as fs17 } from "fs";
8352
+ import { join as join34 } from "path";
8353
+ var rooWriter = {
8354
+ tool: "roo",
8355
+ async detect(repoRoot, home) {
8356
+ return await fileExists13(join34(repoRoot, ".roo")) || await fileExists13(join34(home, ".roo"));
8357
+ },
8358
+ async write(ctx) {
8359
+ const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8360
+ const before = await readOrNull5(repoConfig);
8361
+ await this.remove(ctx);
8362
+ const after = await readOrNull5(repoConfig);
8363
+ return {
8364
+ status: before === after ? "unchanged" : "written",
8365
+ path: repoConfig
8366
+ };
8367
+ },
8368
+ async remove(ctx) {
8369
+ const repoConfig = join34(ctx.repoRoot, ".roo", "mcp.json");
8370
+ const homeConfig = join34(ctx.home, ".roo", "mcp.json");
8371
+ const homeSettings = join34(ctx.home, ".roo", "settings.json");
8372
+ for (const path of [repoConfig, homeConfig, homeSettings]) {
8373
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`).catch(() => {
8374
+ });
8375
+ await removeFromConfig(path, `repowise-${ctx.repoId}`).catch(() => {
8376
+ });
8377
+ }
8378
+ }
8379
+ };
8380
+ async function fileExists13(path) {
8381
+ try {
8382
+ await fs17.access(path);
8383
+ return true;
8384
+ } catch {
8385
+ return false;
8386
+ }
8387
+ }
8388
+ async function readOrNull5(path) {
8389
+ try {
8390
+ return await fs17.readFile(path, "utf-8");
8391
+ } catch {
8392
+ return null;
8393
+ }
8394
+ }
8395
+
8396
+ // ../listener/dist/mcp/auto-config/writers/windsurf.js
8397
+ import { promises as fs18 } from "fs";
8398
+ import { join as join35 } from "path";
7669
8399
  var windsurfWriter = {
7670
8400
  tool: "windsurf",
7671
8401
  async detect(_repoRoot, home) {
7672
- return fileExists9(join29(home, ".codeium", "windsurf"));
8402
+ return fileExists14(join35(home, ".codeium", "windsurf"));
7673
8403
  },
7674
8404
  async write(ctx) {
7675
- const path = join29(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8405
+ const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
7676
8406
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7677
8407
  const status2 = await writeMergedConfig({
7678
8408
  path,
@@ -7682,14 +8412,14 @@ var windsurfWriter = {
7682
8412
  return { status: status2, path };
7683
8413
  },
7684
8414
  async remove(ctx) {
7685
- const path = join29(ctx.home, ".codeium", "windsurf", "mcp_config.json");
8415
+ const path = join35(ctx.home, ".codeium", "windsurf", "mcp_config.json");
7686
8416
  await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
7687
8417
  await removeFromConfig(path, `repowise-${ctx.repoId}`);
7688
8418
  }
7689
8419
  };
7690
- async function fileExists9(path) {
8420
+ async function fileExists14(path) {
7691
8421
  try {
7692
- await fs13.access(path);
8422
+ await fs18.access(path);
7693
8423
  return true;
7694
8424
  } catch {
7695
8425
  return false;
@@ -7699,11 +8429,16 @@ async function fileExists9(path) {
7699
8429
  // ../listener/dist/mcp/auto-config/writers/index.js
7700
8430
  var WRITERS = [
7701
8431
  claudeCodeWriter,
8432
+ claudeCodeHooksWriter,
7702
8433
  clineWriter,
8434
+ clineHooksWriter,
7703
8435
  codexWriter,
8436
+ codexHooksWriter,
7704
8437
  copilotWriter,
8438
+ copilotHooksWriter,
7705
8439
  cursorWriter,
7706
8440
  geminiCliWriter,
8441
+ geminiHooksWriter,
7707
8442
  rooWriter,
7708
8443
  windsurfWriter
7709
8444
  ];
@@ -7732,10 +8467,12 @@ async function runAutoConfig(opts) {
7732
8467
  try {
7733
8468
  const outcome = await writer.write({
7734
8469
  repoRoot: opts.repoRoot,
7735
- repoName: basename2(opts.repoRoot),
8470
+ repoName: basename3(opts.repoRoot),
7736
8471
  repoId: opts.repoId,
7737
8472
  shimCmd: opts.shimCmd,
7738
- home
8473
+ home,
8474
+ contextFolder: opts.contextFolder,
8475
+ selectedTools: opts.selectedTools
7739
8476
  });
7740
8477
  results.push({ tool: writer.tool, detected: true, outcome });
7741
8478
  } catch (err) {
@@ -7755,8 +8492,8 @@ init_installer();
7755
8492
  // ../listener/dist/lsp/warm.js
7756
8493
  init_registry();
7757
8494
  init_lsp_tools();
7758
- import { promises as fs14 } from "fs";
7759
- import { join as join30 } from "path";
8495
+ import { promises as fs19 } from "fs";
8496
+ import { join as join36 } from "path";
7760
8497
  async function collectRepresentativeFiles(repoRoot) {
7761
8498
  const reps = /* @__PURE__ */ new Map();
7762
8499
  let inspected = 0;
@@ -7766,7 +8503,7 @@ async function collectRepresentativeFiles(repoRoot) {
7766
8503
  return;
7767
8504
  let entries;
7768
8505
  try {
7769
- entries = await fs14.readdir(dir);
8506
+ entries = await fs19.readdir(dir);
7770
8507
  } catch {
7771
8508
  return;
7772
8509
  }
@@ -7788,7 +8525,7 @@ async function collectRepresentativeFiles(repoRoot) {
7788
8525
  await inspect(repoRoot, "");
7789
8526
  let topEntries = [];
7790
8527
  try {
7791
- topEntries = await fs14.readdir(repoRoot);
8528
+ topEntries = await fs19.readdir(repoRoot);
7792
8529
  } catch {
7793
8530
  return reps;
7794
8531
  }
@@ -7797,10 +8534,10 @@ async function collectRepresentativeFiles(repoRoot) {
7797
8534
  continue;
7798
8535
  if (name === "node_modules" || name === "dist" || name === "build")
7799
8536
  continue;
7800
- const sub = join30(repoRoot, name);
8537
+ const sub = join36(repoRoot, name);
7801
8538
  try {
7802
- const stat7 = await fs14.stat(sub);
7803
- if (stat7.isDirectory())
8539
+ const stat8 = await fs19.stat(sub);
8540
+ if (stat8.isDirectory())
7804
8541
  await inspect(sub, name);
7805
8542
  } catch {
7806
8543
  }
@@ -7813,8 +8550,8 @@ async function warmReposLsp(repos, workspaces, lspOverrides, isAvailable) {
7813
8550
  if (!repoRoot)
7814
8551
  continue;
7815
8552
  try {
7816
- const stat7 = await fs14.stat(repoRoot);
7817
- if (!stat7.isDirectory())
8553
+ const stat8 = await fs19.stat(repoRoot);
8554
+ if (!stat8.isDirectory())
7818
8555
  continue;
7819
8556
  } catch {
7820
8557
  continue;
@@ -7842,12 +8579,12 @@ async function warmReposLsp(repos, workspaces, lspOverrides, isAvailable) {
7842
8579
 
7843
8580
  // ../listener/dist/lib/workspace-prep.js
7844
8581
  init_config_dir();
7845
- import { promises as fs15 } from "fs";
7846
- import { spawn as spawn9, execFile as execFile4 } from "child_process";
8582
+ import { promises as fs20 } from "fs";
8583
+ import { spawn as spawn9, execFile as execFile5 } from "child_process";
7847
8584
  import { createWriteStream as createWriteStream2 } from "fs";
7848
- import { join as join31 } from "path";
8585
+ import { join as join37 } from "path";
7849
8586
  import { promisify as promisify3 } from "util";
7850
- var execFileAsync3 = promisify3(execFile4);
8587
+ var execFileAsync3 = promisify3(execFile5);
7851
8588
  async function isCommandOnPath2(cmd) {
7852
8589
  try {
7853
8590
  await execFileAsync3("which", [cmd], { timeout: 2e3 });
@@ -7879,7 +8616,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7879
8616
  ".repowise/compile_commands.json"
7880
8617
  ];
7881
8618
  for (const c of candidates) {
7882
- if (await fileExists10(repoRoot, c))
8619
+ if (await fileExists15(repoRoot, c))
7883
8620
  return false;
7884
8621
  }
7885
8622
  const sources = [];
@@ -7899,7 +8636,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7899
8636
  return;
7900
8637
  let ents;
7901
8638
  try {
7902
- ents = await fs15.readdir(dir, { withFileTypes: true });
8639
+ ents = await fs20.readdir(dir, { withFileTypes: true });
7903
8640
  } catch {
7904
8641
  return;
7905
8642
  }
@@ -7909,7 +8646,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7909
8646
  if (e.isDirectory()) {
7910
8647
  if (skip.has(e.name) || e.name.startsWith("."))
7911
8648
  continue;
7912
- await rec(join31(dir, e.name));
8649
+ await rec(join37(dir, e.name));
7913
8650
  continue;
7914
8651
  }
7915
8652
  const dot = e.name.lastIndexOf(".");
@@ -7918,7 +8655,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7918
8655
  const ext = e.name.slice(dot);
7919
8656
  if (!C_EXTS.has(ext) && !CPP_EXTS.has(ext))
7920
8657
  continue;
7921
- sources.push({ path: join31(dir, e.name), ext });
8658
+ sources.push({ path: join37(dir, e.name), ext });
7922
8659
  }
7923
8660
  }
7924
8661
  await rec(repoRoot);
@@ -7934,18 +8671,18 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
7934
8671
  command: `${driver} ${std} -I. -I./include -c "${src}"`
7935
8672
  };
7936
8673
  });
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));
8674
+ const outDir = join37(repoRoot, ".repowise");
8675
+ await fs20.mkdir(outDir, { recursive: true });
8676
+ const outPath = join37(outDir, "compile_commands.json");
8677
+ await fs20.writeFile(outPath, JSON.stringify(entries, null, 2));
7941
8678
  const capHit = sources.length >= MAX_STUB_SOURCES;
7942
8679
  console.log(`[workspace-prep] ${repoId}: wrote ${entries.length.toString()}-entry compile_commands stub to .repowise/${capHit ? " (cap reached \u2014 additional sources truncated)" : ""}`);
7943
8680
  return true;
7944
8681
  }
7945
8682
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
7946
- async function fileExists10(repoRoot, name) {
8683
+ async function fileExists15(repoRoot, name) {
7947
8684
  try {
7948
- await fs15.access(join31(repoRoot, name));
8685
+ await fs20.access(join37(repoRoot, name));
7949
8686
  return true;
7950
8687
  } catch {
7951
8688
  return false;
@@ -7953,10 +8690,10 @@ async function fileExists10(repoRoot, name) {
7953
8690
  }
7954
8691
  async function detectWorkspaceDeps(repoRoot) {
7955
8692
  const missing = [];
7956
- if (await fileExists10(repoRoot, "package.json")) {
8693
+ if (await fileExists15(repoRoot, "package.json")) {
7957
8694
  let pkgRaw = "";
7958
8695
  try {
7959
- pkgRaw = await fs15.readFile(join31(repoRoot, "package.json"), "utf8");
8696
+ pkgRaw = await fs20.readFile(join37(repoRoot, "package.json"), "utf8");
7960
8697
  } catch {
7961
8698
  }
7962
8699
  if (pkgRaw) {
@@ -7968,16 +8705,16 @@ async function detectWorkspaceDeps(repoRoot) {
7968
8705
  } catch {
7969
8706
  }
7970
8707
  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");
8708
+ 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
8709
  if (!installed) {
7973
8710
  let command = ["npm", "install", "--no-audit", "--no-fund"];
7974
- if (await fileExists10(repoRoot, "pnpm-lock.yaml")) {
8711
+ if (await fileExists15(repoRoot, "pnpm-lock.yaml")) {
7975
8712
  command = ["pnpm", "install", "--frozen-lockfile"];
7976
- } else if (await fileExists10(repoRoot, "yarn.lock")) {
8713
+ } else if (await fileExists15(repoRoot, "yarn.lock")) {
7977
8714
  command = ["yarn", "install", "--frozen-lockfile"];
7978
- } else if (await fileExists10(repoRoot, "bun.lock") || await fileExists10(repoRoot, "bun.lockb")) {
8715
+ } else if (await fileExists15(repoRoot, "bun.lock") || await fileExists15(repoRoot, "bun.lockb")) {
7979
8716
  command = ["bun", "install", "--frozen-lockfile"];
7980
- } else if (await fileExists10(repoRoot, "package-lock.json")) {
8717
+ } else if (await fileExists15(repoRoot, "package-lock.json")) {
7981
8718
  command = ["npm", "ci"];
7982
8719
  }
7983
8720
  missing.push({
@@ -7990,8 +8727,8 @@ async function detectWorkspaceDeps(repoRoot) {
7990
8727
  }
7991
8728
  }
7992
8729
  }
7993
- if (await fileExists10(repoRoot, "Gemfile")) {
7994
- const installed = await fileExists10(repoRoot, "Gemfile.lock") && (await fileExists10(repoRoot, "vendor/bundle") || await fileExists10(repoRoot, ".bundle/config"));
8730
+ if (await fileExists15(repoRoot, "Gemfile")) {
8731
+ const installed = await fileExists15(repoRoot, "Gemfile.lock") && (await fileExists15(repoRoot, "vendor/bundle") || await fileExists15(repoRoot, ".bundle/config"));
7995
8732
  if (!installed) {
7996
8733
  missing.push({
7997
8734
  ecosystem: "bundler",
@@ -8001,7 +8738,7 @@ async function detectWorkspaceDeps(repoRoot) {
8001
8738
  });
8002
8739
  }
8003
8740
  }
8004
- if (await fileExists10(repoRoot, "Cargo.toml")) {
8741
+ if (await fileExists15(repoRoot, "Cargo.toml")) {
8005
8742
  missing.push({
8006
8743
  ecosystem: "cargo",
8007
8744
  artefactName: "cargo registry cache",
@@ -8009,8 +8746,8 @@ async function detectWorkspaceDeps(repoRoot) {
8009
8746
  command: ["cargo", "metadata", "--format-version", "1", "--offline=false"]
8010
8747
  });
8011
8748
  }
8012
- if (await fileExists10(repoRoot, "pubspec.yaml")) {
8013
- if (!await fileExists10(repoRoot, ".dart_tool/package_config.json")) {
8749
+ if (await fileExists15(repoRoot, "pubspec.yaml")) {
8750
+ if (!await fileExists15(repoRoot, ".dart_tool/package_config.json")) {
8014
8751
  missing.push({
8015
8752
  ecosystem: "dart-pub",
8016
8753
  artefactName: ".dart_tool/",
@@ -8019,7 +8756,7 @@ async function detectWorkspaceDeps(repoRoot) {
8019
8756
  });
8020
8757
  }
8021
8758
  }
8022
- if (await fileExists10(repoRoot, "pom.xml")) {
8759
+ if (await fileExists15(repoRoot, "pom.xml")) {
8023
8760
  missing.push({
8024
8761
  ecosystem: "maven",
8025
8762
  artefactName: "maven (manual)",
@@ -8027,7 +8764,7 @@ async function detectWorkspaceDeps(repoRoot) {
8027
8764
  command: []
8028
8765
  });
8029
8766
  }
8030
- if (await fileExists10(repoRoot, "build.gradle") || await fileExists10(repoRoot, "build.gradle.kts")) {
8767
+ if (await fileExists15(repoRoot, "build.gradle") || await fileExists15(repoRoot, "build.gradle.kts")) {
8031
8768
  missing.push({
8032
8769
  ecosystem: "gradle",
8033
8770
  artefactName: "gradle (manual)",
@@ -8035,8 +8772,8 @@ async function detectWorkspaceDeps(repoRoot) {
8035
8772
  command: []
8036
8773
  });
8037
8774
  }
8038
- if (await fileExists10(repoRoot, "build.sbt")) {
8039
- const hasBloop = await fileExists10(repoRoot, ".bloop");
8775
+ if (await fileExists15(repoRoot, "build.sbt")) {
8776
+ const hasBloop = await fileExists15(repoRoot, ".bloop");
8040
8777
  if (hasBloop) {
8041
8778
  missing.push({
8042
8779
  ecosystem: "sbt",
@@ -8059,8 +8796,8 @@ async function runWorkspacePreps(opts) {
8059
8796
  if (opts.missing.length === 0)
8060
8797
  return [];
8061
8798
  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 });
8799
+ const logPath2 = join37(getConfigDir(), `install-log.${safeRepoId}.txt`);
8800
+ await fs20.mkdir(getConfigDir(), { recursive: true });
8064
8801
  const stream = opts.logStream ?? createWriteStream2(logPath2, { flags: "a" });
8065
8802
  stream.on("error", () => {
8066
8803
  });
@@ -8206,11 +8943,104 @@ async function prepareWorkspaceDepsForRepos(repos) {
8206
8943
  }
8207
8944
  }
8208
8945
 
8946
+ // ../listener/dist/lsp/build-orchestrator.js
8947
+ init_registry();
8948
+ import { execFile as execFileCb, spawn as spawn10 } from "child_process";
8949
+ import { access as access3, readdir as readdir4 } from "fs/promises";
8950
+ import { join as join38 } from "path";
8951
+ import { promisify as promisify4 } from "util";
8952
+ var execFile6 = promisify4(execFileCb);
8953
+ var DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1e3;
8954
+ async function isCommandOnPath3(command) {
8955
+ try {
8956
+ await execFile6(process.platform === "win32" ? "where" : "which", [command], {
8957
+ timeout: 5e3
8958
+ });
8959
+ return true;
8960
+ } catch {
8961
+ return false;
8962
+ }
8963
+ }
8964
+ async function hasBuildableManifest(root, language) {
8965
+ const spec = getBuildSpec(language);
8966
+ if (!spec)
8967
+ return false;
8968
+ let entries;
8969
+ try {
8970
+ entries = await readdir4(root);
8971
+ } catch {
8972
+ return false;
8973
+ }
8974
+ return spec.manifests.some((m) => m.startsWith("*.") ? entries.some((e) => e.endsWith(m.slice(1))) : entries.includes(m));
8975
+ }
8976
+ async function ensureIndexable(indexRoot, language, options = {}) {
8977
+ const spec = getBuildSpec(language);
8978
+ if (!spec)
8979
+ return { ready: true };
8980
+ if (!await hasBuildableManifest(indexRoot, language)) {
8981
+ return { ready: true };
8982
+ }
8983
+ if (spec.platform && (options.platform ?? process.platform) !== spec.platform) {
8984
+ return { ready: false, reason: "no-toolchain" };
8985
+ }
8986
+ const probe = options.isOnPath ?? isCommandOnPath3;
8987
+ for (const cmd of spec.gateCommands) {
8988
+ if (!await probe(cmd)) {
8989
+ console.warn(`[build-orchestrator] ${language}: '${cmd}' not on PATH \u2014 skipping build (AST-only for this language)`);
8990
+ return { ready: false, reason: "no-toolchain" };
8991
+ }
8992
+ }
8993
+ const exec2 = options.execImpl ?? execFile6;
8994
+ for (const gate of spec.gateExecs) {
8995
+ try {
8996
+ await exec2(gate.command, [...gate.args], { timeout: 15e3 });
8997
+ } catch {
8998
+ console.warn(`[build-orchestrator] ${language}: '${gate.command} ${gate.args.join(" ")}' failed \u2014 skipping build`);
8999
+ return { ready: false, reason: "no-toolchain" };
9000
+ }
9001
+ }
9002
+ try {
9003
+ await access3(join38(indexRoot, spec.indexStoreProbe));
9004
+ return { ready: true, built: false };
9005
+ } catch {
9006
+ }
9007
+ const timeoutMs = options.timeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS;
9008
+ const spawnImpl = options.spawnImpl ?? spawn10;
9009
+ const ok = await new Promise((resolve5) => {
9010
+ const child = spawnImpl(spec.command, [...spec.args], {
9011
+ cwd: indexRoot,
9012
+ stdio: ["ignore", "pipe", "pipe"]
9013
+ });
9014
+ const killTimer = setTimeout(() => {
9015
+ console.warn(`[build-orchestrator] ${language}: build exceeded ${(timeoutMs / 1e3).toString()}s \u2014 killing (AST-only)`);
9016
+ child.kill("SIGKILL");
9017
+ }, timeoutMs);
9018
+ const onChunk = () => {
9019
+ options.onActivity?.();
9020
+ };
9021
+ child.stdout?.on("data", onChunk);
9022
+ child.stderr?.on("data", onChunk);
9023
+ child.on("error", () => {
9024
+ clearTimeout(killTimer);
9025
+ resolve5(false);
9026
+ });
9027
+ child.on("close", (code) => {
9028
+ clearTimeout(killTimer);
9029
+ resolve5(code === 0);
9030
+ });
9031
+ });
9032
+ if (!ok) {
9033
+ return { ready: false, reason: "build-failed" };
9034
+ }
9035
+ console.log(`[build-orchestrator] ${language}: cold build complete at ${indexRoot}`);
9036
+ return { ready: true, built: true };
9037
+ }
9038
+
8209
9039
  // ../listener/dist/typed-resolution/resolver-loop.js
8210
9040
  init_src();
8211
9041
  init_registry();
8212
- import { execFile as execFileCb } from "child_process";
8213
- import { promisify as promisify4 } from "util";
9042
+ import { execFile as execFileCb2 } from "child_process";
9043
+ import { promisify as promisify5 } from "util";
8214
9044
 
8215
9045
  // ../listener/dist/typed-resolution/lsp-upgrade-strategy.js
8216
9046
  var goStrategy = {
@@ -8686,13 +9516,13 @@ async function resolveReceiverViaStrategy(session, uri, receiver, repoRoot, opts
8686
9516
  init_sidecar_cache();
8687
9517
  init_sidecar_client();
8688
9518
  var BREAKER_TRIP_THRESHOLD = 3;
8689
- var execFile5 = promisify4(execFileCb);
8690
- async function isCommandOnPath3(command) {
9519
+ var execFile7 = promisify5(execFileCb2);
9520
+ async function isCommandOnPath4(command) {
8691
9521
  if (/[^\w./+-]/.test(command))
8692
9522
  return false;
8693
9523
  const probeCmd = process.platform === "win32" ? "where" : "which";
8694
9524
  try {
8695
- await execFile5(probeCmd, [command], { timeout: 5e3 });
9525
+ await execFile7(probeCmd, [command], { timeout: 5e3 });
8696
9526
  return true;
8697
9527
  } catch {
8698
9528
  return false;
@@ -8700,7 +9530,7 @@ async function isCommandOnPath3(command) {
8700
9530
  }
8701
9531
  async function isWorkingTreeAtCommit(repoRoot, commitSha) {
8702
9532
  try {
8703
- const { stdout } = await execFile5("git", ["-C", repoRoot, "rev-parse", "HEAD"], {
9533
+ const { stdout } = await execFile7("git", ["-C", repoRoot, "rev-parse", "HEAD"], {
8704
9534
  timeout: 5e3
8705
9535
  });
8706
9536
  const head = stdout.trim();
@@ -8733,85 +9563,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
8733
9563
  const breakerOpenEvents = {};
8734
9564
  const pathProbeCache = /* @__PURE__ */ new Map();
8735
9565
  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;
9566
+ const resolveOneReceiver = async (receiver) => {
9567
+ const cached = warmCacheIndex.get(typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column));
9568
+ if (cached) {
9569
+ warmHits += 1;
9570
+ return cached.targetSymbolId;
9571
+ }
9572
+ const language = detectLanguage(receiver.filePath);
9573
+ if (!language)
9574
+ return null;
9575
+ seenLanguages.add(language);
9576
+ if (trippedLanguages.has(language))
9577
+ return null;
9578
+ if (missingBinaryLanguages.has(language))
9579
+ return null;
9580
+ const config2 = getEffectiveConfig(language, process.env, input.lspOverrides);
9581
+ if (!config2)
9582
+ return null;
9583
+ let onPath = pathProbeCache.get(language);
9584
+ if (onPath === void 0) {
9585
+ const probe = input.isOnPath ?? isCommandOnPath4;
9586
+ onPath = await probe(config2.command);
9587
+ pathProbeCache.set(language, onPath);
9588
+ if (!onPath) {
9589
+ missingBinaryLanguages.add(language);
9590
+ console.warn(`[typed-resolution] ${language}: ${config2.command} not on PATH \u2014 skipping LSP upgrade for this cycle. install: ${config2.installHint}`);
8750
9591
  }
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}`);
9592
+ }
9593
+ if (!onPath)
9594
+ return null;
9595
+ try {
9596
+ const session = await input.workspaces.getOrOpen({
9597
+ repoRoot: input.repoRoot,
9598
+ language,
9599
+ config: config2
9600
+ });
9601
+ const uri = await input.workspaces.ensureOpen(session, receiver.filePath);
9602
+ let methodErrorsForReceiver = 0;
9603
+ const attempt = await resolveReceiverViaStrategy(session, uri, receiver, input.repoRoot, {
9604
+ onMethodError: () => {
9605
+ methodErrorsForReceiver += 1;
8770
9606
  }
8771
- }
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;
8802
- }
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)}`);
9607
+ });
9608
+ const hasResult = attempt !== null && (attempt.targetSymbolId !== null || attempt.targets.length > 0);
9609
+ if (hasResult) {
9610
+ consecutiveFailures.set(language, 0);
9611
+ } else if (methodErrorsForReceiver > 0) {
8815
9612
  const next = (consecutiveFailures.get(language) ?? 0) + 1;
8816
9613
  consecutiveFailures.set(language, next);
8817
9614
  if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
@@ -8820,7 +9617,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
8820
9617
  console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
8821
9618
  }
8822
9619
  return null;
9620
+ } else {
9621
+ consecutiveFailures.set(language, 0);
9622
+ return null;
9623
+ }
9624
+ const lookupKey = typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column);
9625
+ const schemaKind = attempt.kind === "definition" || attempt.kind === "implementation" || attempt.kind === "typeDefinition" || attempt.kind === "workspace-symbol-fallback" ? attempt.kind : void 0;
9626
+ telemetryByKey.set(lookupKey, {
9627
+ kind: schemaKind,
9628
+ lspMethod: attempt.method,
9629
+ targets: attempt.targets.length > 0 ? attempt.targets : void 0,
9630
+ lspLatencyMs: attempt.lspLatencyMs,
9631
+ confidence: attempt.targets.length > 1 ? attempt.baseConfidence / attempt.targets.length : attempt.baseConfidence
9632
+ });
9633
+ return attempt.targetSymbolId;
9634
+ } catch (err) {
9635
+ 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)}`);
9636
+ const next = (consecutiveFailures.get(language) ?? 0) + 1;
9637
+ consecutiveFailures.set(language, next);
9638
+ if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
9639
+ trippedLanguages.add(language);
9640
+ breakerOpenEvents[language] = (breakerOpenEvents[language] ?? 0) + 1;
9641
+ console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
8823
9642
  }
9643
+ return null;
9644
+ }
9645
+ };
9646
+ let processedCount = 0;
9647
+ let resolvedOkCount = 0;
9648
+ const sidecar = await buildSidecar({
9649
+ receivers,
9650
+ producer: input.producer,
9651
+ languages: [],
9652
+ // populated below by mutating after observation
9653
+ // Item 14 — fan out across languages so pyright + gopls + tsserver
9654
+ // run in parallel. The groupKey is the detected language; serial
9655
+ // within a language so we don't overwhelm a single LSP server.
9656
+ groupKey: (receiver) => detectLanguage(receiver.filePath),
9657
+ // Phase 4 — instrument per-receiver progress for the wait-status
9658
+ // reporter (resolved deltas drive the server's stall verdict).
9659
+ resolve: async (receiver) => {
9660
+ const out = await resolveOneReceiver(receiver);
9661
+ processedCount += 1;
9662
+ if (out !== null)
9663
+ resolvedOkCount += 1;
9664
+ input.onReceiverProcessed?.(processedCount, receivers.length, resolvedOkCount);
9665
+ return out;
8824
9666
  }
8825
9667
  });
8826
9668
  const enriched = sidecar.resolutions.map((entry) => {
@@ -8879,6 +9721,234 @@ async function runDownloaderCycle(input, fetchImpl = fetch) {
8879
9721
  return { downloaded: true, edgesUpgraded: result.edgesUpgraded };
8880
9722
  }
8881
9723
 
9724
+ // ../listener/dist/typed-resolution/committed-checkout.js
9725
+ init_config_dir();
9726
+ import { execFile as execFileCb3 } from "child_process";
9727
+ import { createHash as createHash4 } from "crypto";
9728
+ import { mkdir as mkdir15, readdir as readdir5, rm, stat as stat6 } from "fs/promises";
9729
+ import { join as join39 } from "path";
9730
+ import { promisify as promisify6 } from "util";
9731
+ var execFile8 = promisify6(execFileCb3);
9732
+ var GIT_OP_TIMEOUT_MS = 6e4;
9733
+ var MAX_CONCURRENT_WORKTREES = 3;
9734
+ function worktreeBaseDir() {
9735
+ return join39(getConfigDir(), "lsp-worktrees");
9736
+ }
9737
+ async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktreeBaseDir()) {
9738
+ let reaped = 0;
9739
+ let entries;
9740
+ try {
9741
+ entries = await readdir5(base);
9742
+ } catch {
9743
+ return 0;
9744
+ }
9745
+ const cutoff = Date.now() - maxAgeMs;
9746
+ for (const entry of entries) {
9747
+ const dir = join39(base, entry);
9748
+ try {
9749
+ const info = await stat6(dir);
9750
+ if (!info.isDirectory() || info.mtimeMs > cutoff)
9751
+ continue;
9752
+ await rm(dir, { recursive: true, force: true });
9753
+ reaped += 1;
9754
+ } catch {
9755
+ }
9756
+ }
9757
+ if (reaped > 0) {
9758
+ console.log(`[typed-resolution] reaped ${reaped.toString()} orphaned LSP worktree(s)`);
9759
+ }
9760
+ return reaped;
9761
+ }
9762
+ async function prepareCommittedTree(repoRoot, commitSha, options = {}) {
9763
+ const exec2 = options.execImpl ?? execFile8;
9764
+ const git = (args) => exec2("git", args, { timeout: GIT_OP_TIMEOUT_MS });
9765
+ try {
9766
+ const { stdout: headRaw } = await git(["-C", repoRoot, "rev-parse", "HEAD"]);
9767
+ const head = headRaw.trim();
9768
+ const atCommit = head === commitSha || head.startsWith(commitSha) || commitSha.startsWith(head);
9769
+ if (atCommit) {
9770
+ const { stdout: status2 } = await git(["-C", repoRoot, "status", "--porcelain"]);
9771
+ if (status2.trim().length === 0) {
9772
+ return { indexRoot: repoRoot, reused: true, cleanup: () => Promise.resolve() };
9773
+ }
9774
+ }
9775
+ } catch {
9776
+ return null;
9777
+ }
9778
+ const base = options.worktreeBase ?? worktreeBaseDir();
9779
+ const dirName = createHash4("sha256").update(`${repoRoot}\0${commitSha}`).digest("hex").slice(0, 16) + "-" + commitSha.slice(0, 12);
9780
+ const worktreePath = join39(base, dirName);
9781
+ try {
9782
+ await mkdir15(base, { recursive: true });
9783
+ await git(["-C", repoRoot, "worktree", "prune"]).catch(() => void 0);
9784
+ const existing = await readdir5(base).catch(() => []);
9785
+ const max = options.maxConcurrentWorktrees ?? MAX_CONCURRENT_WORKTREES;
9786
+ if (!existing.includes(dirName) && existing.length >= max) {
9787
+ console.warn(`[typed-resolution] worktree budget exceeded (${existing.length.toString()}/${max.toString()}) \u2014 proceeding AST-only`);
9788
+ return null;
9789
+ }
9790
+ if (!existing.includes(dirName)) {
9791
+ await git(["-C", repoRoot, "worktree", "add", "--detach", worktreePath, commitSha]);
9792
+ }
9793
+ const cleanup = async () => {
9794
+ try {
9795
+ await exec2("git", ["-C", repoRoot, "worktree", "remove", "--force", worktreePath], {
9796
+ timeout: GIT_OP_TIMEOUT_MS
9797
+ });
9798
+ } catch {
9799
+ }
9800
+ await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
9801
+ await exec2("git", ["-C", repoRoot, "worktree", "prune"], {
9802
+ timeout: GIT_OP_TIMEOUT_MS
9803
+ }).catch(() => void 0);
9804
+ };
9805
+ return { indexRoot: worktreePath, reused: false, cleanup };
9806
+ } catch (err) {
9807
+ console.warn(`[typed-resolution] committed checkout failed \u2014 proceeding AST-only: ${err instanceof Error ? err.message : String(err)}`);
9808
+ await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
9809
+ return null;
9810
+ }
9811
+ }
9812
+
9813
+ // ../listener/dist/typed-resolution/status-reporter.js
9814
+ var REQUEST_TIMEOUT_MS = 3e4;
9815
+ var DEFAULT_INTERVAL_MS = 6e4;
9816
+ var NO_WAIT_STOP_THRESHOLD = 2;
9817
+ function createStatusReporter(options) {
9818
+ const fetchImpl = options.fetchImpl ?? fetch;
9819
+ const url = `${options.apiUrl.replace(/\/+$/, "")}/v1/listeners/typed-resolution-status`;
9820
+ let timer = null;
9821
+ let pollSeq = 0;
9822
+ let lastResolvedCount = 0;
9823
+ let consecutiveNoWait = 0;
9824
+ let stopped = false;
9825
+ let lastPostFailed = false;
9826
+ async function post(state, snapshot, resolvedDelta) {
9827
+ pollSeq += 1;
9828
+ const body = JSON.stringify({
9829
+ repoId: options.repoId,
9830
+ commitSha: options.commitSha,
9831
+ state,
9832
+ resolvedDelta,
9833
+ remainingReceivers: snapshot.remainingReceivers,
9834
+ indexingActive: snapshot.indexingActive,
9835
+ pollSeq
9836
+ });
9837
+ const attempt = async (token) => {
9838
+ const ctrl = new AbortController();
9839
+ const t = setTimeout(() => {
9840
+ ctrl.abort();
9841
+ }, REQUEST_TIMEOUT_MS);
9842
+ try {
9843
+ return await fetchImpl(url, {
9844
+ method: "POST",
9845
+ headers: {
9846
+ "Content-Type": "application/json",
9847
+ Authorization: `Bearer ${token}`
9848
+ },
9849
+ body,
9850
+ signal: ctrl.signal
9851
+ });
9852
+ } finally {
9853
+ clearTimeout(t);
9854
+ }
9855
+ };
9856
+ lastPostFailed = false;
9857
+ try {
9858
+ let res = await attempt(await options.getAuthToken());
9859
+ if ((res.status === 401 || res.status === 403) && options.getAuthTokenForceRefresh) {
9860
+ res = await attempt(await options.getAuthTokenForceRefresh());
9861
+ }
9862
+ if (!res.ok) {
9863
+ lastPostFailed = true;
9864
+ console.warn(`[typed-resolution] status push failed: HTTP ${res.status.toString()} (server silence window is the backstop)`);
9865
+ return;
9866
+ }
9867
+ const payload = await res.json().catch(() => null);
9868
+ const data = payload?.data;
9869
+ if (data?.accepted === false && data.reason === "disabled") {
9870
+ consecutiveNoWait += 1;
9871
+ if (consecutiveNoWait >= NO_WAIT_STOP_THRESHOLD && timer) {
9872
+ clearInterval(timer);
9873
+ timer = null;
9874
+ }
9875
+ } else {
9876
+ consecutiveNoWait = 0;
9877
+ }
9878
+ } catch (err) {
9879
+ lastPostFailed = true;
9880
+ console.warn(`[typed-resolution] status push error (ignored): ${err instanceof Error ? err.message : String(err)}`);
9881
+ }
9882
+ }
9883
+ function tick() {
9884
+ const snapshot = options.getSnapshot();
9885
+ const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
9886
+ lastResolvedCount = snapshot.resolvedCount;
9887
+ void post(delta > 0 ? "progressing" : "stalled", snapshot, delta);
9888
+ }
9889
+ return {
9890
+ start() {
9891
+ if (timer || stopped)
9892
+ return;
9893
+ tick();
9894
+ timer = setInterval(tick, options.intervalMs ?? DEFAULT_INTERVAL_MS);
9895
+ },
9896
+ async reportCompleted() {
9897
+ this.stop();
9898
+ const snapshot = options.getSnapshot();
9899
+ const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
9900
+ await post("completed", snapshot, delta);
9901
+ if (lastPostFailed)
9902
+ await post("completed", snapshot, delta);
9903
+ },
9904
+ async reportCrashed() {
9905
+ this.stop();
9906
+ await post("crashed", options.getSnapshot(), 0);
9907
+ if (lastPostFailed)
9908
+ await post("crashed", options.getSnapshot(), 0);
9909
+ },
9910
+ stop() {
9911
+ stopped = true;
9912
+ if (timer) {
9913
+ clearInterval(timer);
9914
+ timer = null;
9915
+ }
9916
+ }
9917
+ };
9918
+ }
9919
+
9920
+ // ../listener/dist/typed-resolution/graph-fetch.js
9921
+ var REQUEST_TIMEOUT_MS2 = 3e4;
9922
+ async function fetchProducerGraph(apiUrl, authToken, repoId, fetchImpl = fetch) {
9923
+ const url = `${apiUrl.replace(/\/+$/, "")}/v1/repos/${encodeURIComponent(repoId)}/graph`;
9924
+ const ctrl = new AbortController();
9925
+ const timer = setTimeout(() => {
9926
+ ctrl.abort();
9927
+ }, REQUEST_TIMEOUT_MS2);
9928
+ try {
9929
+ const res = await fetchImpl(url, {
9930
+ headers: { Authorization: `Bearer ${authToken}` },
9931
+ signal: ctrl.signal
9932
+ });
9933
+ if (!res.ok) {
9934
+ console.warn(`[typed-resolution] graph fetch failed: HTTP ${res.status.toString()}`);
9935
+ return null;
9936
+ }
9937
+ const parsed = await res.json();
9938
+ if (typeof parsed !== "object" || parsed === null)
9939
+ return null;
9940
+ const graph = parsed;
9941
+ if (typeof graph.commitSha !== "string" || graph.commitSha.length === 0)
9942
+ return null;
9943
+ return graph;
9944
+ } catch (err) {
9945
+ console.warn(`[typed-resolution] graph fetch error: ${err instanceof Error ? err.message : String(err)}`);
9946
+ return null;
9947
+ } finally {
9948
+ clearTimeout(timer);
9949
+ }
9950
+ }
9951
+
8882
9952
  // ../listener/dist/typed-resolution/hooks.js
8883
9953
  function buildTypedResolutionHooks(options) {
8884
9954
  const producer = options.producer ?? "repowise-listener/0.0.0";
@@ -8921,6 +9991,102 @@ function buildTypedResolutionHooks(options) {
8921
9991
  ...lspOverrides ? { lspOverrides } : {}
8922
9992
  });
8923
9993
  },
9994
+ async onSidecarRequested(repoId, commitSha, localPath) {
9995
+ if (!await hasBuildableManifest(localPath, "swift")) {
9996
+ return;
9997
+ }
9998
+ console.log(`[typed-resolution] sidecar.requested for ${repoId}@${commitSha.slice(0, 12)} \u2014 preparing Swift build`);
9999
+ const prepared = await prepareCommittedTree(localPath, commitSha);
10000
+ if (!prepared)
10001
+ return;
10002
+ const result = await ensureIndexable(prepared.indexRoot, "swift");
10003
+ if (!result.ready) {
10004
+ console.warn(`[typed-resolution] Swift prep for ${repoId} skipped: ${result.reason ?? "unknown"} (AST-only for swift)`);
10005
+ await prepared.cleanup().catch(() => void 0);
10006
+ }
10007
+ },
10008
+ async onGraphReady(repoId, commitSha, localPath) {
10009
+ const apiUrl = options.repoApiUrls.get(repoId);
10010
+ if (!apiUrl)
10011
+ return;
10012
+ const token = await resolveToken();
10013
+ if (!token)
10014
+ return;
10015
+ const graph = await fetchProducerGraph(apiUrl, token, repoId);
10016
+ const shaMatches = graph !== null && (graph.commitSha === commitSha || graph.commitSha.startsWith(commitSha) || commitSha.startsWith(graph.commitSha));
10017
+ if (!graph || !shaMatches) {
10018
+ 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`);
10019
+ return;
10020
+ }
10021
+ const receivers = graph.unresolvedReceivers ?? [];
10022
+ const snapshot = {
10023
+ resolvedCount: 0,
10024
+ remainingReceivers: receivers.length,
10025
+ // Until the first receiver completes, session spawn + server
10026
+ // indexing is the activity signal that keeps the wait alive.
10027
+ indexingActive: true
10028
+ };
10029
+ const reporter = createStatusReporter({
10030
+ apiUrl,
10031
+ repoId,
10032
+ commitSha: graph.commitSha,
10033
+ getAuthToken: options.getAuthToken,
10034
+ ...options.getAuthTokenForceRefresh ? { getAuthTokenForceRefresh: options.getAuthTokenForceRefresh } : {},
10035
+ getSnapshot: () => ({ ...snapshot })
10036
+ });
10037
+ reporter.start();
10038
+ let prepared = null;
10039
+ try {
10040
+ if (receivers.length === 0) {
10041
+ await reporter.reportCompleted();
10042
+ return;
10043
+ }
10044
+ prepared = await prepareCommittedTree(localPath, graph.commitSha);
10045
+ if (!prepared) {
10046
+ await reporter.reportCrashed();
10047
+ return;
10048
+ }
10049
+ const buildResult = await ensureIndexable(prepared.indexRoot, "swift", {
10050
+ onActivity: () => {
10051
+ snapshot.indexingActive = true;
10052
+ }
10053
+ });
10054
+ if (!buildResult.ready) {
10055
+ console.warn(`[typed-resolution] swift build unavailable for ${repoId}: ${buildResult.reason ?? "unknown"} \u2014 continuing without it`);
10056
+ }
10057
+ const lspOverrides = options.getLspOverrides?.();
10058
+ const result = await runProducerCycle({
10059
+ apiUrl,
10060
+ authToken: token,
10061
+ repoId,
10062
+ repoRoot: prepared.indexRoot,
10063
+ graph,
10064
+ workspaces: options.workspaces,
10065
+ producer,
10066
+ ...lspOverrides ? { lspOverrides } : {},
10067
+ onReceiverProcessed: (processed, total, resolvedCount) => {
10068
+ snapshot.resolvedCount = resolvedCount;
10069
+ snapshot.remainingReceivers = Math.max(0, total - processed);
10070
+ snapshot.indexingActive = false;
10071
+ }
10072
+ });
10073
+ if (result) {
10074
+ await reporter.reportCompleted();
10075
+ } else {
10076
+ await reporter.reportCrashed();
10077
+ }
10078
+ } catch (err) {
10079
+ console.warn(`[typed-resolution] graph.ready producer failed for ${repoId}: ${err instanceof Error ? err.message : String(err)}`);
10080
+ await reporter.reportCrashed().catch(() => void 0);
10081
+ } finally {
10082
+ reporter.stop();
10083
+ if (prepared) {
10084
+ await prepared.cleanup().catch((cleanupErr) => {
10085
+ console.warn(`[typed-resolution] worktree cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`);
10086
+ });
10087
+ }
10088
+ }
10089
+ },
8924
10090
  async onSidecarMerged(repoId, commitSha) {
8925
10091
  const apiUrl = options.repoApiUrls.get(repoId);
8926
10092
  if (!apiUrl)
@@ -8947,8 +10113,8 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
8947
10113
  var CRASH_LOOP_THRESHOLD = 3;
8948
10114
  async function readRawToolConfig() {
8949
10115
  try {
8950
- const configPath = join32(getConfigDir(), "config.json");
8951
- const data = await readFile12(configPath, "utf-8");
10116
+ const configPath = join40(getConfigDir(), "config.json");
10117
+ const data = await readFile13(configPath, "utf-8");
8952
10118
  const raw = JSON.parse(data);
8953
10119
  return {
8954
10120
  aiTools: Array.isArray(raw["aiTools"]) ? raw["aiTools"] : void 0,
@@ -8967,10 +10133,20 @@ async function updateToolConfigsForRepo(localPath, config2, state, repoId) {
8967
10133
  }
8968
10134
  if (tools.length === 0)
8969
10135
  return;
10136
+ if (tools.includes("roo-code")) {
10137
+ tools = tools.map((t) => t === "roo-code" ? "kilo" : t);
10138
+ tools = tools.filter((t, i) => tools.indexOf(t) === i);
10139
+ }
10140
+ await migrateRooToKilo(localPath).catch(() => {
10141
+ });
8970
10142
  const contextFiles = await scanLocalContextFiles(localPath, contextFolder);
8971
10143
  if (contextFiles.length === 0)
8972
10144
  return;
8973
- const hash = JSON.stringify({ tools, files: contextFiles.map((f) => f.fileName) });
10145
+ const hash = JSON.stringify({
10146
+ rev: AI_TOOLS_REFERENCE_REV,
10147
+ tools,
10148
+ files: contextFiles.map((f) => f.fileName)
10149
+ });
8974
10150
  if (state.repos[repoId]?.lastToolConfigHash === hash)
8975
10151
  return;
8976
10152
  const written = /* @__PURE__ */ new Set();
@@ -9010,8 +10186,8 @@ function resolveAuditRoots() {
9010
10186
  const out = /* @__PURE__ */ new Set();
9011
10187
  let dir = dirname15(process.execPath);
9012
10188
  for (let i = 0; i < 8; i += 1) {
9013
- out.add(join32(dir, "node_modules"));
9014
- out.add(join32(dir, "lib", "node_modules"));
10189
+ out.add(join40(dir, "node_modules"));
10190
+ out.add(join40(dir, "lib", "node_modules"));
9015
10191
  const parent = dirname15(dir);
9016
10192
  if (parent === dir)
9017
10193
  break;
@@ -9141,6 +10317,47 @@ async function runProducerHook(notif, localPath, ctx) {
9141
10317
  console.warn(`[typed-resolution] producer failed for ${notif.repoId}: ${prodErr instanceof Error ? prodErr.message : String(prodErr)}`);
9142
10318
  }
9143
10319
  }
10320
+ var processedSideEffectNotifs = /* @__PURE__ */ new Set();
10321
+ var MAX_PROCESSED_SIDE_EFFECT_NOTIFS = 512;
10322
+ function alreadyProcessedSideEffect(notificationId) {
10323
+ if (processedSideEffectNotifs.has(notificationId))
10324
+ return true;
10325
+ processedSideEffectNotifs.add(notificationId);
10326
+ if (processedSideEffectNotifs.size > MAX_PROCESSED_SIDE_EFFECT_NOTIFS) {
10327
+ const oldest = processedSideEffectNotifs.values().next().value;
10328
+ if (oldest !== void 0)
10329
+ processedSideEffectNotifs.delete(oldest);
10330
+ }
10331
+ return false;
10332
+ }
10333
+ async function handleSidecarRequested(notif, ctx) {
10334
+ if (!ctx.typedResolutionHooks?.onSidecarRequested || !notif.commitSha)
10335
+ return;
10336
+ if (alreadyProcessedSideEffect(notif.notificationId))
10337
+ return;
10338
+ const localPath = ctx.repoLocalPaths.get(notif.repoId);
10339
+ if (!localPath)
10340
+ return;
10341
+ try {
10342
+ await ctx.typedResolutionHooks.onSidecarRequested(notif.repoId, notif.commitSha, localPath);
10343
+ } catch (prepErr) {
10344
+ console.warn(`[typed-resolution] sidecar.requested prep failed for ${notif.repoId}: ${prepErr instanceof Error ? prepErr.message : String(prepErr)}`);
10345
+ }
10346
+ }
10347
+ async function handleGraphReady(notif, ctx) {
10348
+ if (!ctx.typedResolutionHooks?.onGraphReady || !notif.commitSha)
10349
+ return;
10350
+ if (alreadyProcessedSideEffect(notif.notificationId))
10351
+ return;
10352
+ const localPath = ctx.repoLocalPaths.get(notif.repoId);
10353
+ if (!localPath)
10354
+ return;
10355
+ try {
10356
+ await ctx.typedResolutionHooks.onGraphReady(notif.repoId, notif.commitSha, localPath);
10357
+ } catch (resolveErr) {
10358
+ console.warn(`[typed-resolution] graph.ready resolve failed for ${notif.repoId}: ${resolveErr instanceof Error ? resolveErr.message : String(resolveErr)}`);
10359
+ }
10360
+ }
9144
10361
  async function handleSidecarMerged(notif, ctx) {
9145
10362
  if (!ctx.typedResolutionHooks || !notif.commitSha)
9146
10363
  return;
@@ -9171,6 +10388,10 @@ async function processNotifications(notifications, state, repoLocalPaths, apiUrl
9171
10388
  updateCount++;
9172
10389
  } else if (notif.type === "sidecar.merged") {
9173
10390
  await handleSidecarMerged(notif, ctx);
10391
+ } else if (notif.type === "sidecar.requested") {
10392
+ await handleSidecarRequested(notif, ctx);
10393
+ } else if (notif.type === "graph.ready") {
10394
+ await handleGraphReady(notif, ctx);
9174
10395
  } else {
9175
10396
  state.repos[notif.repoId] = {
9176
10397
  ...state.repos[notif.repoId],
@@ -9231,7 +10452,7 @@ async function checkStaleContext(repos, state, groups) {
9231
10452
  if (group?.offline.isOffline)
9232
10453
  continue;
9233
10454
  const { statSync: statSync2, readdirSync: readdirSync2 } = await import("fs");
9234
- const contextPath = join32(repo.localPath, "repowise-context");
10455
+ const contextPath = join40(repo.localPath, "repowise-context");
9235
10456
  let isMissingOrEmpty = false;
9236
10457
  try {
9237
10458
  const s = statSync2(contextPath);
@@ -9262,6 +10483,7 @@ async function checkStaleContext(repos, state, groups) {
9262
10483
  }
9263
10484
  async function reconcileMcpConfigs(repos, packageName) {
9264
10485
  const shimCmd = packageName;
10486
+ const { contextFolder, aiTools } = await readRawToolConfig();
9265
10487
  for (const repo of repos) {
9266
10488
  if (!repo.localPath)
9267
10489
  continue;
@@ -9276,7 +10498,9 @@ async function reconcileMcpConfigs(repos, packageName) {
9276
10498
  const results = await runAutoConfig({
9277
10499
  repoRoot: repo.localPath,
9278
10500
  repoId: repo.repoId,
9279
- shimCmd
10501
+ shimCmd,
10502
+ contextFolder,
10503
+ selectedTools: aiTools
9280
10504
  });
9281
10505
  const written = results.filter((r) => r.detected && r.outcome?.status === "written");
9282
10506
  if (written.length > 0) {
@@ -9289,10 +10513,10 @@ async function reconcileMcpConfigs(repos, packageName) {
9289
10513
  }
9290
10514
  async function reconcileAgentInstructions(repos) {
9291
10515
  for (const repo of repos) {
9292
- const path = join32(repo.localPath, "repowise-context", "project-overview.md");
10516
+ const path = join40(repo.localPath, "repowise-context", "project-overview.md");
9293
10517
  let content;
9294
10518
  try {
9295
- content = await readFile12(path, "utf-8");
10519
+ content = await readFile13(path, "utf-8");
9296
10520
  } catch (err) {
9297
10521
  const code = err?.code;
9298
10522
  if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "EPERM" || code === "EISDIR") {
@@ -9305,7 +10529,7 @@ async function reconcileAgentInstructions(repos) {
9305
10529
  if (injected === content)
9306
10530
  continue;
9307
10531
  try {
9308
- await writeFile14(path, injected, "utf-8");
10532
+ await writeFile15(path, injected, "utf-8");
9309
10533
  console.log(`[reconcile] Reconciled agent instructions for ${repo.repoId}`);
9310
10534
  } catch (err) {
9311
10535
  console.warn(`[reconcile] write failed for ${repo.repoId}:`, err instanceof Error ? err.message : String(err));
@@ -9350,9 +10574,9 @@ async function startListener() {
9350
10574
  console.warn(`[integrity] audit failed: ${err instanceof Error ? err.message : String(err)}`);
9351
10575
  }
9352
10576
  const configDir = getConfigDir();
9353
- await mkdir14(configDir, { recursive: true });
9354
- const lockPath = join32(configDir, "listener.lock");
9355
- await writeFile14(lockPath, "", { flag: "a" });
10577
+ await mkdir16(configDir, { recursive: true });
10578
+ const lockPath = join40(configDir, "listener.lock");
10579
+ await writeFile15(lockPath, "", { flag: "a" });
9356
10580
  let lockIsHeld = false;
9357
10581
  try {
9358
10582
  lockIsHeld = await lockfile4.check(lockPath, { stale: 3e4, realpath: false });
@@ -9394,7 +10618,7 @@ async function startListener() {
9394
10618
  return;
9395
10619
  }
9396
10620
  if (config2.repos.length === 0 && !config2.autoDiscoverRepos) {
9397
- console.error(`No repos configured. Add repos to ${join32(configDir, "config.json")}`);
10621
+ console.error(`No repos configured. Add repos to ${join40(configDir, "config.json")}`);
9398
10622
  await releaseLockAndExit();
9399
10623
  process.exitCode = 1;
9400
10624
  return;
@@ -9462,8 +10686,8 @@ async function startListener() {
9462
10686
  let currentVersion = "";
9463
10687
  try {
9464
10688
  const selfDir = dirname15(fileURLToPath3(import.meta.url));
9465
- const pkgJsonPath = join32(selfDir, "..", "..", "package.json");
9466
- const pkgJson = JSON.parse(await readFile12(pkgJsonPath, "utf-8"));
10689
+ const pkgJsonPath = join40(selfDir, "..", "..", "package.json");
10690
+ const pkgJson = JSON.parse(await readFile13(pkgJsonPath, "utf-8"));
9467
10691
  currentVersion = pkgJson.version;
9468
10692
  } catch (err) {
9469
10693
  console.log(`[auto-update] Version detection failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -9591,6 +10815,9 @@ async function startListener() {
9591
10815
  console.warn("[workspace-prep] Pre-install scan failed:", err instanceof Error ? err.message : String(err));
9592
10816
  }
9593
10817
  })();
10818
+ void reapOrphanWorktrees().catch((reapErr) => {
10819
+ console.warn(`[typed-resolution] worktree reap failed: ${reapErr instanceof Error ? reapErr.message : String(reapErr)}`);
10820
+ });
9594
10821
  const typedResolutionHooks = mcpRuntime?.lspWorkspaces ? buildTypedResolutionHooks({
9595
10822
  workspaces: mcpRuntime.lspWorkspaces,
9596
10823
  graphCache: mcpRuntime.graphCache,
@@ -9887,12 +11114,12 @@ async function startListener() {
9887
11114
  } catch {
9888
11115
  }
9889
11116
  }
9890
- const credentialsPath = join32(getConfigDir(), "credentials.json");
11117
+ const credentialsPath = join40(getConfigDir(), "credentials.json");
9891
11118
  let credentialsChanged = false;
9892
11119
  let watcher = null;
9893
11120
  try {
9894
- const fs25 = await import("fs");
9895
- watcher = fs25.watch(credentialsPath, () => {
11121
+ const fs30 = await import("fs");
11122
+ watcher = fs30.watch(credentialsPath, () => {
9896
11123
  credentialsChanged = true;
9897
11124
  });
9898
11125
  } catch {
@@ -9933,7 +11160,7 @@ if (isDirectRun) {
9933
11160
 
9934
11161
  // src/lib/env.ts
9935
11162
  import { homedir as homedir6 } from "os";
9936
- import { join as join33 } from "path";
11163
+ import { join as join41 } from "path";
9937
11164
  var IS_STAGING2 = true ? true : false;
9938
11165
  var PRODUCTION = {
9939
11166
  apiUrl: "https://api.repowise.ai",
@@ -9953,7 +11180,7 @@ function getEnvConfig() {
9953
11180
  return IS_STAGING2 ? STAGING : PRODUCTION;
9954
11181
  }
9955
11182
  function getConfigDir2() {
9956
- return join33(homedir6(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
11183
+ return join41(homedir6(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
9957
11184
  }
9958
11185
  function getPackageName() {
9959
11186
  return true ? "repowisestage" : "repowise";
@@ -9963,12 +11190,12 @@ function getPackageName() {
9963
11190
  import chalk from "chalk";
9964
11191
 
9965
11192
  // 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";
11193
+ import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir17, rename as rename4, unlink as unlink10 } from "fs/promises";
11194
+ import { join as join42 } from "path";
9968
11195
  import lockfile5 from "proper-lockfile";
9969
11196
  async function getConfig() {
9970
11197
  try {
9971
- const data = await readFile13(join34(getConfigDir2(), "config.json"), "utf-8");
11198
+ const data = await readFile14(join42(getConfigDir2(), "config.json"), "utf-8");
9972
11199
  return JSON.parse(data);
9973
11200
  } catch {
9974
11201
  return {};
@@ -9976,15 +11203,15 @@ async function getConfig() {
9976
11203
  }
9977
11204
  async function saveConfig(config2) {
9978
11205
  const dir = getConfigDir2();
9979
- const path = join34(dir, "config.json");
9980
- await mkdir15(dir, { recursive: true });
11206
+ const path = join42(dir, "config.json");
11207
+ await mkdir17(dir, { recursive: true });
9981
11208
  const tmpPath = path + ".tmp";
9982
11209
  try {
9983
- await writeFile15(tmpPath, JSON.stringify(config2, null, 2));
11210
+ await writeFile16(tmpPath, JSON.stringify(config2, null, 2));
9984
11211
  await rename4(tmpPath, path);
9985
11212
  } catch (err) {
9986
11213
  try {
9987
- await unlink9(tmpPath);
11214
+ await unlink10(tmpPath);
9988
11215
  } catch {
9989
11216
  }
9990
11217
  throw err;
@@ -9992,10 +11219,10 @@ async function saveConfig(config2) {
9992
11219
  }
9993
11220
  async function mergeAndSaveConfig(updates) {
9994
11221
  const dir = getConfigDir2();
9995
- const path = join34(dir, "config.json");
9996
- await mkdir15(dir, { recursive: true });
11222
+ const path = join42(dir, "config.json");
11223
+ await mkdir17(dir, { recursive: true });
9997
11224
  try {
9998
- await writeFile15(path, "", { flag: "a" });
11225
+ await writeFile16(path, "", { flag: "a" });
9999
11226
  } catch {
10000
11227
  }
10001
11228
  let release = null;
@@ -10003,7 +11230,7 @@ async function mergeAndSaveConfig(updates) {
10003
11230
  release = await lockfile5.lock(path, { stale: 1e4, retries: 3, realpath: false });
10004
11231
  let raw = {};
10005
11232
  try {
10006
- raw = JSON.parse(await readFile13(path, "utf-8"));
11233
+ raw = JSON.parse(await readFile14(path, "utf-8"));
10007
11234
  } catch {
10008
11235
  }
10009
11236
  const merged = { ...raw, ...updates };
@@ -10063,16 +11290,16 @@ async function showWelcome(currentVersion) {
10063
11290
 
10064
11291
  // src/commands/create.ts
10065
11292
  import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
10066
- import { dirname as dirname16, join as join40 } from "path";
11293
+ import { dirname as dirname16, join as join47 } from "path";
10067
11294
  init_src();
10068
11295
  import chalk8 from "chalk";
10069
11296
  import ora from "ora";
10070
11297
 
10071
11298
  // 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";
11299
+ import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
11300
+ import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir18, chmod as chmod4, unlink as unlink11 } from "fs/promises";
10074
11301
  import http from "http";
10075
- import { join as join35 } from "path";
11302
+ import { join as join43 } from "path";
10076
11303
  var CLI_CALLBACK_PORT = 19876;
10077
11304
  var CALLBACK_TIMEOUT_MS = 12e4;
10078
11305
  function getCognitoConfigForStorage() {
@@ -10096,7 +11323,7 @@ function generateCodeVerifier() {
10096
11323
  return randomBytes3(32).toString("base64url");
10097
11324
  }
10098
11325
  function generateCodeChallenge(verifier) {
10099
- return createHash4("sha256").update(verifier).digest("base64url");
11326
+ return createHash5("sha256").update(verifier).digest("base64url");
10100
11327
  }
10101
11328
  function generateState() {
10102
11329
  return randomBytes3(32).toString("hex");
@@ -10238,8 +11465,8 @@ async function refreshTokens2(refreshToken) {
10238
11465
  }
10239
11466
  async function getStoredCredentials2() {
10240
11467
  try {
10241
- const credPath = join35(getConfigDir2(), "credentials.json");
10242
- const data = await readFile14(credPath, "utf-8");
11468
+ const credPath = join43(getConfigDir2(), "credentials.json");
11469
+ const data = await readFile15(credPath, "utf-8");
10243
11470
  return JSON.parse(data);
10244
11471
  } catch (err) {
10245
11472
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -10250,14 +11477,14 @@ async function getStoredCredentials2() {
10250
11477
  }
10251
11478
  async function storeCredentials2(credentials) {
10252
11479
  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));
11480
+ const credPath = join43(dir, "credentials.json");
11481
+ await mkdir18(dir, { recursive: true, mode: 448 });
11482
+ await writeFile17(credPath, JSON.stringify(credentials, null, 2));
10256
11483
  await chmod4(credPath, 384);
10257
11484
  }
10258
11485
  async function clearCredentials() {
10259
11486
  try {
10260
- await unlink10(join35(getConfigDir2(), "credentials.json"));
11487
+ await unlink11(join43(getConfigDir2(), "credentials.json"));
10261
11488
  } catch (err) {
10262
11489
  if (err.code !== "ENOENT") throw err;
10263
11490
  }
@@ -10432,7 +11659,8 @@ async function selectAiTools() {
10432
11659
  new Separator(chalk2.dim("\u2500\u2500 More Tools \u2500\u2500")),
10433
11660
  { name: "Cline", value: "cline" },
10434
11661
  { name: "Codex", value: "codex" },
10435
- { name: "Roo Code", value: "roo-code" },
11662
+ // Roo Code shut down 2026-05-15; Kilo Code is the community successor.
11663
+ { name: "Kilo Code", value: "kilo" },
10436
11664
  { name: "Gemini CLI", value: "gemini" },
10437
11665
  new Separator(chalk2.dim("\u2500\u2500 Cloud Agents \u2500\u2500")),
10438
11666
  { name: "Warp", value: "warp" },
@@ -10462,55 +11690,11 @@ async function selectAiTools() {
10462
11690
  }
10463
11691
  }
10464
11692
 
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
11693
  // src/lib/gitignore.ts
10510
11694
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
10511
- import { join as join37 } from "path";
11695
+ import { join as join44 } from "path";
10512
11696
  function ensureGitignore(repoRoot, entry) {
10513
- const gitignorePath = join37(repoRoot, ".gitignore");
11697
+ const gitignorePath = join44(repoRoot, ".gitignore");
10514
11698
  if (existsSync2(gitignorePath)) {
10515
11699
  const content = readFileSync2(gitignorePath, "utf-8");
10516
11700
  const lines = content.split("\n").map((l) => l.trim());
@@ -11076,8 +12260,8 @@ async function promptDepInstallConsent(missing, opts) {
11076
12260
  import chalk6 from "chalk";
11077
12261
 
11078
12262
  // src/lib/dep-installer.ts
11079
- import { promises as fs16 } from "fs";
11080
- import { join as join38 } from "path";
12263
+ import { promises as fs21 } from "fs";
12264
+ import { join as join45 } from "path";
11081
12265
  var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
11082
12266
  "typescript",
11083
12267
  "javascript",
@@ -11087,26 +12271,26 @@ var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
11087
12271
  ]);
11088
12272
  var exists = async (p) => {
11089
12273
  try {
11090
- await fs16.access(p);
12274
+ await fs21.access(p);
11091
12275
  return true;
11092
12276
  } catch {
11093
12277
  return false;
11094
12278
  }
11095
12279
  };
11096
- async function fileExists11(repoRoot, name) {
11097
- return exists(join38(repoRoot, name));
12280
+ async function fileExists16(repoRoot, name) {
12281
+ return exists(join45(repoRoot, name));
11098
12282
  }
11099
12283
  async function detectNodePackageManager(repoRoot) {
11100
- if (await fileExists11(repoRoot, "pnpm-lock.yaml")) {
12284
+ if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
11101
12285
  return { ecosystem: "pnpm", command: ["pnpm", "install", "--frozen-lockfile"] };
11102
12286
  }
11103
- if (await fileExists11(repoRoot, "yarn.lock")) {
12287
+ if (await fileExists16(repoRoot, "yarn.lock")) {
11104
12288
  return { ecosystem: "yarn", command: ["yarn", "install", "--frozen-lockfile"] };
11105
12289
  }
11106
- if (await fileExists11(repoRoot, "bun.lock") || await fileExists11(repoRoot, "bun.lockb")) {
12290
+ if (await fileExists16(repoRoot, "bun.lock") || await fileExists16(repoRoot, "bun.lockb")) {
11107
12291
  return { ecosystem: "bun", command: ["bun", "install", "--frozen-lockfile"] };
11108
12292
  }
11109
- if (await fileExists11(repoRoot, "package-lock.json")) {
12293
+ if (await fileExists16(repoRoot, "package-lock.json")) {
11110
12294
  return { ecosystem: "npm", command: ["npm", "ci"] };
11111
12295
  }
11112
12296
  return { ecosystem: "npm", command: ["npm", "install", "--no-audit", "--no-fund"] };
@@ -11114,8 +12298,8 @@ async function detectNodePackageManager(repoRoot) {
11114
12298
  async function detectMissingDeps(repoRoot, scopedLanguages) {
11115
12299
  const missing = [];
11116
12300
  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");
12301
+ if (wantsNode && await fileExists16(repoRoot, "package.json")) {
12302
+ 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
12303
  if (!nodeModulesComplete) {
11120
12304
  const pm = await detectNodePackageManager(repoRoot);
11121
12305
  missing.push({
@@ -11128,9 +12312,9 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11128
12312
  }
11129
12313
  }
11130
12314
  if (scopedLanguages.has("python")) {
11131
- if (await fileExists11(repoRoot, "requirements.txt")) {
12315
+ if (await fileExists16(repoRoot, "requirements.txt")) {
11132
12316
  const venvPipPath = process.platform === "win32" ? "Scripts/pip.exe" : "bin/pip";
11133
- const venvPresent = await fileExists11(repoRoot, `.venv/${venvPipPath}`) || await fileExists11(repoRoot, `venv/${venvPipPath}`);
12317
+ const venvPresent = await fileExists16(repoRoot, `.venv/${venvPipPath}`) || await fileExists16(repoRoot, `venv/${venvPipPath}`);
11134
12318
  if (!venvPresent) {
11135
12319
  missing.push({
11136
12320
  kind: "pip-venv",
@@ -11142,7 +12326,7 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11142
12326
  }
11143
12327
  }
11144
12328
  if (scopedLanguages.has("go")) {
11145
- if (await fileExists11(repoRoot, "go.mod")) {
12329
+ if (await fileExists16(repoRoot, "go.mod")) {
11146
12330
  missing.push({
11147
12331
  kind: "simple",
11148
12332
  ecosystem: "go",
@@ -11153,8 +12337,8 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11153
12337
  }
11154
12338
  }
11155
12339
  if (scopedLanguages.has("php")) {
11156
- if (await fileExists11(repoRoot, "composer.json")) {
11157
- if (!await fileExists11(repoRoot, "vendor")) {
12340
+ if (await fileExists16(repoRoot, "composer.json")) {
12341
+ if (!await fileExists16(repoRoot, "vendor")) {
11158
12342
  missing.push({
11159
12343
  kind: "simple",
11160
12344
  ecosystem: "composer",
@@ -11169,15 +12353,15 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
11169
12353
  }
11170
12354
 
11171
12355
  // src/lib/dep-installer-runner.ts
11172
- import { spawn as spawn10 } from "child_process";
12356
+ import { spawn as spawn11 } from "child_process";
11173
12357
  import { createWriteStream as createWriteStream3 } from "fs";
11174
- import { promises as fs17 } from "fs";
11175
- import { join as join39 } from "path";
12358
+ import { promises as fs22 } from "fs";
12359
+ import { join as join46 } from "path";
11176
12360
  var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
11177
12361
  async function runMissingDepInstalls(opts) {
11178
12362
  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 });
12363
+ const logPath2 = join46(getConfigDir2(), `install-log.${safeRepoId}.txt`);
12364
+ await fs22.mkdir(getConfigDir2(), { recursive: true });
11181
12365
  const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
11182
12366
  stream.on("error", () => {
11183
12367
  });
@@ -11223,7 +12407,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
11223
12407
  }
11224
12408
  async function runPipFlow(repoRoot, stream, timeoutMs) {
11225
12409
  await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
11226
- const venvPip = process.platform === "win32" ? join39(".venv", "Scripts", "pip.exe") : join39(".venv", "bin", "pip");
12410
+ const venvPip = process.platform === "win32" ? join46(".venv", "Scripts", "pip.exe") : join46(".venv", "bin", "pip");
11227
12411
  await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
11228
12412
  }
11229
12413
  function runSimple(cmd, repoRoot, stream, timeoutMs) {
@@ -11236,7 +12420,7 @@ $ ${cmd.join(" ")}
11236
12420
  reject(new Error("runSimple called with empty command"));
11237
12421
  return;
11238
12422
  }
11239
- const child = spawn10(head, cmd.slice(1), {
12423
+ const child = spawn11(head, cmd.slice(1), {
11240
12424
  cwd: repoRoot,
11241
12425
  stdio: ["ignore", "pipe", "pipe"]
11242
12426
  });
@@ -11591,91 +12775,142 @@ async function create() {
11591
12775
  const progressRenderer = new ProgressRenderer();
11592
12776
  let depInstallShown = false;
11593
12777
  let seenAwaitingInput = false;
12778
+ let lspWaitStartedAt = null;
12779
+ let lspFellBackNoticeShown = false;
11594
12780
  let depInstallPromise = null;
11595
12781
  let lspInstallPromise = null;
11596
12782
  if (repoRoot) {
11597
12783
  const lspResult = await maybeInstallLspServers({ repoRoot, spinner });
11598
12784
  lspInstallPromise = lspResult.installPromise;
11599
12785
  }
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) {
12786
+ let sigintBusy = false;
12787
+ const onSigint = () => {
12788
+ if (sigintBusy) return;
12789
+ sigintBusy = true;
12790
+ void (async () => {
11623
12791
  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`
12792
+ try {
12793
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
12794
+ const abort = await confirm3({
12795
+ message: "Scan is still running. Abort it? (No = continue in background)",
12796
+ default: false
12797
+ });
12798
+ if (!abort) {
12799
+ console.log(
12800
+ chalk8.dim(
12801
+ "Scan continues on the server \u2014 watch progress in the dashboard; context arrives via the listener."
12802
+ )
12803
+ );
12804
+ process.exit(0);
12805
+ }
12806
+ await apiRequest(`/v1/sync/${syncId}/cancel`, { method: "POST" });
12807
+ console.log(chalk8.yellow("Scan aborted."));
12808
+ process.exit(1);
12809
+ } catch {
12810
+ process.exit(130);
12811
+ }
12812
+ })();
12813
+ };
12814
+ process.on("SIGINT", onSigint);
12815
+ try {
12816
+ while (true) {
12817
+ if (++pollAttempts > MAX_POLL_ATTEMPTS) {
12818
+ spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
12819
+ process.exitCode = 1;
12820
+ return;
12821
+ }
12822
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
12823
+ let syncResult;
12824
+ try {
12825
+ syncResult = await apiRequest(`/v1/sync/${syncId}/status`);
12826
+ pollErrors = 0;
12827
+ } catch (pollErr) {
12828
+ pollErrors++;
12829
+ if (pollErrors >= MAX_POLL_ERRORS) {
12830
+ throw pollErr;
12831
+ }
12832
+ continue;
12833
+ }
12834
+ if (sigintBusy) {
12835
+ continue;
12836
+ }
12837
+ progressRenderer.update(syncResult, spinner);
12838
+ if (!graphOnly && syncResult.status === "in_progress") {
12839
+ if (syncResult.lspWaitState === "awaiting" || syncResult.lspWaitState === "analyzing") {
12840
+ lspWaitStartedAt ??= Date.now();
12841
+ spinner.text = `Analyzing repo for accurate dependency graph\u2026 (${formatElapsed(Date.now() - lspWaitStartedAt)})`;
12842
+ } else if (syncResult.lspWaitState === "fell-back" && !lspFellBackNoticeShown) {
12843
+ lspFellBackNoticeShown = true;
12844
+ spinner.stop();
12845
+ console.log(
12846
+ chalk8.dim(" Typed analysis unavailable \u2014 using fast static analysis for this scan.")
12847
+ );
12848
+ spinner.start();
12849
+ }
12850
+ }
12851
+ if (syncResult.status === "awaiting_input") {
12852
+ seenAwaitingInput = true;
12853
+ }
12854
+ if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
12855
+ spinner.stop();
12856
+ await handleInterview(
12857
+ syncId,
12858
+ syncResult.questionId,
12859
+ syncResult.questionText,
12860
+ syncResult.questionContext ?? void 0,
12861
+ syncResult.discoveryResult?.estimatedInterviewQuestions
11660
12862
  );
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
- )
12863
+ spinner.start("Resuming pipeline...");
12864
+ continue;
12865
+ }
12866
+ const interviewPhaseDone = seenAwaitingInput || syncResult.discoveryResult?.estimatedInterviewQuestions === 0;
12867
+ if (!depInstallShown && repoRoot && repoId && syncResult.discoveryResult?.languages && syncResult.discoveryResult.languages.length > 0 && syncResult.status !== "awaiting_input" && interviewPhaseDone) {
12868
+ depInstallShown = true;
12869
+ const scopedLanguages = new Set(
12870
+ syncResult.discoveryResult.languages.map((l) => l.name.toLowerCase())
11669
12871
  );
12872
+ const result = await maybePromptAndInstallDeps({
12873
+ repoRoot,
12874
+ repoId,
12875
+ scopedLanguages,
12876
+ spinner,
12877
+ resumeText: "Resuming pipeline..."
12878
+ });
12879
+ depInstallPromise = result.installPromise;
12880
+ }
12881
+ if (syncResult.status === "completed") {
12882
+ progressRenderer.finalize();
12883
+ const generatedFiles = syncResult.filesGenerated ?? [];
12884
+ const fileCount = generatedFiles.length;
12885
+ if (fileCount > 0) {
12886
+ const coreCount = generatedFiles.filter(
12887
+ (f) => CORE_FILES.has(f.split("/").pop() ?? f)
12888
+ ).length;
12889
+ const tailoredCount = fileCount - coreCount;
12890
+ spinner.succeed(
12891
+ `Context generation complete \u2014 ${coreCount} core + ${tailoredCount} tailored files`
12892
+ );
12893
+ } else if (graphOnly) {
12894
+ spinner.succeed("Knowledge graph built \u2014 MCP ready (Free plan).");
12895
+ } else {
12896
+ spinner.warn(chalk8.yellow("Pipeline completed but no context files were generated."));
12897
+ console.log(
12898
+ chalk8.yellow(
12899
+ " This may be due to AI throttling or a parsing issue. Try running `repowise create` again."
12900
+ )
12901
+ );
12902
+ }
12903
+ break;
12904
+ }
12905
+ if (syncResult.status === "failed") {
12906
+ progressRenderer.finalize();
12907
+ spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
12908
+ process.exitCode = 1;
12909
+ return;
11670
12910
  }
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
12911
  }
12912
+ } finally {
12913
+ process.removeListener("SIGINT", onSigint);
11679
12914
  }
11680
12915
  if (repoRoot && !graphOnly) {
11681
12916
  spinner.start("Downloading context files from server...");
@@ -11683,7 +12918,7 @@ async function create() {
11683
12918
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
11684
12919
  const files = listResult.data?.files ?? listResult.files ?? [];
11685
12920
  if (files.length > 0) {
11686
- const contextDir = join40(repoRoot, DEFAULT_CONTEXT_FOLDER);
12921
+ const contextDir = join47(repoRoot, DEFAULT_CONTEXT_FOLDER);
11687
12922
  mkdirSync(contextDir, { recursive: true });
11688
12923
  let downloadedCount = 0;
11689
12924
  let failedCount = 0;
@@ -11697,7 +12932,7 @@ async function create() {
11697
12932
  const response = await fetch(presignedUrl);
11698
12933
  if (response.ok) {
11699
12934
  const content = await response.text();
11700
- const filePath = join40(contextDir, file.fileName);
12935
+ const filePath = join47(contextDir, file.fileName);
11701
12936
  mkdirSync(dirname16(filePath), { recursive: true });
11702
12937
  writeFileSync2(filePath, content, "utf-8");
11703
12938
  downloadedCount++;
@@ -11773,8 +13008,8 @@ Files are stored on our servers (not in git). Retry when online.`
11773
13008
  results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
11774
13009
  }
11775
13010
  if (tools.includes("claude-code")) {
11776
- await writeClaudeSubagentHook(repoRoot, contextFolder);
11777
- results.push(" Configured .claude/settings.json (SubagentStart hook)");
13011
+ const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder);
13012
+ results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
11778
13013
  }
11779
13014
  spinner.succeed("AI tools configured");
11780
13015
  console.log(chalk8.dim(results.join("\n")));
@@ -11900,7 +13135,7 @@ Files are stored on our servers (not in git). Retry when online.`
11900
13135
 
11901
13136
  // src/commands/member.ts
11902
13137
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
11903
- import { dirname as dirname17, join as join41, resolve, sep } from "path";
13138
+ import { dirname as dirname17, join as join48, resolve, sep } from "path";
11904
13139
  import chalk9 from "chalk";
11905
13140
  import ora2 from "ora";
11906
13141
  var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
@@ -12054,7 +13289,7 @@ async function member() {
12054
13289
  spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
12055
13290
  const { tools } = await selectAiTools();
12056
13291
  spinner.start("Downloading context files...");
12057
- const contextDir = join41(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13292
+ const contextDir = join48(repoRoot, DEFAULT_CONTEXT_FOLDER2);
12058
13293
  mkdirSync2(contextDir, { recursive: true });
12059
13294
  let downloadedCount = 0;
12060
13295
  let failedCount = 0;
@@ -12125,8 +13360,8 @@ async function member() {
12125
13360
  configured.push(`${created ? "Created" : "Updated"} AGENTS.md`);
12126
13361
  }
12127
13362
  if (tools.includes("claude-code")) {
12128
- await writeClaudeSubagentHook(repoRoot, DEFAULT_CONTEXT_FOLDER2);
12129
- configured.push("Configured .claude/settings.json (SubagentStart hook)");
13363
+ const { relPath } = await writeClaudeHooksToRepo(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13364
+ configured.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
12130
13365
  }
12131
13366
  spinner.succeed("AI tools configured");
12132
13367
  for (const msg of configured) {
@@ -12252,15 +13487,15 @@ import chalk10 from "chalk";
12252
13487
  import ora3 from "ora";
12253
13488
 
12254
13489
  // src/lib/tenant-graph-purge.ts
12255
- import { promises as fs18 } from "fs";
13490
+ import { promises as fs23 } from "fs";
12256
13491
  import { homedir as homedir7 } from "os";
12257
- import { join as join42 } from "path";
13492
+ import { join as join49 } from "path";
12258
13493
  async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
12259
- const graphsDir = join42(home, ".repowise", "graphs");
13494
+ const graphsDir = join49(home, ".repowise", "graphs");
12260
13495
  const result = { kept: [], removed: [] };
12261
13496
  let entries;
12262
13497
  try {
12263
- entries = await fs18.readdir(graphsDir);
13498
+ entries = await fs23.readdir(graphsDir);
12264
13499
  } catch (err) {
12265
13500
  if (err.code === "ENOENT") return result;
12266
13501
  throw err;
@@ -12274,15 +13509,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
12274
13509
  result.kept.push(entry);
12275
13510
  continue;
12276
13511
  }
12277
- const path = join42(graphsDir, entry);
13512
+ const path = join49(graphsDir, entry);
12278
13513
  try {
12279
- const stat7 = await fs18.lstat(path);
12280
- if (stat7.isSymbolicLink()) {
12281
- await fs18.unlink(path);
13514
+ const stat8 = await fs23.lstat(path);
13515
+ if (stat8.isSymbolicLink()) {
13516
+ await fs23.unlink(path);
12282
13517
  result.removed.push(entry);
12283
13518
  continue;
12284
13519
  }
12285
- await fs18.rm(path, { recursive: true, force: true });
13520
+ await fs23.rm(path, { recursive: true, force: true });
12286
13521
  result.removed.push(entry);
12287
13522
  } catch {
12288
13523
  }
@@ -12367,11 +13602,11 @@ async function logout() {
12367
13602
 
12368
13603
  // src/commands/status.ts
12369
13604
  import { readFile as readFile16 } from "fs/promises";
12370
- import { basename as basename3, join as join43 } from "path";
13605
+ import { basename as basename4, join as join50 } from "path";
12371
13606
  async function status() {
12372
13607
  const configDir = getConfigDir2();
12373
- const STATE_PATH = join43(configDir, "listener-state.json");
12374
- const CONFIG_PATH = join43(configDir, "config.json");
13608
+ const STATE_PATH = join50(configDir, "listener-state.json");
13609
+ const CONFIG_PATH = join50(configDir, "config.json");
12375
13610
  let state = null;
12376
13611
  try {
12377
13612
  const data = await readFile16(STATE_PATH, "utf-8");
@@ -12403,7 +13638,7 @@ async function status() {
12403
13638
  const configData = await readFile16(CONFIG_PATH, "utf-8");
12404
13639
  const config2 = JSON.parse(configData);
12405
13640
  for (const repo of config2.repos ?? []) {
12406
- repoNames.set(repo.repoId, basename3(repo.localPath));
13641
+ repoNames.set(repo.repoId, basename4(repo.localPath));
12407
13642
  }
12408
13643
  } catch {
12409
13644
  }
@@ -12419,7 +13654,7 @@ async function status() {
12419
13654
 
12420
13655
  // src/commands/sync.ts
12421
13656
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
12422
- import { dirname as dirname18, join as join44 } from "path";
13657
+ import { dirname as dirname18, join as join51 } from "path";
12423
13658
  import chalk12 from "chalk";
12424
13659
  import ora4 from "ora";
12425
13660
  var POLL_INTERVAL_MS2 = 3e3;
@@ -12568,7 +13803,7 @@ async function sync() {
12568
13803
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
12569
13804
  const files = listResult.data?.files ?? listResult.files ?? [];
12570
13805
  if (files.length > 0) {
12571
- const contextDir = join44(repoRoot, DEFAULT_CONTEXT_FOLDER3);
13806
+ const contextDir = join51(repoRoot, DEFAULT_CONTEXT_FOLDER3);
12572
13807
  mkdirSync3(contextDir, { recursive: true });
12573
13808
  let downloadedCount = 0;
12574
13809
  let failedCount = 0;
@@ -12582,7 +13817,7 @@ async function sync() {
12582
13817
  const response = await fetch(presignedUrl);
12583
13818
  if (response.ok) {
12584
13819
  const content = await response.text();
12585
- const filePath = join44(contextDir, file.fileName);
13820
+ const filePath = join51(contextDir, file.fileName);
12586
13821
  mkdirSync3(dirname18(filePath), { recursive: true });
12587
13822
  writeFileSync4(filePath, content, "utf-8");
12588
13823
  downloadedCount++;
@@ -12603,7 +13838,7 @@ async function sync() {
12603
13838
  }
12604
13839
  try {
12605
13840
  const existingConfig = await getConfig();
12606
- const aiTools = existingConfig.aiTools ?? [];
13841
+ const aiTools = (existingConfig.aiTools ?? []).map((t) => t === "roo-code" ? "kilo" : t).filter((t, i, arr) => arr.indexOf(t) === i);
12607
13842
  if (aiTools.length > 0) {
12608
13843
  const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER3);
12609
13844
  if (contextFiles.length > 0) {
@@ -12834,8 +14069,8 @@ async function config() {
12834
14069
 
12835
14070
  // src/commands/mcp-log.ts
12836
14071
  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";
14072
+ import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
14073
+ import { dirname as dirname19, join as join52 } from "path";
12839
14074
  var FLAG_FILE = "mcp-log.flag";
12840
14075
  var LOG_FILE = "mcp-log.jsonl.enc";
12841
14076
  var KEY_FILE = "mcp-log.key";
@@ -12843,14 +14078,14 @@ var ENDPOINT_FILE = "listener.endpoint";
12843
14078
  var IV_BYTES2 = 12;
12844
14079
  var TAG_BYTES2 = 16;
12845
14080
  function flagPath() {
12846
- return join45(getConfigDir2(), FLAG_FILE);
14081
+ return join52(getConfigDir2(), FLAG_FILE);
12847
14082
  }
12848
14083
  function logPath() {
12849
- return join45(getConfigDir2(), LOG_FILE);
14084
+ return join52(getConfigDir2(), LOG_FILE);
12850
14085
  }
12851
14086
  async function writeFlag(flag) {
12852
14087
  const path = flagPath();
12853
- await mkdir18(dirname19(path), { recursive: true });
14088
+ await mkdir19(dirname19(path), { recursive: true });
12854
14089
  await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
12855
14090
  }
12856
14091
  async function mcpLogOn() {
@@ -12887,14 +14122,14 @@ async function trySendConsentToServer() {
12887
14122
  let apiUrl = null;
12888
14123
  let token = null;
12889
14124
  try {
12890
- const body = await readFile17(join45(getConfigDir2(), "config.json"), "utf-8");
14125
+ const body = await readFile17(join52(getConfigDir2(), "config.json"), "utf-8");
12891
14126
  const parsed = JSON.parse(body);
12892
14127
  apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
12893
14128
  } catch {
12894
14129
  return false;
12895
14130
  }
12896
14131
  try {
12897
- const body = await readFile17(join45(getConfigDir2(), "credentials.json"), "utf-8");
14132
+ const body = await readFile17(join52(getConfigDir2(), "credentials.json"), "utf-8");
12898
14133
  const parsed = JSON.parse(body);
12899
14134
  token = parsed.idToken ?? null;
12900
14135
  } catch {
@@ -12955,7 +14190,7 @@ async function mcpLogStatus() {
12955
14190
  process.stderr.write(`Log file: ${logPath()}
12956
14191
  `);
12957
14192
  try {
12958
- const s = await stat6(logPath());
14193
+ const s = await stat7(logPath());
12959
14194
  process.stderr.write(
12960
14195
  `Log size: ${formatBytes(s.size)} (last modified ${s.mtime.toISOString()})
12961
14196
  `
@@ -12964,7 +14199,7 @@ async function mcpLogStatus() {
12964
14199
  process.stderr.write("Log size: no file yet\n");
12965
14200
  }
12966
14201
  try {
12967
- const endpointBody = await readFile17(join45(getConfigDir2(), ENDPOINT_FILE), "utf-8");
14202
+ const endpointBody = await readFile17(join52(getConfigDir2(), ENDPOINT_FILE), "utf-8");
12968
14203
  const match = /endpoint=([^\n]+)/.exec(endpointBody);
12969
14204
  process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
12970
14205
  `);
@@ -12995,7 +14230,7 @@ async function mcpLogViewingFlags(flags = {}) {
12995
14230
  const key = await readKey();
12996
14231
  if (!key) {
12997
14232
  process.stderr.write(
12998
- `No encryption key at ${join45(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
14233
+ `No encryption key at ${join52(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
12999
14234
  `
13000
14235
  );
13001
14236
  return;
@@ -13041,11 +14276,11 @@ async function mcpLogViewingFlags(flags = {}) {
13041
14276
  }
13042
14277
  }
13043
14278
  if (flags.follow) {
13044
- let lastSize = (await stat6(path)).size;
14279
+ let lastSize = (await stat7(path)).size;
13045
14280
  while (true) {
13046
14281
  await new Promise((resolve5) => setTimeout(resolve5, 1e3));
13047
14282
  try {
13048
- const s = await stat6(path);
14283
+ const s = await stat7(path);
13049
14284
  if (s.size > lastSize) {
13050
14285
  const fresh = await readFile17(path, "utf-8");
13051
14286
  const tail = fresh.slice(lastSize);
@@ -13071,7 +14306,7 @@ async function mcpLogViewingFlags(flags = {}) {
13071
14306
  }
13072
14307
  async function readKey() {
13073
14308
  try {
13074
- const body = await readFile17(join45(getConfigDir2(), KEY_FILE), "utf-8");
14309
+ const body = await readFile17(join52(getConfigDir2(), KEY_FILE), "utf-8");
13075
14310
  const parsed = Buffer.from(body.trim(), "base64");
13076
14311
  if (parsed.length !== 32) return null;
13077
14312
  return parsed;
@@ -13114,8 +14349,8 @@ async function mcpLog(subcommand, flags = {}) {
13114
14349
  import chalk14 from "chalk";
13115
14350
 
13116
14351
  // src/lib/graph-loader.ts
13117
- import { promises as fs19 } from "fs";
13118
- import { join as join46, resolve as resolve2 } from "path";
14352
+ import { promises as fs24 } from "fs";
14353
+ import { join as join53, resolve as resolve2 } from "path";
13119
14354
  import { gunzipSync } from "zlib";
13120
14355
  var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
13121
14356
  var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
@@ -13132,8 +14367,8 @@ var GraphNotFoundError = class extends Error {
13132
14367
  var cache = /* @__PURE__ */ new Map();
13133
14368
  async function loadGraph(repoRoot = process.cwd()) {
13134
14369
  const root = resolve2(repoRoot);
13135
- const gzPath = join46(root, GZIPPED_GRAPH_PATH);
13136
- const plainPath = join46(root, RELATIVE_GRAPH_PATH);
14370
+ const gzPath = join53(root, GZIPPED_GRAPH_PATH);
14371
+ const plainPath = join53(root, RELATIVE_GRAPH_PATH);
13137
14372
  const cached = cache.get(root);
13138
14373
  if (cached) {
13139
14374
  return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
@@ -13141,14 +14376,14 @@ async function loadGraph(repoRoot = process.cwd()) {
13141
14376
  let graphPath = null;
13142
14377
  let raw = null;
13143
14378
  try {
13144
- raw = await fs19.readFile(gzPath);
14379
+ raw = await fs24.readFile(gzPath);
13145
14380
  graphPath = gzPath;
13146
14381
  } catch (err) {
13147
14382
  if (err.code !== "ENOENT") throw err;
13148
14383
  }
13149
14384
  if (!raw) {
13150
14385
  try {
13151
- raw = await fs19.readFile(plainPath);
14386
+ raw = await fs24.readFile(plainPath);
13152
14387
  graphPath = plainPath;
13153
14388
  } catch (err) {
13154
14389
  if (err.code === "ENOENT") {
@@ -13547,14 +14782,14 @@ function registerQueryCommand(program2) {
13547
14782
  }
13548
14783
 
13549
14784
  // src/commands/uninstall.ts
13550
- import { promises as fs23 } from "fs";
14785
+ import { promises as fs28 } from "fs";
13551
14786
  import { homedir as homedir9 } from "os";
13552
- import { join as join50 } from "path";
14787
+ import { join as join57 } from "path";
13553
14788
  import chalk15 from "chalk";
13554
14789
 
13555
14790
  // src/lib/cleanup/marker-blocks.ts
13556
- import { promises as fs20 } from "fs";
13557
- import { join as join47 } from "path";
14791
+ import { promises as fs25 } from "fs";
14792
+ import { join as join54 } from "path";
13558
14793
  var MARKER_START = "<!-- repowise-start -->";
13559
14794
  var MARKER_END = "<!-- repowise-end -->";
13560
14795
  var CONTEXT_FILES = [
@@ -13570,7 +14805,7 @@ var CONTEXT_FILES = [
13570
14805
  async function stripMarkerBlock(filePath) {
13571
14806
  let raw;
13572
14807
  try {
13573
- raw = await fs20.readFile(filePath, "utf-8");
14808
+ raw = await fs25.readFile(filePath, "utf-8");
13574
14809
  } catch (err) {
13575
14810
  if (err.code === "ENOENT")
13576
14811
  return { path: filePath, status: "missing" };
@@ -13585,16 +14820,16 @@ async function stripMarkerBlock(filePath) {
13585
14820
  const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
13586
14821
  const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
13587
14822
  if (stripped.length === 0) {
13588
- await fs20.unlink(filePath);
14823
+ await fs25.unlink(filePath);
13589
14824
  return { path: filePath, status: "deleted" };
13590
14825
  }
13591
- await fs20.writeFile(filePath, stripped + "\n", "utf-8");
14826
+ await fs25.writeFile(filePath, stripped + "\n", "utf-8");
13592
14827
  return { path: filePath, status: "stripped" };
13593
14828
  }
13594
14829
  async function stripAllMarkerBlocks(repoRoot) {
13595
14830
  const out = [];
13596
14831
  for (const relative of CONTEXT_FILES) {
13597
- const full = join47(repoRoot, relative);
14832
+ const full = join54(repoRoot, relative);
13598
14833
  const result = await stripMarkerBlock(full).catch((err) => ({
13599
14834
  path: full,
13600
14835
  status: "untouched",
@@ -13606,25 +14841,25 @@ async function stripAllMarkerBlocks(repoRoot) {
13606
14841
  }
13607
14842
 
13608
14843
  // src/lib/cleanup/mcp-configs.ts
13609
- import { promises as fs21 } from "fs";
13610
- import { join as join48 } from "path";
14844
+ import { promises as fs26 } from "fs";
14845
+ import { join as join55 } from "path";
13611
14846
  function mcpConfigPaths(repoRoot, home) {
13612
14847
  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")
14848
+ join55(repoRoot, ".mcp.json"),
14849
+ join55(repoRoot, ".cursor", "mcp.json"),
14850
+ join55(repoRoot, ".vscode", "mcp.json"),
14851
+ join55(repoRoot, ".roo", "mcp.json"),
14852
+ join55(home, ".cline", "mcp.json"),
14853
+ join55(home, ".codeium", "windsurf", "mcp_config.json"),
14854
+ join55(home, ".gemini", "settings.json"),
14855
+ join55(home, ".codex", "mcp.json"),
14856
+ join55(home, ".roo", "mcp.json")
13622
14857
  ];
13623
14858
  }
13624
14859
  async function removeRepowiseFromConfig(path, serverName) {
13625
14860
  let raw;
13626
14861
  try {
13627
- raw = await fs21.readFile(path, "utf-8");
14862
+ raw = await fs26.readFile(path, "utf-8");
13628
14863
  } catch (err) {
13629
14864
  if (err.code === "ENOENT") return { path, status: "not-found" };
13630
14865
  return { path, status: "error", error: err.message };
@@ -13644,7 +14879,7 @@ async function removeRepowiseFromConfig(path, serverName) {
13644
14879
  } else {
13645
14880
  next.mcpServers = servers;
13646
14881
  }
13647
- await fs21.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
14882
+ await fs26.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
13648
14883
  return { path, status: "removed" };
13649
14884
  }
13650
14885
  async function removeAllMcpEntries(repoRoot, home, repoId) {
@@ -13657,17 +14892,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
13657
14892
  }
13658
14893
 
13659
14894
  // src/lib/cleanup/local-state.ts
13660
- import { promises as fs22 } from "fs";
14895
+ import { promises as fs27 } from "fs";
13661
14896
  import { homedir as homedir8 } from "os";
13662
- import { join as join49, resolve as resolve3 } from "path";
14897
+ import { join as join56, resolve as resolve3 } from "path";
13663
14898
  async function clearLocalState(homeOverride) {
13664
14899
  const home = homeOverride ?? homedir8();
13665
- const target = resolve3(join49(home, ".repowise"));
14900
+ const target = resolve3(join56(home, ".repowise"));
13666
14901
  if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
13667
14902
  return { path: target, status: "error", error: "refused: not under home" };
13668
14903
  }
13669
14904
  try {
13670
- await fs22.rm(target, { recursive: true, force: false });
14905
+ await fs27.rm(target, { recursive: true, force: false });
13671
14906
  return { path: target, status: "removed" };
13672
14907
  } catch (err) {
13673
14908
  if (err.code === "ENOENT")
@@ -13706,7 +14941,7 @@ async function uninstall2(opts = {}) {
13706
14941
  else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
13707
14942
  if (tier === "stop") return report;
13708
14943
  try {
13709
- await fs23.unlink(join50(home, ".repowise", "credentials.json"));
14944
+ await fs28.unlink(join57(home, ".repowise", "credentials.json"));
13710
14945
  report.removed.push("credentials");
13711
14946
  } catch (err) {
13712
14947
  if (err.code !== "ENOENT") {
@@ -13733,7 +14968,7 @@ async function uninstall2(opts = {}) {
13733
14968
  const allPaths = mcpConfigPaths(repoRoot, home);
13734
14969
  for (const p of allPaths) {
13735
14970
  try {
13736
- await fs23.access(p);
14971
+ await fs28.access(p);
13737
14972
  } catch {
13738
14973
  }
13739
14974
  }
@@ -13745,7 +14980,7 @@ async function uninstall2(opts = {}) {
13745
14980
  }
13746
14981
  async function defaultLoadRepoIds(home) {
13747
14982
  try {
13748
- const raw = await fs23.readFile(join50(home, ".repowise", "config.json"), "utf-8");
14983
+ const raw = await fs28.readFile(join57(home, ".repowise", "config.json"), "utf-8");
13749
14984
  const parsed = JSON.parse(raw);
13750
14985
  return (parsed.repos ?? []).map((r) => r.repoId);
13751
14986
  } catch {
@@ -13792,12 +15027,12 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
13792
15027
 
13793
15028
  // src/commands/mcp-shim.ts
13794
15029
  init_config_dir();
13795
- import { promises as fs24 } from "fs";
15030
+ import { promises as fs29 } from "fs";
13796
15031
  import { createInterface as createInterface2 } from "readline";
13797
- import { join as join51 } from "path";
15032
+ import { join as join58 } from "path";
13798
15033
  var DEFAULT_MAX = 200 * 1024;
13799
15034
  async function mcpShim(opts) {
13800
- const endpointPath = opts.endpointFile ?? join51(getConfigDir(), "listener.endpoint");
15035
+ const endpointPath = opts.endpointFile ?? join58(getConfigDir(), "listener.endpoint");
13801
15036
  const stdin = opts.stdin ?? process.stdin;
13802
15037
  const stdout = opts.stdout ?? process.stdout;
13803
15038
  const stderr = opts.stderr ?? process.stderr;
@@ -13870,7 +15105,7 @@ async function mcpShim(opts) {
13870
15105
  }
13871
15106
  async function readEndpoint(path) {
13872
15107
  try {
13873
- const raw = (await fs24.readFile(path, "utf-8")).trim();
15108
+ const raw = (await fs29.readFile(path, "utf-8")).trim();
13874
15109
  if (!raw) return null;
13875
15110
  if (raw.startsWith("http://") || raw.startsWith("https://")) {
13876
15111
  return { endpoint: raw.split("\n")[0].trim(), secret: null };
@@ -14110,7 +15345,7 @@ init_registry();
14110
15345
  init_native_installer();
14111
15346
  init_coursier_installer();
14112
15347
  init_toolchain_installer();
14113
- import { spawn as spawn11 } from "child_process";
15348
+ import { spawn as spawn12 } from "child_process";
14114
15349
  import { resolve as resolve4 } from "path";
14115
15350
  import chalk16 from "chalk";
14116
15351
  async function isOnPath(command) {
@@ -14118,7 +15353,7 @@ async function isOnPath(command) {
14118
15353
  const isWin = process.platform === "win32";
14119
15354
  const probeCmd = isWin ? "where" : "which";
14120
15355
  return new Promise((resolve5) => {
14121
- const child = spawn11(probeCmd, [command], { stdio: "ignore" });
15356
+ const child = spawn12(probeCmd, [command], { stdio: "ignore" });
14122
15357
  child.on("close", (code) => {
14123
15358
  resolve5(code === 0);
14124
15359
  });
@@ -14285,13 +15520,13 @@ function formatInstallLine(lang, outcome) {
14285
15520
  }
14286
15521
  var MAX_KEEP_WARM_MINUTES = 240;
14287
15522
  async function autoDetectLanguages(cwd) {
14288
- const { readdir: readdir4 } = await import("fs/promises");
15523
+ const { readdir: readdir6 } = await import("fs/promises");
14289
15524
  const seen = /* @__PURE__ */ new Set();
14290
15525
  async function walk(dir, depth) {
14291
15526
  if (depth > 1) return;
14292
15527
  let entries;
14293
15528
  try {
14294
- entries = await readdir4(dir, { withFileTypes: true });
15529
+ entries = await readdir6(dir, { withFileTypes: true });
14295
15530
  } catch {
14296
15531
  return;
14297
15532
  }
@@ -14546,7 +15781,7 @@ async function lspOn() {
14546
15781
  // bin/repowise.ts
14547
15782
  var __filename = fileURLToPath4(import.meta.url);
14548
15783
  var __dirname = dirname20(__filename);
14549
- var pkg = JSON.parse(readFileSync3(join52(__dirname, "..", "..", "package.json"), "utf-8"));
15784
+ var pkg = JSON.parse(readFileSync3(join59(__dirname, "..", "..", "package.json"), "utf-8"));
14550
15785
  var program = new Command();
14551
15786
  program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
14552
15787
  await showWelcome(pkg.version);