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