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