repowisestage 0.0.58 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/repowise.js +1937 -702
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -25,19 +25,19 @@ var init_config_dir = __esm({
|
|
|
25
25
|
// ../listener/dist/process-manager.js
|
|
26
26
|
import { spawn } from "child_process";
|
|
27
27
|
import { openSync, closeSync } from "fs";
|
|
28
|
-
import { readFile as
|
|
28
|
+
import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink5 } from "fs/promises";
|
|
29
29
|
import { homedir as homedir2 } from "os";
|
|
30
|
-
import { join as
|
|
30
|
+
import { join as join10 } from "path";
|
|
31
31
|
import { createRequire } from "module";
|
|
32
32
|
import { fileURLToPath } from "url";
|
|
33
33
|
function repowiseDir() {
|
|
34
34
|
return getConfigDir();
|
|
35
35
|
}
|
|
36
36
|
function pidPath() {
|
|
37
|
-
return
|
|
37
|
+
return join10(repowiseDir(), "listener.pid");
|
|
38
38
|
}
|
|
39
39
|
function logDirPath() {
|
|
40
|
-
return
|
|
40
|
+
return join10(repowiseDir(), "logs");
|
|
41
41
|
}
|
|
42
42
|
function resolveListenerCommand() {
|
|
43
43
|
try {
|
|
@@ -51,7 +51,7 @@ function resolveListenerCommand() {
|
|
|
51
51
|
}
|
|
52
52
|
async function readPid() {
|
|
53
53
|
try {
|
|
54
|
-
const content = await
|
|
54
|
+
const content = await readFile6(pidPath(), "utf-8");
|
|
55
55
|
const pid = parseInt(content.trim(), 10);
|
|
56
56
|
return Number.isNaN(pid) ? null : pid;
|
|
57
57
|
} catch (err) {
|
|
@@ -75,10 +75,10 @@ async function startBackground() {
|
|
|
75
75
|
return pid2;
|
|
76
76
|
}
|
|
77
77
|
const logDir2 = logDirPath();
|
|
78
|
-
await
|
|
78
|
+
await mkdir7(logDir2, { recursive: true });
|
|
79
79
|
const cmd = resolveListenerCommand();
|
|
80
|
-
const stdoutFd = openSync(
|
|
81
|
-
const stderrFd = openSync(
|
|
80
|
+
const stdoutFd = openSync(join10(logDir2, "listener-stdout.log"), "a");
|
|
81
|
+
const stderrFd = openSync(join10(logDir2, "listener-stderr.log"), "a");
|
|
82
82
|
const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
|
|
83
83
|
detached: true,
|
|
84
84
|
stdio: ["ignore", stdoutFd, stderrFd],
|
|
@@ -91,7 +91,7 @@ async function startBackground() {
|
|
|
91
91
|
const pid = child.pid;
|
|
92
92
|
if (!pid)
|
|
93
93
|
throw new Error("Failed to spawn listener process");
|
|
94
|
-
await
|
|
94
|
+
await writeFile7(pidPath(), String(pid));
|
|
95
95
|
return pid;
|
|
96
96
|
}
|
|
97
97
|
async function stopProcess() {
|
|
@@ -126,7 +126,7 @@ async function isRunning() {
|
|
|
126
126
|
}
|
|
127
127
|
async function removePidFile() {
|
|
128
128
|
try {
|
|
129
|
-
await
|
|
129
|
+
await unlink5(pidPath());
|
|
130
130
|
} catch {
|
|
131
131
|
}
|
|
132
132
|
}
|
|
@@ -146,13 +146,13 @@ __export(service_installer_exports, {
|
|
|
146
146
|
stopService: () => stopService,
|
|
147
147
|
uninstall: () => uninstall
|
|
148
148
|
});
|
|
149
|
-
import { execFile as
|
|
150
|
-
import { writeFile as
|
|
149
|
+
import { execFile as execFile4 } from "child_process";
|
|
150
|
+
import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
|
|
151
151
|
import { homedir as homedir3 } from "os";
|
|
152
|
-
import { join as
|
|
152
|
+
import { join as join13 } from "path";
|
|
153
153
|
function exec(cmd, args) {
|
|
154
154
|
return new Promise((resolve5, reject) => {
|
|
155
|
-
|
|
155
|
+
execFile4(cmd, args, (err, stdout) => {
|
|
156
156
|
if (err) {
|
|
157
157
|
reject(err);
|
|
158
158
|
return;
|
|
@@ -162,10 +162,10 @@ function exec(cmd, args) {
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
function plistPath() {
|
|
165
|
-
return
|
|
165
|
+
return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
166
166
|
}
|
|
167
167
|
function logDir() {
|
|
168
|
-
return
|
|
168
|
+
return join13(getConfigDir(), "logs");
|
|
169
169
|
}
|
|
170
170
|
function buildPlist() {
|
|
171
171
|
const cmd = resolveListenerCommand();
|
|
@@ -187,9 +187,9 @@ ${programArgs}
|
|
|
187
187
|
<key>KeepAlive</key>
|
|
188
188
|
<true/>
|
|
189
189
|
<key>StandardOutPath</key>
|
|
190
|
-
<string>${
|
|
190
|
+
<string>${join13(logs, "listener-stdout.log")}</string>
|
|
191
191
|
<key>StandardErrorPath</key>
|
|
192
|
-
<string>${
|
|
192
|
+
<string>${join13(logs, "listener-stderr.log")}</string>
|
|
193
193
|
<key>ProcessType</key>
|
|
194
194
|
<string>Background</string>
|
|
195
195
|
<!--
|
|
@@ -217,13 +217,13 @@ ${programArgs}
|
|
|
217
217
|
</plist>`;
|
|
218
218
|
}
|
|
219
219
|
async function darwinInstall() {
|
|
220
|
-
await
|
|
221
|
-
await
|
|
220
|
+
await mkdir8(logDir(), { recursive: true });
|
|
221
|
+
await mkdir8(join13(homedir3(), "Library", "LaunchAgents"), { recursive: true });
|
|
222
222
|
try {
|
|
223
223
|
await exec("launchctl", ["unload", plistPath()]);
|
|
224
224
|
} catch {
|
|
225
225
|
}
|
|
226
|
-
await
|
|
226
|
+
await writeFile8(plistPath(), buildPlist());
|
|
227
227
|
await exec("launchctl", ["load", plistPath()]);
|
|
228
228
|
}
|
|
229
229
|
async function darwinUninstall() {
|
|
@@ -232,7 +232,7 @@ async function darwinUninstall() {
|
|
|
232
232
|
} catch {
|
|
233
233
|
}
|
|
234
234
|
try {
|
|
235
|
-
await
|
|
235
|
+
await unlink6(plistPath());
|
|
236
236
|
} catch {
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -245,7 +245,7 @@ async function darwinIsInstalled() {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
function unitPath() {
|
|
248
|
-
return
|
|
248
|
+
return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
249
249
|
}
|
|
250
250
|
function buildUnit() {
|
|
251
251
|
const cmd = resolveListenerCommand();
|
|
@@ -261,16 +261,16 @@ Type=simple
|
|
|
261
261
|
ExecStart=${execStart}
|
|
262
262
|
Restart=always
|
|
263
263
|
RestartSec=10
|
|
264
|
-
StandardOutput=append:${
|
|
265
|
-
StandardError=append:${
|
|
264
|
+
StandardOutput=append:${join13(logs, "listener-stdout.log")}
|
|
265
|
+
StandardError=append:${join13(logs, "listener-stderr.log")}
|
|
266
266
|
|
|
267
267
|
[Install]
|
|
268
268
|
WantedBy=default.target`;
|
|
269
269
|
}
|
|
270
270
|
async function linuxInstall() {
|
|
271
|
-
await
|
|
272
|
-
await
|
|
273
|
-
await
|
|
271
|
+
await mkdir8(logDir(), { recursive: true });
|
|
272
|
+
await mkdir8(join13(homedir3(), ".config", "systemd", "user"), { recursive: true });
|
|
273
|
+
await writeFile8(unitPath(), buildUnit());
|
|
274
274
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
275
275
|
await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
|
|
276
276
|
await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
|
|
@@ -285,7 +285,7 @@ async function linuxUninstall() {
|
|
|
285
285
|
} catch {
|
|
286
286
|
}
|
|
287
287
|
try {
|
|
288
|
-
await
|
|
288
|
+
await unlink6(unitPath());
|
|
289
289
|
} catch {
|
|
290
290
|
}
|
|
291
291
|
try {
|
|
@@ -302,7 +302,7 @@ async function linuxIsInstalled() {
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
async function win32Install() {
|
|
305
|
-
await
|
|
305
|
+
await mkdir8(logDir(), { recursive: true });
|
|
306
306
|
const cmd = resolveListenerCommand();
|
|
307
307
|
const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
|
|
308
308
|
await exec("schtasks", [
|
|
@@ -517,7 +517,10 @@ async function probeServers(isAvailable, env = process.env, configOverrides) {
|
|
|
517
517
|
}
|
|
518
518
|
return out;
|
|
519
519
|
}
|
|
520
|
-
|
|
520
|
+
function getBuildSpec(language) {
|
|
521
|
+
return BUILD_SPECS[language] ?? null;
|
|
522
|
+
}
|
|
523
|
+
var LSP_REGISTRY, BUILD_SPECS;
|
|
521
524
|
var init_registry = __esm({
|
|
522
525
|
"../listener/dist/lsp/registry.js"() {
|
|
523
526
|
"use strict";
|
|
@@ -798,7 +801,8 @@ var init_registry = __esm({
|
|
|
798
801
|
extensions: [".swift"],
|
|
799
802
|
lspLanguageId: "swift",
|
|
800
803
|
installHint: "Bundled with Xcode Command Line Tools on macOS",
|
|
801
|
-
toolchainInstallKey: "sourcekit-lsp"
|
|
804
|
+
toolchainInstallKey: "sourcekit-lsp",
|
|
805
|
+
requiresBuild: true
|
|
802
806
|
}
|
|
803
807
|
],
|
|
804
808
|
dart: [
|
|
@@ -913,6 +917,17 @@ var init_registry = __esm({
|
|
|
913
917
|
}
|
|
914
918
|
]
|
|
915
919
|
};
|
|
920
|
+
BUILD_SPECS = {
|
|
921
|
+
swift: {
|
|
922
|
+
gateCommands: ["swift", "xcrun"],
|
|
923
|
+
gateExecs: [{ command: "xcode-select", args: ["-p"] }],
|
|
924
|
+
manifests: ["Package.swift", "*.xcodeproj", "*.xcworkspace"],
|
|
925
|
+
platform: "darwin",
|
|
926
|
+
command: "swift",
|
|
927
|
+
args: ["build"],
|
|
928
|
+
indexStoreProbe: ".build/index/store"
|
|
929
|
+
}
|
|
930
|
+
};
|
|
916
931
|
}
|
|
917
932
|
});
|
|
918
933
|
|
|
@@ -1467,12 +1482,12 @@ import { createGunzip } from "zlib";
|
|
|
1467
1482
|
import { spawn as spawn4 } from "child_process";
|
|
1468
1483
|
import { pipeline } from "stream/promises";
|
|
1469
1484
|
import { createHash as createHash2 } from "crypto";
|
|
1470
|
-
import { join as
|
|
1485
|
+
import { join as join15, dirname as dirname5 } from "path";
|
|
1471
1486
|
function getNativeLspDir() {
|
|
1472
|
-
return
|
|
1487
|
+
return join15(getLspInstallDir(), "native");
|
|
1473
1488
|
}
|
|
1474
1489
|
function getNativeLspBinDir() {
|
|
1475
|
-
return
|
|
1490
|
+
return join15(getNativeLspDir(), "bin");
|
|
1476
1491
|
}
|
|
1477
1492
|
async function ensureNativeLspInstalled(lspKey) {
|
|
1478
1493
|
const entry = NATIVE_LSP_VERSIONS[lspKey];
|
|
@@ -1498,15 +1513,15 @@ async function ensureNativeLspInstalled(lspKey) {
|
|
|
1498
1513
|
}
|
|
1499
1514
|
}
|
|
1500
1515
|
const usesWrapper = Boolean(asset.launcherJarGlob && asset.wrapperBinaryName);
|
|
1501
|
-
const versionDir =
|
|
1516
|
+
const versionDir = join15(getNativeLspDir(), lspKey, entry.version);
|
|
1502
1517
|
if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
|
|
1503
1518
|
const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
|
|
1504
|
-
const shimPath =
|
|
1519
|
+
const shimPath = join15(getNativeLspBinDir(), asset.wrapperBinaryName);
|
|
1505
1520
|
if (jarPath && await pathExists(shimPath)) {
|
|
1506
1521
|
return { installed: false, alreadyPresent: true, binaryPath: shimPath };
|
|
1507
1522
|
}
|
|
1508
1523
|
} else {
|
|
1509
|
-
const installedBinary =
|
|
1524
|
+
const installedBinary = join15(versionDir, asset.binaryPath);
|
|
1510
1525
|
if (await pathExists(installedBinary)) {
|
|
1511
1526
|
await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
|
|
1512
1527
|
return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
|
|
@@ -1514,7 +1529,7 @@ async function ensureNativeLspInstalled(lspKey) {
|
|
|
1514
1529
|
}
|
|
1515
1530
|
try {
|
|
1516
1531
|
await fs.mkdir(versionDir, { recursive: true });
|
|
1517
|
-
const tmpDownload =
|
|
1532
|
+
const tmpDownload = join15(versionDir, `.${asset.filename}.download`);
|
|
1518
1533
|
const downloadUrl = entry.source.kind === "github-release" ? `https://github.com/${entry.source.repo}/releases/download/${entry.version}/${asset.filename}` : asset.url ?? "";
|
|
1519
1534
|
if (!downloadUrl) {
|
|
1520
1535
|
return {
|
|
@@ -1556,7 +1571,7 @@ async function ensureNativeLspInstalled(lspKey) {
|
|
|
1556
1571
|
await pruneStaleVersions(lspKey, entry.version);
|
|
1557
1572
|
return { installed: true, alreadyPresent: false, binaryPath: shimPath };
|
|
1558
1573
|
}
|
|
1559
|
-
const installedBinary =
|
|
1574
|
+
const installedBinary = join15(versionDir, asset.binaryPath);
|
|
1560
1575
|
if (!await pathExists(installedBinary)) {
|
|
1561
1576
|
return {
|
|
1562
1577
|
installed: false,
|
|
@@ -1580,7 +1595,7 @@ async function resolveGlobInsideDir(versionDir, glob) {
|
|
|
1580
1595
|
const parts = glob.split("/");
|
|
1581
1596
|
let dir = versionDir;
|
|
1582
1597
|
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
1583
|
-
dir =
|
|
1598
|
+
dir = join15(dir, parts[i]);
|
|
1584
1599
|
}
|
|
1585
1600
|
const pattern = parts[parts.length - 1];
|
|
1586
1601
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
@@ -1593,10 +1608,10 @@ async function resolveGlobInsideDir(versionDir, glob) {
|
|
|
1593
1608
|
}
|
|
1594
1609
|
const matches = entries.filter((e) => re.test(e)).sort();
|
|
1595
1610
|
const last = matches[matches.length - 1];
|
|
1596
|
-
return last ?
|
|
1611
|
+
return last ? join15(dir, last) : null;
|
|
1597
1612
|
}
|
|
1598
1613
|
async function pruneStaleVersions(lspKey, currentVersion) {
|
|
1599
|
-
const lspDir =
|
|
1614
|
+
const lspDir = join15(getNativeLspDir(), lspKey);
|
|
1600
1615
|
let entries;
|
|
1601
1616
|
try {
|
|
1602
1617
|
entries = await fs.readdir(lspDir);
|
|
@@ -1605,7 +1620,7 @@ async function pruneStaleVersions(lspKey, currentVersion) {
|
|
|
1605
1620
|
}
|
|
1606
1621
|
await Promise.all(entries.filter((name) => name !== currentVersion).map(async (name) => {
|
|
1607
1622
|
try {
|
|
1608
|
-
await fs.rm(
|
|
1623
|
+
await fs.rm(join15(lspDir, name), { recursive: true, force: true });
|
|
1609
1624
|
} catch {
|
|
1610
1625
|
}
|
|
1611
1626
|
}));
|
|
@@ -1619,9 +1634,9 @@ function cmdQuote(value) {
|
|
|
1619
1634
|
async function writeJdtlsWrapper(input) {
|
|
1620
1635
|
const binDir = getNativeLspBinDir();
|
|
1621
1636
|
await fs.mkdir(binDir, { recursive: true });
|
|
1622
|
-
const shimPath =
|
|
1637
|
+
const shimPath = join15(binDir, input.binaryName);
|
|
1623
1638
|
const platformCfgDir = process.platform === "darwin" ? "config_mac" : process.platform === "win32" ? "config_win" : "config_linux";
|
|
1624
|
-
const workspaceBase =
|
|
1639
|
+
const workspaceBase = join15(input.versionDir, "workspaces");
|
|
1625
1640
|
const qLauncher = shQuote(input.launcherJarPath);
|
|
1626
1641
|
const qConfig = shQuote(`${input.versionDir}/${platformCfgDir}`);
|
|
1627
1642
|
const qWorkspaceBase = shQuote(workspaceBase);
|
|
@@ -1681,7 +1696,7 @@ async function ensureSymlinkInBin(lspKey, target, sourceAssetPath) {
|
|
|
1681
1696
|
await fs.mkdir(binDir, { recursive: true });
|
|
1682
1697
|
const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
|
|
1683
1698
|
const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
|
|
1684
|
-
const linkPath =
|
|
1699
|
+
const linkPath = join15(binDir, linkName);
|
|
1685
1700
|
try {
|
|
1686
1701
|
await fs.unlink(linkPath);
|
|
1687
1702
|
} catch {
|
|
@@ -1730,12 +1745,12 @@ async function extractAsset(source, destDir, asset) {
|
|
|
1730
1745
|
return;
|
|
1731
1746
|
}
|
|
1732
1747
|
if (filename.endsWith(".gz")) {
|
|
1733
|
-
const outPath =
|
|
1748
|
+
const outPath = join15(destDir, asset.binaryPath);
|
|
1734
1749
|
await fs.mkdir(dirname5(outPath), { recursive: true });
|
|
1735
1750
|
await pipeline((await fs.open(source)).createReadStream(), createGunzip(), createWriteStream(outPath));
|
|
1736
1751
|
return;
|
|
1737
1752
|
}
|
|
1738
|
-
await fs.copyFile(source,
|
|
1753
|
+
await fs.copyFile(source, join15(destDir, asset.binaryPath));
|
|
1739
1754
|
}
|
|
1740
1755
|
async function runExternal(cmd, args) {
|
|
1741
1756
|
return new Promise((resolve5, reject) => {
|
|
@@ -1773,9 +1788,9 @@ var init_native_installer = __esm({
|
|
|
1773
1788
|
// ../listener/dist/lsp/coursier-installer.js
|
|
1774
1789
|
import { promises as fs2 } from "fs";
|
|
1775
1790
|
import { spawn as spawn5 } from "child_process";
|
|
1776
|
-
import { join as
|
|
1791
|
+
import { join as join16 } from "path";
|
|
1777
1792
|
function getCoursierBinDir() {
|
|
1778
|
-
return
|
|
1793
|
+
return join16(getLspInstallDir(), "coursier-bin");
|
|
1779
1794
|
}
|
|
1780
1795
|
async function ensureCoursierLspInstalled(key) {
|
|
1781
1796
|
const entry = COURSIER_LSPS[key];
|
|
@@ -1783,8 +1798,8 @@ async function ensureCoursierLspInstalled(key) {
|
|
|
1783
1798
|
return { installed: false, alreadyPresent: false, skipped: "no-coursier-entry" };
|
|
1784
1799
|
}
|
|
1785
1800
|
const binDir = getCoursierBinDir();
|
|
1786
|
-
const binaryPath =
|
|
1787
|
-
const binaryPathExe =
|
|
1801
|
+
const binaryPath = join16(binDir, entry.appName);
|
|
1802
|
+
const binaryPathExe = join16(binDir, `${entry.appName}.bat`);
|
|
1788
1803
|
if (await pathExists2(binaryPath) || await pathExists2(binaryPathExe)) {
|
|
1789
1804
|
return { installed: false, alreadyPresent: true, binaryPath };
|
|
1790
1805
|
}
|
|
@@ -1797,8 +1812,8 @@ async function ensureCoursierLspInstalled(key) {
|
|
|
1797
1812
|
error: csBootstrap.error ?? csBootstrap.skipped
|
|
1798
1813
|
};
|
|
1799
1814
|
}
|
|
1800
|
-
const csPath =
|
|
1801
|
-
const csPathExe =
|
|
1815
|
+
const csPath = join16(getNativeLspBinDir(), "coursier");
|
|
1816
|
+
const csPathExe = join16(getNativeLspBinDir(), "coursier.exe");
|
|
1802
1817
|
const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
|
|
1803
1818
|
if (!cs) {
|
|
1804
1819
|
return {
|
|
@@ -1864,7 +1879,7 @@ var init_coursier_installer = __esm({
|
|
|
1864
1879
|
import { spawn as spawn6 } from "child_process";
|
|
1865
1880
|
import { promises as fs3 } from "fs";
|
|
1866
1881
|
import { homedir as homedir4 } from "os";
|
|
1867
|
-
import { join as
|
|
1882
|
+
import { join as join17 } from "path";
|
|
1868
1883
|
async function ensureToolchainLspInstalled(key) {
|
|
1869
1884
|
const entry = TOOLCHAIN_LSPS[key];
|
|
1870
1885
|
if (!entry) {
|
|
@@ -1912,7 +1927,7 @@ async function ensureToolchainLspInstalled(key) {
|
|
|
1912
1927
|
error: err instanceof Error ? err.message : String(err)
|
|
1913
1928
|
};
|
|
1914
1929
|
}
|
|
1915
|
-
const binaryPath =
|
|
1930
|
+
const binaryPath = join17(binDir, entry.binaryName);
|
|
1916
1931
|
if (!await pathExists3(binaryPath)) {
|
|
1917
1932
|
return {
|
|
1918
1933
|
installed: false,
|
|
@@ -1929,14 +1944,14 @@ async function resolveToolchainBinDir(entry) {
|
|
|
1929
1944
|
}
|
|
1930
1945
|
if (entry.toolchainBinDir) {
|
|
1931
1946
|
if (entry.toolchain === "go") {
|
|
1932
|
-
const gopath = process.env["GOPATH"] ??
|
|
1933
|
-
return
|
|
1947
|
+
const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
|
|
1948
|
+
return join17(gopath, "bin");
|
|
1934
1949
|
}
|
|
1935
|
-
return
|
|
1950
|
+
return join17(homedir4(), entry.toolchainBinDir);
|
|
1936
1951
|
}
|
|
1937
1952
|
if (entry.toolchain === "gem") {
|
|
1938
1953
|
const userdir = await captureStdout("gem", ["env", "userdir"]);
|
|
1939
|
-
return
|
|
1954
|
+
return join17(userdir.trim(), "bin");
|
|
1940
1955
|
}
|
|
1941
1956
|
throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
|
|
1942
1957
|
}
|
|
@@ -1946,10 +1961,10 @@ function getToolchainBinDirs() {
|
|
|
1946
1961
|
if (entry.bundled || !entry.toolchain || !entry.toolchainBinDir)
|
|
1947
1962
|
continue;
|
|
1948
1963
|
if (entry.toolchain === "go") {
|
|
1949
|
-
const gopath = process.env["GOPATH"] ??
|
|
1950
|
-
dirs.add(
|
|
1964
|
+
const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
|
|
1965
|
+
dirs.add(join17(gopath, "bin"));
|
|
1951
1966
|
} else {
|
|
1952
|
-
dirs.add(
|
|
1967
|
+
dirs.add(join17(homedir4(), entry.toolchainBinDir));
|
|
1953
1968
|
}
|
|
1954
1969
|
}
|
|
1955
1970
|
return [...dirs];
|
|
@@ -2017,20 +2032,20 @@ var init_toolchain_installer = __esm({
|
|
|
2017
2032
|
|
|
2018
2033
|
// ../listener/dist/lsp/installer.js
|
|
2019
2034
|
import { promises as fs4 } from "fs";
|
|
2020
|
-
import { join as
|
|
2035
|
+
import { join as join18, dirname as dirname6 } from "path";
|
|
2021
2036
|
import { spawn as spawn7 } from "child_process";
|
|
2022
2037
|
import lockfile3 from "proper-lockfile";
|
|
2023
2038
|
function getLspInstallDir() {
|
|
2024
|
-
return
|
|
2039
|
+
return join18(getConfigDir(), "lsp-servers");
|
|
2025
2040
|
}
|
|
2026
2041
|
function getLspBinDir() {
|
|
2027
|
-
return
|
|
2042
|
+
return join18(getLspInstallDir(), "node_modules", ".bin");
|
|
2028
2043
|
}
|
|
2029
2044
|
async function ensureNpmLspInstalled(config2) {
|
|
2030
2045
|
if (!config2.npmPackage)
|
|
2031
2046
|
return { installed: false, skipped: "no-npm-package" };
|
|
2032
2047
|
const installDir = getLspInstallDir();
|
|
2033
|
-
const binPath =
|
|
2048
|
+
const binPath = join18(installDir, "node_modules", ".bin", config2.command);
|
|
2034
2049
|
if (await pathExists4(binPath)) {
|
|
2035
2050
|
return { installed: false, skipped: "already-present" };
|
|
2036
2051
|
}
|
|
@@ -2052,7 +2067,7 @@ async function ensureNpmLspInstalled(config2) {
|
|
|
2052
2067
|
if (await pathExists4(binPath)) {
|
|
2053
2068
|
return { installed: false, skipped: "already-present" };
|
|
2054
2069
|
}
|
|
2055
|
-
const pkgJsonPath =
|
|
2070
|
+
const pkgJsonPath = join18(installDir, "package.json");
|
|
2056
2071
|
if (!await pathExists4(pkgJsonPath)) {
|
|
2057
2072
|
await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
|
|
2058
2073
|
}
|
|
@@ -2081,7 +2096,7 @@ async function ensureNpmLspInstalled(config2) {
|
|
|
2081
2096
|
}
|
|
2082
2097
|
async function resolveNpmCommand() {
|
|
2083
2098
|
const nodeDir = dirname6(process.execPath);
|
|
2084
|
-
for (const candidate of [
|
|
2099
|
+
for (const candidate of [join18(nodeDir, "npm"), join18(nodeDir, "npm.cmd")]) {
|
|
2085
2100
|
try {
|
|
2086
2101
|
await fs4.access(candidate);
|
|
2087
2102
|
return candidate;
|
|
@@ -2172,10 +2187,10 @@ async function detectRepoLanguages(repoRoot) {
|
|
|
2172
2187
|
continue;
|
|
2173
2188
|
if (name === "node_modules" || name === "dist" || name === "build")
|
|
2174
2189
|
continue;
|
|
2175
|
-
const sub =
|
|
2190
|
+
const sub = join18(repoRoot, name);
|
|
2176
2191
|
try {
|
|
2177
|
-
const
|
|
2178
|
-
if (
|
|
2192
|
+
const stat8 = await fs4.stat(sub);
|
|
2193
|
+
if (stat8.isDirectory())
|
|
2179
2194
|
await inspect(sub);
|
|
2180
2195
|
} catch {
|
|
2181
2196
|
}
|
|
@@ -2188,8 +2203,8 @@ async function prepareLspServersForRepos(repos, options = {}) {
|
|
|
2188
2203
|
if (!r.localPath)
|
|
2189
2204
|
continue;
|
|
2190
2205
|
try {
|
|
2191
|
-
const
|
|
2192
|
-
if (!
|
|
2206
|
+
const stat8 = await fs4.stat(r.localPath);
|
|
2207
|
+
if (!stat8.isDirectory())
|
|
2193
2208
|
continue;
|
|
2194
2209
|
} catch {
|
|
2195
2210
|
continue;
|
|
@@ -2293,8 +2308,8 @@ __export(sidecar_cache_exports, {
|
|
|
2293
2308
|
persistMergedSidecar: () => persistMergedSidecar,
|
|
2294
2309
|
sidecarCachePaths: () => sidecarCachePaths
|
|
2295
2310
|
});
|
|
2296
|
-
import { mkdir as
|
|
2297
|
-
import { dirname as dirname8, join as
|
|
2311
|
+
import { mkdir as mkdir9, readFile as readFile8, readdir as readdir3, stat as stat2, unlink as unlink8, writeFile as writeFile9 } from "fs/promises";
|
|
2312
|
+
import { dirname as dirname8, join as join19 } from "path";
|
|
2298
2313
|
function repoIdSafe(repoId) {
|
|
2299
2314
|
return /^[A-Za-z0-9_.-]{1,128}$/.test(repoId) && !repoId.startsWith(".");
|
|
2300
2315
|
}
|
|
@@ -2308,15 +2323,15 @@ function sidecarCachePaths(repoId, commitSha) {
|
|
|
2308
2323
|
if (!commitShaSafe(commitSha)) {
|
|
2309
2324
|
throw new Error(`unsafe commitSha: ${commitSha}`);
|
|
2310
2325
|
}
|
|
2311
|
-
const repoDir =
|
|
2312
|
-
const fullPath =
|
|
2326
|
+
const repoDir = join19(getConfigDir(), "typed-resolution", repoId);
|
|
2327
|
+
const fullPath = join19(repoDir, `${commitSha}.jsonl`);
|
|
2313
2328
|
return { fullPath, repoDir };
|
|
2314
2329
|
}
|
|
2315
2330
|
async function persistMergedSidecar(repoId, commitSha, sidecar) {
|
|
2316
2331
|
const { fullPath, repoDir } = sidecarCachePaths(repoId, commitSha);
|
|
2317
|
-
await
|
|
2332
|
+
await mkdir9(repoDir, { recursive: true });
|
|
2318
2333
|
const tmpPath = `${fullPath}.tmp`;
|
|
2319
|
-
await
|
|
2334
|
+
await writeFile9(tmpPath, JSON.stringify(sidecar), "utf-8");
|
|
2320
2335
|
const { rename: rename5 } = await import("fs/promises");
|
|
2321
2336
|
await rename5(tmpPath, fullPath);
|
|
2322
2337
|
try {
|
|
@@ -2332,7 +2347,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
2332
2347
|
const withMtime = [];
|
|
2333
2348
|
for (const name of jsonlFiles) {
|
|
2334
2349
|
try {
|
|
2335
|
-
const s = await stat2(
|
|
2350
|
+
const s = await stat2(join19(repoDir, name));
|
|
2336
2351
|
withMtime.push({ name, mtimeMs: s.mtimeMs });
|
|
2337
2352
|
} catch {
|
|
2338
2353
|
}
|
|
@@ -2343,7 +2358,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
2343
2358
|
for (const { name, mtimeMs } of candidates) {
|
|
2344
2359
|
if (now - mtimeMs >= SWEEP_MAX_AGE_MS) {
|
|
2345
2360
|
try {
|
|
2346
|
-
await
|
|
2361
|
+
await unlink8(join19(repoDir, name));
|
|
2347
2362
|
} catch {
|
|
2348
2363
|
}
|
|
2349
2364
|
}
|
|
@@ -2369,7 +2384,7 @@ async function loadMergedSidecar(repoId, commitSha) {
|
|
|
2369
2384
|
let body;
|
|
2370
2385
|
try {
|
|
2371
2386
|
const { fullPath } = sidecarCachePaths(repoId, commitSha);
|
|
2372
|
-
body = await
|
|
2387
|
+
body = await readFile8(fullPath, "utf-8");
|
|
2373
2388
|
} catch {
|
|
2374
2389
|
return null;
|
|
2375
2390
|
}
|
|
@@ -2954,7 +2969,7 @@ async function postLspInstallOutcomes(input) {
|
|
|
2954
2969
|
const ctrl = new AbortController();
|
|
2955
2970
|
const timer = setTimeout(() => {
|
|
2956
2971
|
ctrl.abort();
|
|
2957
|
-
},
|
|
2972
|
+
}, REQUEST_TIMEOUT_MS3);
|
|
2958
2973
|
try {
|
|
2959
2974
|
const res = await fetchImpl(url, {
|
|
2960
2975
|
method: "POST",
|
|
@@ -2989,30 +3004,30 @@ async function postLspInstallOutcomes(input) {
|
|
|
2989
3004
|
console.warn(`[lsp-telemetry] unexpected failure: ${err instanceof Error ? err.message : String(err)}`);
|
|
2990
3005
|
}
|
|
2991
3006
|
}
|
|
2992
|
-
var
|
|
3007
|
+
var REQUEST_TIMEOUT_MS3;
|
|
2993
3008
|
var init_telemetry = __esm({
|
|
2994
3009
|
"../listener/dist/lsp/telemetry.js"() {
|
|
2995
3010
|
"use strict";
|
|
2996
|
-
|
|
3011
|
+
REQUEST_TIMEOUT_MS3 = 5e3;
|
|
2997
3012
|
}
|
|
2998
3013
|
});
|
|
2999
3014
|
|
|
3000
3015
|
// bin/repowise.ts
|
|
3001
3016
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3002
3017
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3003
|
-
import { dirname as dirname20, join as
|
|
3018
|
+
import { dirname as dirname20, join as join59 } from "path";
|
|
3004
3019
|
import { Command } from "commander";
|
|
3005
3020
|
|
|
3006
3021
|
// ../listener/dist/main.js
|
|
3007
3022
|
init_config_dir();
|
|
3008
|
-
import { readFile as
|
|
3009
|
-
import { join as
|
|
3023
|
+
import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir16, stat as fsStat } from "fs/promises";
|
|
3024
|
+
import { join as join40, dirname as dirname15 } from "path";
|
|
3010
3025
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3011
3026
|
import lockfile4 from "proper-lockfile";
|
|
3012
3027
|
|
|
3013
3028
|
// ../../packages/shared/dist/lib/ai-tools.js
|
|
3014
|
-
import { readFile, writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
|
|
3015
|
-
import { join as
|
|
3029
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, readdir, stat, unlink as unlink2 } from "fs/promises";
|
|
3030
|
+
import { join as join3, dirname } from "path";
|
|
3016
3031
|
|
|
3017
3032
|
// ../../packages/shared/dist/lib/project-overview-injection.js
|
|
3018
3033
|
var START_MARKER = "<!-- repowise:agent-instructions:start -->";
|
|
@@ -3029,7 +3044,7 @@ Treat RepoWise as your default starting point for every task that touches this c
|
|
|
3029
3044
|
1. **Always start with RepoWise.** Before any task \u2014 questions, planning, coding, debugging, refactoring, tests, code review \u2014 gather context from RepoWise FIRST. Skipping it leads to wrong patterns, duplicated abstractions, and missed constraints.
|
|
3030
3045
|
|
|
3031
3046
|
2. **Use the three-tier hierarchy in this order:**
|
|
3032
|
-
- **Tier 1 \u2014 RepoWise MCP** for surgical structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`
|
|
3047
|
+
- **Tier 1 \u2014 RepoWise MCP** for surgical structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`find_symbol\`, \`get_impact\`, \`lsp_definition\`, etc. \u2014 the server is named "RepoWise MCP for <repo>", so hosts that prefix tool names expose them accordingly). Each tool's description tells you when to use it.
|
|
3033
3048
|
- **Tier 2 \u2014 Context files in \`repowise-context/\`** for narrative context: how a domain is designed, what patterns exist, what conventions to follow. Use the *Context File Routing Map* below to pick which files to read.
|
|
3034
3049
|
- **Tier 3 \u2014 Native code search** (\`read_file\` / \`grep\` / \`glob\` on raw source) ONLY when tiers 1-2 don't cover the question, or for ephemeral state (recent commits, in-flight branches).
|
|
3035
3050
|
|
|
@@ -3055,7 +3070,7 @@ Treat RepoWise as your default starting point for every task that touches this c
|
|
|
3055
3070
|
|
|
3056
3071
|
_Powered by RepoWise._
|
|
3057
3072
|
|
|
3058
|
-
That's it \u2014 one short line, no longer preamble. The host's tool-call UI already shows
|
|
3073
|
+
That's it \u2014 one short line, no longer preamble. The host's tool-call UI already shows RepoWise MCP calls visibly; this tag adds the same surface for context-file reads. If you didn't use RepoWise on this turn, omit the tag \u2014 never fake it.
|
|
3059
3074
|
|
|
3060
3075
|
8. **Surface MCP gaps explicitly.** If RepoWise MCP is unavailable (no \`.mcp.json\` configured, listener not running), tell the user \u2014 don't silently fall back to \`grep\`.
|
|
3061
3076
|
${END_MARKER}`;
|
|
@@ -3084,7 +3099,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,85 +9563,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
|
|
|
8733
9563
|
const breakerOpenEvents = {};
|
|
8734
9564
|
const pathProbeCache = /* @__PURE__ */ new Map();
|
|
8735
9565
|
const missingBinaryLanguages = /* @__PURE__ */ new Set();
|
|
8736
|
-
const
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
9566
|
+
const resolveOneReceiver = async (receiver) => {
|
|
9567
|
+
const cached = warmCacheIndex.get(typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column));
|
|
9568
|
+
if (cached) {
|
|
9569
|
+
warmHits += 1;
|
|
9570
|
+
return cached.targetSymbolId;
|
|
9571
|
+
}
|
|
9572
|
+
const language = detectLanguage(receiver.filePath);
|
|
9573
|
+
if (!language)
|
|
9574
|
+
return null;
|
|
9575
|
+
seenLanguages.add(language);
|
|
9576
|
+
if (trippedLanguages.has(language))
|
|
9577
|
+
return null;
|
|
9578
|
+
if (missingBinaryLanguages.has(language))
|
|
9579
|
+
return null;
|
|
9580
|
+
const config2 = getEffectiveConfig(language, process.env, input.lspOverrides);
|
|
9581
|
+
if (!config2)
|
|
9582
|
+
return null;
|
|
9583
|
+
let onPath = pathProbeCache.get(language);
|
|
9584
|
+
if (onPath === void 0) {
|
|
9585
|
+
const probe = input.isOnPath ?? isCommandOnPath4;
|
|
9586
|
+
onPath = await probe(config2.command);
|
|
9587
|
+
pathProbeCache.set(language, onPath);
|
|
9588
|
+
if (!onPath) {
|
|
9589
|
+
missingBinaryLanguages.add(language);
|
|
9590
|
+
console.warn(`[typed-resolution] ${language}: ${config2.command} not on PATH \u2014 skipping LSP upgrade for this cycle. install: ${config2.installHint}`);
|
|
8750
9591
|
}
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
onPath = await probe(config2.command);
|
|
8766
|
-
pathProbeCache.set(language, onPath);
|
|
8767
|
-
if (!onPath) {
|
|
8768
|
-
missingBinaryLanguages.add(language);
|
|
8769
|
-
console.warn(`[typed-resolution] ${language}: ${config2.command} not on PATH \u2014 skipping LSP upgrade for this cycle. install: ${config2.installHint}`);
|
|
9592
|
+
}
|
|
9593
|
+
if (!onPath)
|
|
9594
|
+
return null;
|
|
9595
|
+
try {
|
|
9596
|
+
const session = await input.workspaces.getOrOpen({
|
|
9597
|
+
repoRoot: input.repoRoot,
|
|
9598
|
+
language,
|
|
9599
|
+
config: config2
|
|
9600
|
+
});
|
|
9601
|
+
const uri = await input.workspaces.ensureOpen(session, receiver.filePath);
|
|
9602
|
+
let methodErrorsForReceiver = 0;
|
|
9603
|
+
const attempt = await resolveReceiverViaStrategy(session, uri, receiver, input.repoRoot, {
|
|
9604
|
+
onMethodError: () => {
|
|
9605
|
+
methodErrorsForReceiver += 1;
|
|
8770
9606
|
}
|
|
8771
|
-
}
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
repoRoot: input.repoRoot,
|
|
8777
|
-
language,
|
|
8778
|
-
config: config2
|
|
8779
|
-
});
|
|
8780
|
-
const uri = await input.workspaces.ensureOpen(session, receiver.filePath);
|
|
8781
|
-
let methodErrorsForReceiver = 0;
|
|
8782
|
-
const attempt = await resolveReceiverViaStrategy(session, uri, receiver, input.repoRoot, {
|
|
8783
|
-
onMethodError: () => {
|
|
8784
|
-
methodErrorsForReceiver += 1;
|
|
8785
|
-
}
|
|
8786
|
-
});
|
|
8787
|
-
const hasResult = attempt !== null && (attempt.targetSymbolId !== null || attempt.targets.length > 0);
|
|
8788
|
-
if (hasResult) {
|
|
8789
|
-
consecutiveFailures.set(language, 0);
|
|
8790
|
-
} else if (methodErrorsForReceiver > 0) {
|
|
8791
|
-
const next = (consecutiveFailures.get(language) ?? 0) + 1;
|
|
8792
|
-
consecutiveFailures.set(language, next);
|
|
8793
|
-
if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
|
|
8794
|
-
trippedLanguages.add(language);
|
|
8795
|
-
breakerOpenEvents[language] = (breakerOpenEvents[language] ?? 0) + 1;
|
|
8796
|
-
console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
|
|
8797
|
-
}
|
|
8798
|
-
return null;
|
|
8799
|
-
} else {
|
|
8800
|
-
consecutiveFailures.set(language, 0);
|
|
8801
|
-
return null;
|
|
8802
|
-
}
|
|
8803
|
-
const lookupKey = typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column);
|
|
8804
|
-
const schemaKind = attempt.kind === "definition" || attempt.kind === "implementation" || attempt.kind === "typeDefinition" || attempt.kind === "workspace-symbol-fallback" ? attempt.kind : void 0;
|
|
8805
|
-
telemetryByKey.set(lookupKey, {
|
|
8806
|
-
kind: schemaKind,
|
|
8807
|
-
lspMethod: attempt.method,
|
|
8808
|
-
targets: attempt.targets.length > 0 ? attempt.targets : void 0,
|
|
8809
|
-
lspLatencyMs: attempt.lspLatencyMs,
|
|
8810
|
-
confidence: attempt.targets.length > 1 ? attempt.baseConfidence / attempt.targets.length : attempt.baseConfidence
|
|
8811
|
-
});
|
|
8812
|
-
return attempt.targetSymbolId;
|
|
8813
|
-
} catch (err) {
|
|
8814
|
-
console.warn(`[typed-resolution] LSP query failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
|
|
9607
|
+
});
|
|
9608
|
+
const hasResult = attempt !== null && (attempt.targetSymbolId !== null || attempt.targets.length > 0);
|
|
9609
|
+
if (hasResult) {
|
|
9610
|
+
consecutiveFailures.set(language, 0);
|
|
9611
|
+
} else if (methodErrorsForReceiver > 0) {
|
|
8815
9612
|
const next = (consecutiveFailures.get(language) ?? 0) + 1;
|
|
8816
9613
|
consecutiveFailures.set(language, next);
|
|
8817
9614
|
if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
|
|
@@ -8820,7 +9617,52 @@ async function runProducerCycle(input, fetchImpl = fetch) {
|
|
|
8820
9617
|
console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
|
|
8821
9618
|
}
|
|
8822
9619
|
return null;
|
|
9620
|
+
} else {
|
|
9621
|
+
consecutiveFailures.set(language, 0);
|
|
9622
|
+
return null;
|
|
9623
|
+
}
|
|
9624
|
+
const lookupKey = typedResolutionKey(receiver.filePath, receiver.propertyName, receiver.line, receiver.column);
|
|
9625
|
+
const schemaKind = attempt.kind === "definition" || attempt.kind === "implementation" || attempt.kind === "typeDefinition" || attempt.kind === "workspace-symbol-fallback" ? attempt.kind : void 0;
|
|
9626
|
+
telemetryByKey.set(lookupKey, {
|
|
9627
|
+
kind: schemaKind,
|
|
9628
|
+
lspMethod: attempt.method,
|
|
9629
|
+
targets: attempt.targets.length > 0 ? attempt.targets : void 0,
|
|
9630
|
+
lspLatencyMs: attempt.lspLatencyMs,
|
|
9631
|
+
confidence: attempt.targets.length > 1 ? attempt.baseConfidence / attempt.targets.length : attempt.baseConfidence
|
|
9632
|
+
});
|
|
9633
|
+
return attempt.targetSymbolId;
|
|
9634
|
+
} catch (err) {
|
|
9635
|
+
console.warn(`[typed-resolution] LSP query failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
|
|
9636
|
+
const next = (consecutiveFailures.get(language) ?? 0) + 1;
|
|
9637
|
+
consecutiveFailures.set(language, next);
|
|
9638
|
+
if (next >= BREAKER_TRIP_THRESHOLD && !trippedLanguages.has(language)) {
|
|
9639
|
+
trippedLanguages.add(language);
|
|
9640
|
+
breakerOpenEvents[language] = (breakerOpenEvents[language] ?? 0) + 1;
|
|
9641
|
+
console.warn(`[typed-resolution] breaker tripped for language=${language} after ${next.toString()} consecutive failures \u2014 skipping further LSP calls this cycle`);
|
|
8823
9642
|
}
|
|
9643
|
+
return null;
|
|
9644
|
+
}
|
|
9645
|
+
};
|
|
9646
|
+
let processedCount = 0;
|
|
9647
|
+
let resolvedOkCount = 0;
|
|
9648
|
+
const sidecar = await buildSidecar({
|
|
9649
|
+
receivers,
|
|
9650
|
+
producer: input.producer,
|
|
9651
|
+
languages: [],
|
|
9652
|
+
// populated below by mutating after observation
|
|
9653
|
+
// Item 14 — fan out across languages so pyright + gopls + tsserver
|
|
9654
|
+
// run in parallel. The groupKey is the detected language; serial
|
|
9655
|
+
// within a language so we don't overwhelm a single LSP server.
|
|
9656
|
+
groupKey: (receiver) => detectLanguage(receiver.filePath),
|
|
9657
|
+
// Phase 4 — instrument per-receiver progress for the wait-status
|
|
9658
|
+
// reporter (resolved deltas drive the server's stall verdict).
|
|
9659
|
+
resolve: async (receiver) => {
|
|
9660
|
+
const out = await resolveOneReceiver(receiver);
|
|
9661
|
+
processedCount += 1;
|
|
9662
|
+
if (out !== null)
|
|
9663
|
+
resolvedOkCount += 1;
|
|
9664
|
+
input.onReceiverProcessed?.(processedCount, receivers.length, resolvedOkCount);
|
|
9665
|
+
return out;
|
|
8824
9666
|
}
|
|
8825
9667
|
});
|
|
8826
9668
|
const enriched = sidecar.resolutions.map((entry) => {
|
|
@@ -8879,6 +9721,234 @@ async function runDownloaderCycle(input, fetchImpl = fetch) {
|
|
|
8879
9721
|
return { downloaded: true, edgesUpgraded: result.edgesUpgraded };
|
|
8880
9722
|
}
|
|
8881
9723
|
|
|
9724
|
+
// ../listener/dist/typed-resolution/committed-checkout.js
|
|
9725
|
+
init_config_dir();
|
|
9726
|
+
import { execFile as execFileCb3 } from "child_process";
|
|
9727
|
+
import { createHash as createHash4 } from "crypto";
|
|
9728
|
+
import { mkdir as mkdir15, readdir as readdir5, rm, stat as stat6 } from "fs/promises";
|
|
9729
|
+
import { join as join39 } from "path";
|
|
9730
|
+
import { promisify as promisify6 } from "util";
|
|
9731
|
+
var execFile8 = promisify6(execFileCb3);
|
|
9732
|
+
var GIT_OP_TIMEOUT_MS = 6e4;
|
|
9733
|
+
var MAX_CONCURRENT_WORKTREES = 3;
|
|
9734
|
+
function worktreeBaseDir() {
|
|
9735
|
+
return join39(getConfigDir(), "lsp-worktrees");
|
|
9736
|
+
}
|
|
9737
|
+
async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktreeBaseDir()) {
|
|
9738
|
+
let reaped = 0;
|
|
9739
|
+
let entries;
|
|
9740
|
+
try {
|
|
9741
|
+
entries = await readdir5(base);
|
|
9742
|
+
} catch {
|
|
9743
|
+
return 0;
|
|
9744
|
+
}
|
|
9745
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
9746
|
+
for (const entry of entries) {
|
|
9747
|
+
const dir = join39(base, entry);
|
|
9748
|
+
try {
|
|
9749
|
+
const info = await stat6(dir);
|
|
9750
|
+
if (!info.isDirectory() || info.mtimeMs > cutoff)
|
|
9751
|
+
continue;
|
|
9752
|
+
await rm(dir, { recursive: true, force: true });
|
|
9753
|
+
reaped += 1;
|
|
9754
|
+
} catch {
|
|
9755
|
+
}
|
|
9756
|
+
}
|
|
9757
|
+
if (reaped > 0) {
|
|
9758
|
+
console.log(`[typed-resolution] reaped ${reaped.toString()} orphaned LSP worktree(s)`);
|
|
9759
|
+
}
|
|
9760
|
+
return reaped;
|
|
9761
|
+
}
|
|
9762
|
+
async function prepareCommittedTree(repoRoot, commitSha, options = {}) {
|
|
9763
|
+
const exec2 = options.execImpl ?? execFile8;
|
|
9764
|
+
const git = (args) => exec2("git", args, { timeout: GIT_OP_TIMEOUT_MS });
|
|
9765
|
+
try {
|
|
9766
|
+
const { stdout: headRaw } = await git(["-C", repoRoot, "rev-parse", "HEAD"]);
|
|
9767
|
+
const head = headRaw.trim();
|
|
9768
|
+
const atCommit = head === commitSha || head.startsWith(commitSha) || commitSha.startsWith(head);
|
|
9769
|
+
if (atCommit) {
|
|
9770
|
+
const { stdout: status2 } = await git(["-C", repoRoot, "status", "--porcelain"]);
|
|
9771
|
+
if (status2.trim().length === 0) {
|
|
9772
|
+
return { indexRoot: repoRoot, reused: true, cleanup: () => Promise.resolve() };
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
} catch {
|
|
9776
|
+
return null;
|
|
9777
|
+
}
|
|
9778
|
+
const base = options.worktreeBase ?? worktreeBaseDir();
|
|
9779
|
+
const dirName = createHash4("sha256").update(`${repoRoot}\0${commitSha}`).digest("hex").slice(0, 16) + "-" + commitSha.slice(0, 12);
|
|
9780
|
+
const worktreePath = join39(base, dirName);
|
|
9781
|
+
try {
|
|
9782
|
+
await mkdir15(base, { recursive: true });
|
|
9783
|
+
await git(["-C", repoRoot, "worktree", "prune"]).catch(() => void 0);
|
|
9784
|
+
const existing = await readdir5(base).catch(() => []);
|
|
9785
|
+
const max = options.maxConcurrentWorktrees ?? MAX_CONCURRENT_WORKTREES;
|
|
9786
|
+
if (!existing.includes(dirName) && existing.length >= max) {
|
|
9787
|
+
console.warn(`[typed-resolution] worktree budget exceeded (${existing.length.toString()}/${max.toString()}) \u2014 proceeding AST-only`);
|
|
9788
|
+
return null;
|
|
9789
|
+
}
|
|
9790
|
+
if (!existing.includes(dirName)) {
|
|
9791
|
+
await git(["-C", repoRoot, "worktree", "add", "--detach", worktreePath, commitSha]);
|
|
9792
|
+
}
|
|
9793
|
+
const cleanup = async () => {
|
|
9794
|
+
try {
|
|
9795
|
+
await exec2("git", ["-C", repoRoot, "worktree", "remove", "--force", worktreePath], {
|
|
9796
|
+
timeout: GIT_OP_TIMEOUT_MS
|
|
9797
|
+
});
|
|
9798
|
+
} catch {
|
|
9799
|
+
}
|
|
9800
|
+
await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
|
|
9801
|
+
await exec2("git", ["-C", repoRoot, "worktree", "prune"], {
|
|
9802
|
+
timeout: GIT_OP_TIMEOUT_MS
|
|
9803
|
+
}).catch(() => void 0);
|
|
9804
|
+
};
|
|
9805
|
+
return { indexRoot: worktreePath, reused: false, cleanup };
|
|
9806
|
+
} catch (err) {
|
|
9807
|
+
console.warn(`[typed-resolution] committed checkout failed \u2014 proceeding AST-only: ${err instanceof Error ? err.message : String(err)}`);
|
|
9808
|
+
await rm(worktreePath, { recursive: true, force: true }).catch(() => void 0);
|
|
9809
|
+
return null;
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
|
|
9813
|
+
// ../listener/dist/typed-resolution/status-reporter.js
|
|
9814
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
9815
|
+
var DEFAULT_INTERVAL_MS = 6e4;
|
|
9816
|
+
var NO_WAIT_STOP_THRESHOLD = 2;
|
|
9817
|
+
function createStatusReporter(options) {
|
|
9818
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
9819
|
+
const url = `${options.apiUrl.replace(/\/+$/, "")}/v1/listeners/typed-resolution-status`;
|
|
9820
|
+
let timer = null;
|
|
9821
|
+
let pollSeq = 0;
|
|
9822
|
+
let lastResolvedCount = 0;
|
|
9823
|
+
let consecutiveNoWait = 0;
|
|
9824
|
+
let stopped = false;
|
|
9825
|
+
let lastPostFailed = false;
|
|
9826
|
+
async function post(state, snapshot, resolvedDelta) {
|
|
9827
|
+
pollSeq += 1;
|
|
9828
|
+
const body = JSON.stringify({
|
|
9829
|
+
repoId: options.repoId,
|
|
9830
|
+
commitSha: options.commitSha,
|
|
9831
|
+
state,
|
|
9832
|
+
resolvedDelta,
|
|
9833
|
+
remainingReceivers: snapshot.remainingReceivers,
|
|
9834
|
+
indexingActive: snapshot.indexingActive,
|
|
9835
|
+
pollSeq
|
|
9836
|
+
});
|
|
9837
|
+
const attempt = async (token) => {
|
|
9838
|
+
const ctrl = new AbortController();
|
|
9839
|
+
const t = setTimeout(() => {
|
|
9840
|
+
ctrl.abort();
|
|
9841
|
+
}, REQUEST_TIMEOUT_MS);
|
|
9842
|
+
try {
|
|
9843
|
+
return await fetchImpl(url, {
|
|
9844
|
+
method: "POST",
|
|
9845
|
+
headers: {
|
|
9846
|
+
"Content-Type": "application/json",
|
|
9847
|
+
Authorization: `Bearer ${token}`
|
|
9848
|
+
},
|
|
9849
|
+
body,
|
|
9850
|
+
signal: ctrl.signal
|
|
9851
|
+
});
|
|
9852
|
+
} finally {
|
|
9853
|
+
clearTimeout(t);
|
|
9854
|
+
}
|
|
9855
|
+
};
|
|
9856
|
+
lastPostFailed = false;
|
|
9857
|
+
try {
|
|
9858
|
+
let res = await attempt(await options.getAuthToken());
|
|
9859
|
+
if ((res.status === 401 || res.status === 403) && options.getAuthTokenForceRefresh) {
|
|
9860
|
+
res = await attempt(await options.getAuthTokenForceRefresh());
|
|
9861
|
+
}
|
|
9862
|
+
if (!res.ok) {
|
|
9863
|
+
lastPostFailed = true;
|
|
9864
|
+
console.warn(`[typed-resolution] status push failed: HTTP ${res.status.toString()} (server silence window is the backstop)`);
|
|
9865
|
+
return;
|
|
9866
|
+
}
|
|
9867
|
+
const payload = await res.json().catch(() => null);
|
|
9868
|
+
const data = payload?.data;
|
|
9869
|
+
if (data?.accepted === false && data.reason === "disabled") {
|
|
9870
|
+
consecutiveNoWait += 1;
|
|
9871
|
+
if (consecutiveNoWait >= NO_WAIT_STOP_THRESHOLD && timer) {
|
|
9872
|
+
clearInterval(timer);
|
|
9873
|
+
timer = null;
|
|
9874
|
+
}
|
|
9875
|
+
} else {
|
|
9876
|
+
consecutiveNoWait = 0;
|
|
9877
|
+
}
|
|
9878
|
+
} catch (err) {
|
|
9879
|
+
lastPostFailed = true;
|
|
9880
|
+
console.warn(`[typed-resolution] status push error (ignored): ${err instanceof Error ? err.message : String(err)}`);
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
function tick() {
|
|
9884
|
+
const snapshot = options.getSnapshot();
|
|
9885
|
+
const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
|
|
9886
|
+
lastResolvedCount = snapshot.resolvedCount;
|
|
9887
|
+
void post(delta > 0 ? "progressing" : "stalled", snapshot, delta);
|
|
9888
|
+
}
|
|
9889
|
+
return {
|
|
9890
|
+
start() {
|
|
9891
|
+
if (timer || stopped)
|
|
9892
|
+
return;
|
|
9893
|
+
tick();
|
|
9894
|
+
timer = setInterval(tick, options.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
9895
|
+
},
|
|
9896
|
+
async reportCompleted() {
|
|
9897
|
+
this.stop();
|
|
9898
|
+
const snapshot = options.getSnapshot();
|
|
9899
|
+
const delta = Math.max(0, snapshot.resolvedCount - lastResolvedCount);
|
|
9900
|
+
await post("completed", snapshot, delta);
|
|
9901
|
+
if (lastPostFailed)
|
|
9902
|
+
await post("completed", snapshot, delta);
|
|
9903
|
+
},
|
|
9904
|
+
async reportCrashed() {
|
|
9905
|
+
this.stop();
|
|
9906
|
+
await post("crashed", options.getSnapshot(), 0);
|
|
9907
|
+
if (lastPostFailed)
|
|
9908
|
+
await post("crashed", options.getSnapshot(), 0);
|
|
9909
|
+
},
|
|
9910
|
+
stop() {
|
|
9911
|
+
stopped = true;
|
|
9912
|
+
if (timer) {
|
|
9913
|
+
clearInterval(timer);
|
|
9914
|
+
timer = null;
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
9917
|
+
};
|
|
9918
|
+
}
|
|
9919
|
+
|
|
9920
|
+
// ../listener/dist/typed-resolution/graph-fetch.js
|
|
9921
|
+
var REQUEST_TIMEOUT_MS2 = 3e4;
|
|
9922
|
+
async function fetchProducerGraph(apiUrl, authToken, repoId, fetchImpl = fetch) {
|
|
9923
|
+
const url = `${apiUrl.replace(/\/+$/, "")}/v1/repos/${encodeURIComponent(repoId)}/graph`;
|
|
9924
|
+
const ctrl = new AbortController();
|
|
9925
|
+
const timer = setTimeout(() => {
|
|
9926
|
+
ctrl.abort();
|
|
9927
|
+
}, REQUEST_TIMEOUT_MS2);
|
|
9928
|
+
try {
|
|
9929
|
+
const res = await fetchImpl(url, {
|
|
9930
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
9931
|
+
signal: ctrl.signal
|
|
9932
|
+
});
|
|
9933
|
+
if (!res.ok) {
|
|
9934
|
+
console.warn(`[typed-resolution] graph fetch failed: HTTP ${res.status.toString()}`);
|
|
9935
|
+
return null;
|
|
9936
|
+
}
|
|
9937
|
+
const parsed = await res.json();
|
|
9938
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
9939
|
+
return null;
|
|
9940
|
+
const graph = parsed;
|
|
9941
|
+
if (typeof graph.commitSha !== "string" || graph.commitSha.length === 0)
|
|
9942
|
+
return null;
|
|
9943
|
+
return graph;
|
|
9944
|
+
} catch (err) {
|
|
9945
|
+
console.warn(`[typed-resolution] graph fetch error: ${err instanceof Error ? err.message : String(err)}`);
|
|
9946
|
+
return null;
|
|
9947
|
+
} finally {
|
|
9948
|
+
clearTimeout(timer);
|
|
9949
|
+
}
|
|
9950
|
+
}
|
|
9951
|
+
|
|
8882
9952
|
// ../listener/dist/typed-resolution/hooks.js
|
|
8883
9953
|
function buildTypedResolutionHooks(options) {
|
|
8884
9954
|
const producer = options.producer ?? "repowise-listener/0.0.0";
|
|
@@ -8921,6 +9991,102 @@ function buildTypedResolutionHooks(options) {
|
|
|
8921
9991
|
...lspOverrides ? { lspOverrides } : {}
|
|
8922
9992
|
});
|
|
8923
9993
|
},
|
|
9994
|
+
async onSidecarRequested(repoId, commitSha, localPath) {
|
|
9995
|
+
if (!await hasBuildableManifest(localPath, "swift")) {
|
|
9996
|
+
return;
|
|
9997
|
+
}
|
|
9998
|
+
console.log(`[typed-resolution] sidecar.requested for ${repoId}@${commitSha.slice(0, 12)} \u2014 preparing Swift build`);
|
|
9999
|
+
const prepared = await prepareCommittedTree(localPath, commitSha);
|
|
10000
|
+
if (!prepared)
|
|
10001
|
+
return;
|
|
10002
|
+
const result = await ensureIndexable(prepared.indexRoot, "swift");
|
|
10003
|
+
if (!result.ready) {
|
|
10004
|
+
console.warn(`[typed-resolution] Swift prep for ${repoId} skipped: ${result.reason ?? "unknown"} (AST-only for swift)`);
|
|
10005
|
+
await prepared.cleanup().catch(() => void 0);
|
|
10006
|
+
}
|
|
10007
|
+
},
|
|
10008
|
+
async onGraphReady(repoId, commitSha, localPath) {
|
|
10009
|
+
const apiUrl = options.repoApiUrls.get(repoId);
|
|
10010
|
+
if (!apiUrl)
|
|
10011
|
+
return;
|
|
10012
|
+
const token = await resolveToken();
|
|
10013
|
+
if (!token)
|
|
10014
|
+
return;
|
|
10015
|
+
const graph = await fetchProducerGraph(apiUrl, token, repoId);
|
|
10016
|
+
const shaMatches = graph !== null && (graph.commitSha === commitSha || graph.commitSha.startsWith(commitSha) || commitSha.startsWith(graph.commitSha));
|
|
10017
|
+
if (!graph || !shaMatches) {
|
|
10018
|
+
console.warn(`[typed-resolution] graph.ready for ${repoId}@${commitSha.slice(0, 12)} but fetched graph is ${graph ? `at ${graph.commitSha.slice(0, 12)}` : "missing"} \u2014 skipping`);
|
|
10019
|
+
return;
|
|
10020
|
+
}
|
|
10021
|
+
const receivers = graph.unresolvedReceivers ?? [];
|
|
10022
|
+
const snapshot = {
|
|
10023
|
+
resolvedCount: 0,
|
|
10024
|
+
remainingReceivers: receivers.length,
|
|
10025
|
+
// Until the first receiver completes, session spawn + server
|
|
10026
|
+
// indexing is the activity signal that keeps the wait alive.
|
|
10027
|
+
indexingActive: true
|
|
10028
|
+
};
|
|
10029
|
+
const reporter = createStatusReporter({
|
|
10030
|
+
apiUrl,
|
|
10031
|
+
repoId,
|
|
10032
|
+
commitSha: graph.commitSha,
|
|
10033
|
+
getAuthToken: options.getAuthToken,
|
|
10034
|
+
...options.getAuthTokenForceRefresh ? { getAuthTokenForceRefresh: options.getAuthTokenForceRefresh } : {},
|
|
10035
|
+
getSnapshot: () => ({ ...snapshot })
|
|
10036
|
+
});
|
|
10037
|
+
reporter.start();
|
|
10038
|
+
let prepared = null;
|
|
10039
|
+
try {
|
|
10040
|
+
if (receivers.length === 0) {
|
|
10041
|
+
await reporter.reportCompleted();
|
|
10042
|
+
return;
|
|
10043
|
+
}
|
|
10044
|
+
prepared = await prepareCommittedTree(localPath, graph.commitSha);
|
|
10045
|
+
if (!prepared) {
|
|
10046
|
+
await reporter.reportCrashed();
|
|
10047
|
+
return;
|
|
10048
|
+
}
|
|
10049
|
+
const buildResult = await ensureIndexable(prepared.indexRoot, "swift", {
|
|
10050
|
+
onActivity: () => {
|
|
10051
|
+
snapshot.indexingActive = true;
|
|
10052
|
+
}
|
|
10053
|
+
});
|
|
10054
|
+
if (!buildResult.ready) {
|
|
10055
|
+
console.warn(`[typed-resolution] swift build unavailable for ${repoId}: ${buildResult.reason ?? "unknown"} \u2014 continuing without it`);
|
|
10056
|
+
}
|
|
10057
|
+
const lspOverrides = options.getLspOverrides?.();
|
|
10058
|
+
const result = await runProducerCycle({
|
|
10059
|
+
apiUrl,
|
|
10060
|
+
authToken: token,
|
|
10061
|
+
repoId,
|
|
10062
|
+
repoRoot: prepared.indexRoot,
|
|
10063
|
+
graph,
|
|
10064
|
+
workspaces: options.workspaces,
|
|
10065
|
+
producer,
|
|
10066
|
+
...lspOverrides ? { lspOverrides } : {},
|
|
10067
|
+
onReceiverProcessed: (processed, total, resolvedCount) => {
|
|
10068
|
+
snapshot.resolvedCount = resolvedCount;
|
|
10069
|
+
snapshot.remainingReceivers = Math.max(0, total - processed);
|
|
10070
|
+
snapshot.indexingActive = false;
|
|
10071
|
+
}
|
|
10072
|
+
});
|
|
10073
|
+
if (result) {
|
|
10074
|
+
await reporter.reportCompleted();
|
|
10075
|
+
} else {
|
|
10076
|
+
await reporter.reportCrashed();
|
|
10077
|
+
}
|
|
10078
|
+
} catch (err) {
|
|
10079
|
+
console.warn(`[typed-resolution] graph.ready producer failed for ${repoId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10080
|
+
await reporter.reportCrashed().catch(() => void 0);
|
|
10081
|
+
} finally {
|
|
10082
|
+
reporter.stop();
|
|
10083
|
+
if (prepared) {
|
|
10084
|
+
await prepared.cleanup().catch((cleanupErr) => {
|
|
10085
|
+
console.warn(`[typed-resolution] worktree cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`);
|
|
10086
|
+
});
|
|
10087
|
+
}
|
|
10088
|
+
}
|
|
10089
|
+
},
|
|
8924
10090
|
async onSidecarMerged(repoId, commitSha) {
|
|
8925
10091
|
const apiUrl = options.repoApiUrls.get(repoId);
|
|
8926
10092
|
if (!apiUrl)
|
|
@@ -8947,8 +10113,8 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
|
|
|
8947
10113
|
var CRASH_LOOP_THRESHOLD = 3;
|
|
8948
10114
|
async function readRawToolConfig() {
|
|
8949
10115
|
try {
|
|
8950
|
-
const configPath =
|
|
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
|
});
|
|
@@ -11591,91 +12775,142 @@ async function create() {
|
|
|
11591
12775
|
const progressRenderer = new ProgressRenderer();
|
|
11592
12776
|
let depInstallShown = false;
|
|
11593
12777
|
let seenAwaitingInput = false;
|
|
12778
|
+
let lspWaitStartedAt = null;
|
|
12779
|
+
let lspFellBackNoticeShown = false;
|
|
11594
12780
|
let depInstallPromise = null;
|
|
11595
12781
|
let lspInstallPromise = null;
|
|
11596
12782
|
if (repoRoot) {
|
|
11597
12783
|
const lspResult = await maybeInstallLspServers({ repoRoot, spinner });
|
|
11598
12784
|
lspInstallPromise = lspResult.installPromise;
|
|
11599
12785
|
}
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
}
|
|
11606
|
-
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
11607
|
-
let syncResult;
|
|
11608
|
-
try {
|
|
11609
|
-
syncResult = await apiRequest(`/v1/sync/${syncId}/status`);
|
|
11610
|
-
pollErrors = 0;
|
|
11611
|
-
} catch (pollErr) {
|
|
11612
|
-
pollErrors++;
|
|
11613
|
-
if (pollErrors >= MAX_POLL_ERRORS) {
|
|
11614
|
-
throw pollErr;
|
|
11615
|
-
}
|
|
11616
|
-
continue;
|
|
11617
|
-
}
|
|
11618
|
-
progressRenderer.update(syncResult, spinner);
|
|
11619
|
-
if (syncResult.status === "awaiting_input") {
|
|
11620
|
-
seenAwaitingInput = true;
|
|
11621
|
-
}
|
|
11622
|
-
if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
|
|
12786
|
+
let sigintBusy = false;
|
|
12787
|
+
const onSigint = () => {
|
|
12788
|
+
if (sigintBusy) return;
|
|
12789
|
+
sigintBusy = true;
|
|
12790
|
+
void (async () => {
|
|
11623
12791
|
spinner.stop();
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
12792
|
+
try {
|
|
12793
|
+
const { confirm: confirm3 } = await import("@inquirer/prompts");
|
|
12794
|
+
const abort = await confirm3({
|
|
12795
|
+
message: "Scan is still running. Abort it? (No = continue in background)",
|
|
12796
|
+
default: false
|
|
12797
|
+
});
|
|
12798
|
+
if (!abort) {
|
|
12799
|
+
console.log(
|
|
12800
|
+
chalk8.dim(
|
|
12801
|
+
"Scan continues on the server \u2014 watch progress in the dashboard; context arrives via the listener."
|
|
12802
|
+
)
|
|
12803
|
+
);
|
|
12804
|
+
process.exit(0);
|
|
12805
|
+
}
|
|
12806
|
+
await apiRequest(`/v1/sync/${syncId}/cancel`, { method: "POST" });
|
|
12807
|
+
console.log(chalk8.yellow("Scan aborted."));
|
|
12808
|
+
process.exit(1);
|
|
12809
|
+
} catch {
|
|
12810
|
+
process.exit(130);
|
|
12811
|
+
}
|
|
12812
|
+
})();
|
|
12813
|
+
};
|
|
12814
|
+
process.on("SIGINT", onSigint);
|
|
12815
|
+
try {
|
|
12816
|
+
while (true) {
|
|
12817
|
+
if (++pollAttempts > MAX_POLL_ATTEMPTS) {
|
|
12818
|
+
spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
|
|
12819
|
+
process.exitCode = 1;
|
|
12820
|
+
return;
|
|
12821
|
+
}
|
|
12822
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
12823
|
+
let syncResult;
|
|
12824
|
+
try {
|
|
12825
|
+
syncResult = await apiRequest(`/v1/sync/${syncId}/status`);
|
|
12826
|
+
pollErrors = 0;
|
|
12827
|
+
} catch (pollErr) {
|
|
12828
|
+
pollErrors++;
|
|
12829
|
+
if (pollErrors >= MAX_POLL_ERRORS) {
|
|
12830
|
+
throw pollErr;
|
|
12831
|
+
}
|
|
12832
|
+
continue;
|
|
12833
|
+
}
|
|
12834
|
+
if (sigintBusy) {
|
|
12835
|
+
continue;
|
|
12836
|
+
}
|
|
12837
|
+
progressRenderer.update(syncResult, spinner);
|
|
12838
|
+
if (!graphOnly && syncResult.status === "in_progress") {
|
|
12839
|
+
if (syncResult.lspWaitState === "awaiting" || syncResult.lspWaitState === "analyzing") {
|
|
12840
|
+
lspWaitStartedAt ??= Date.now();
|
|
12841
|
+
spinner.text = `Analyzing repo for accurate dependency graph\u2026 (${formatElapsed(Date.now() - lspWaitStartedAt)})`;
|
|
12842
|
+
} else if (syncResult.lspWaitState === "fell-back" && !lspFellBackNoticeShown) {
|
|
12843
|
+
lspFellBackNoticeShown = true;
|
|
12844
|
+
spinner.stop();
|
|
12845
|
+
console.log(
|
|
12846
|
+
chalk8.dim(" Typed analysis unavailable \u2014 using fast static analysis for this scan.")
|
|
12847
|
+
);
|
|
12848
|
+
spinner.start();
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
if (syncResult.status === "awaiting_input") {
|
|
12852
|
+
seenAwaitingInput = true;
|
|
12853
|
+
}
|
|
12854
|
+
if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
|
|
12855
|
+
spinner.stop();
|
|
12856
|
+
await handleInterview(
|
|
12857
|
+
syncId,
|
|
12858
|
+
syncResult.questionId,
|
|
12859
|
+
syncResult.questionText,
|
|
12860
|
+
syncResult.questionContext ?? void 0,
|
|
12861
|
+
syncResult.discoveryResult?.estimatedInterviewQuestions
|
|
11660
12862
|
);
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
}
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
)
|
|
12863
|
+
spinner.start("Resuming pipeline...");
|
|
12864
|
+
continue;
|
|
12865
|
+
}
|
|
12866
|
+
const interviewPhaseDone = seenAwaitingInput || syncResult.discoveryResult?.estimatedInterviewQuestions === 0;
|
|
12867
|
+
if (!depInstallShown && repoRoot && repoId && syncResult.discoveryResult?.languages && syncResult.discoveryResult.languages.length > 0 && syncResult.status !== "awaiting_input" && interviewPhaseDone) {
|
|
12868
|
+
depInstallShown = true;
|
|
12869
|
+
const scopedLanguages = new Set(
|
|
12870
|
+
syncResult.discoveryResult.languages.map((l) => l.name.toLowerCase())
|
|
11669
12871
|
);
|
|
12872
|
+
const result = await maybePromptAndInstallDeps({
|
|
12873
|
+
repoRoot,
|
|
12874
|
+
repoId,
|
|
12875
|
+
scopedLanguages,
|
|
12876
|
+
spinner,
|
|
12877
|
+
resumeText: "Resuming pipeline..."
|
|
12878
|
+
});
|
|
12879
|
+
depInstallPromise = result.installPromise;
|
|
12880
|
+
}
|
|
12881
|
+
if (syncResult.status === "completed") {
|
|
12882
|
+
progressRenderer.finalize();
|
|
12883
|
+
const generatedFiles = syncResult.filesGenerated ?? [];
|
|
12884
|
+
const fileCount = generatedFiles.length;
|
|
12885
|
+
if (fileCount > 0) {
|
|
12886
|
+
const coreCount = generatedFiles.filter(
|
|
12887
|
+
(f) => CORE_FILES.has(f.split("/").pop() ?? f)
|
|
12888
|
+
).length;
|
|
12889
|
+
const tailoredCount = fileCount - coreCount;
|
|
12890
|
+
spinner.succeed(
|
|
12891
|
+
`Context generation complete \u2014 ${coreCount} core + ${tailoredCount} tailored files`
|
|
12892
|
+
);
|
|
12893
|
+
} else if (graphOnly) {
|
|
12894
|
+
spinner.succeed("Knowledge graph built \u2014 MCP ready (Free plan).");
|
|
12895
|
+
} else {
|
|
12896
|
+
spinner.warn(chalk8.yellow("Pipeline completed but no context files were generated."));
|
|
12897
|
+
console.log(
|
|
12898
|
+
chalk8.yellow(
|
|
12899
|
+
" This may be due to AI throttling or a parsing issue. Try running `repowise create` again."
|
|
12900
|
+
)
|
|
12901
|
+
);
|
|
12902
|
+
}
|
|
12903
|
+
break;
|
|
12904
|
+
}
|
|
12905
|
+
if (syncResult.status === "failed") {
|
|
12906
|
+
progressRenderer.finalize();
|
|
12907
|
+
spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
|
|
12908
|
+
process.exitCode = 1;
|
|
12909
|
+
return;
|
|
11670
12910
|
}
|
|
11671
|
-
break;
|
|
11672
|
-
}
|
|
11673
|
-
if (syncResult.status === "failed") {
|
|
11674
|
-
progressRenderer.finalize();
|
|
11675
|
-
spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
|
|
11676
|
-
process.exitCode = 1;
|
|
11677
|
-
return;
|
|
11678
12911
|
}
|
|
12912
|
+
} finally {
|
|
12913
|
+
process.removeListener("SIGINT", onSigint);
|
|
11679
12914
|
}
|
|
11680
12915
|
if (repoRoot && !graphOnly) {
|
|
11681
12916
|
spinner.start("Downloading context files from server...");
|
|
@@ -11683,7 +12918,7 @@ async function create() {
|
|
|
11683
12918
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
11684
12919
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
11685
12920
|
if (files.length > 0) {
|
|
11686
|
-
const contextDir =
|
|
12921
|
+
const contextDir = join47(repoRoot, DEFAULT_CONTEXT_FOLDER);
|
|
11687
12922
|
mkdirSync(contextDir, { recursive: true });
|
|
11688
12923
|
let downloadedCount = 0;
|
|
11689
12924
|
let failedCount = 0;
|
|
@@ -11697,7 +12932,7 @@ async function create() {
|
|
|
11697
12932
|
const response = await fetch(presignedUrl);
|
|
11698
12933
|
if (response.ok) {
|
|
11699
12934
|
const content = await response.text();
|
|
11700
|
-
const filePath =
|
|
12935
|
+
const filePath = join47(contextDir, file.fileName);
|
|
11701
12936
|
mkdirSync(dirname16(filePath), { recursive: true });
|
|
11702
12937
|
writeFileSync2(filePath, content, "utf-8");
|
|
11703
12938
|
downloadedCount++;
|
|
@@ -11773,8 +13008,8 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
11773
13008
|
results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
|
|
11774
13009
|
}
|
|
11775
13010
|
if (tools.includes("claude-code")) {
|
|
11776
|
-
await
|
|
11777
|
-
results.push(
|
|
13011
|
+
const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder);
|
|
13012
|
+
results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
|
|
11778
13013
|
}
|
|
11779
13014
|
spinner.succeed("AI tools configured");
|
|
11780
13015
|
console.log(chalk8.dim(results.join("\n")));
|
|
@@ -11900,7 +13135,7 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
11900
13135
|
|
|
11901
13136
|
// src/commands/member.ts
|
|
11902
13137
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
11903
|
-
import { dirname as dirname17, join as
|
|
13138
|
+
import { dirname as dirname17, join as join48, resolve, sep } from "path";
|
|
11904
13139
|
import chalk9 from "chalk";
|
|
11905
13140
|
import ora2 from "ora";
|
|
11906
13141
|
var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
|
|
@@ -12054,7 +13289,7 @@ async function member() {
|
|
|
12054
13289
|
spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
|
|
12055
13290
|
const { tools } = await selectAiTools();
|
|
12056
13291
|
spinner.start("Downloading context files...");
|
|
12057
|
-
const contextDir =
|
|
13292
|
+
const contextDir = join48(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
12058
13293
|
mkdirSync2(contextDir, { recursive: true });
|
|
12059
13294
|
let downloadedCount = 0;
|
|
12060
13295
|
let failedCount = 0;
|
|
@@ -12125,8 +13360,8 @@ async function member() {
|
|
|
12125
13360
|
configured.push(`${created ? "Created" : "Updated"} AGENTS.md`);
|
|
12126
13361
|
}
|
|
12127
13362
|
if (tools.includes("claude-code")) {
|
|
12128
|
-
await
|
|
12129
|
-
configured.push(
|
|
13363
|
+
const { relPath } = await writeClaudeHooksToRepo(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
13364
|
+
configured.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
|
|
12130
13365
|
}
|
|
12131
13366
|
spinner.succeed("AI tools configured");
|
|
12132
13367
|
for (const msg of configured) {
|
|
@@ -12252,15 +13487,15 @@ import chalk10 from "chalk";
|
|
|
12252
13487
|
import ora3 from "ora";
|
|
12253
13488
|
|
|
12254
13489
|
// src/lib/tenant-graph-purge.ts
|
|
12255
|
-
import { promises as
|
|
13490
|
+
import { promises as fs23 } from "fs";
|
|
12256
13491
|
import { homedir as homedir7 } from "os";
|
|
12257
|
-
import { join as
|
|
13492
|
+
import { join as join49 } from "path";
|
|
12258
13493
|
async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
|
|
12259
|
-
const graphsDir =
|
|
13494
|
+
const graphsDir = join49(home, ".repowise", "graphs");
|
|
12260
13495
|
const result = { kept: [], removed: [] };
|
|
12261
13496
|
let entries;
|
|
12262
13497
|
try {
|
|
12263
|
-
entries = await
|
|
13498
|
+
entries = await fs23.readdir(graphsDir);
|
|
12264
13499
|
} catch (err) {
|
|
12265
13500
|
if (err.code === "ENOENT") return result;
|
|
12266
13501
|
throw err;
|
|
@@ -12274,15 +13509,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
|
|
|
12274
13509
|
result.kept.push(entry);
|
|
12275
13510
|
continue;
|
|
12276
13511
|
}
|
|
12277
|
-
const path =
|
|
13512
|
+
const path = join49(graphsDir, entry);
|
|
12278
13513
|
try {
|
|
12279
|
-
const
|
|
12280
|
-
if (
|
|
12281
|
-
await
|
|
13514
|
+
const stat8 = await fs23.lstat(path);
|
|
13515
|
+
if (stat8.isSymbolicLink()) {
|
|
13516
|
+
await fs23.unlink(path);
|
|
12282
13517
|
result.removed.push(entry);
|
|
12283
13518
|
continue;
|
|
12284
13519
|
}
|
|
12285
|
-
await
|
|
13520
|
+
await fs23.rm(path, { recursive: true, force: true });
|
|
12286
13521
|
result.removed.push(entry);
|
|
12287
13522
|
} catch {
|
|
12288
13523
|
}
|
|
@@ -12367,11 +13602,11 @@ async function logout() {
|
|
|
12367
13602
|
|
|
12368
13603
|
// src/commands/status.ts
|
|
12369
13604
|
import { readFile as readFile16 } from "fs/promises";
|
|
12370
|
-
import { basename as
|
|
13605
|
+
import { basename as basename4, join as join50 } from "path";
|
|
12371
13606
|
async function status() {
|
|
12372
13607
|
const configDir = getConfigDir2();
|
|
12373
|
-
const STATE_PATH =
|
|
12374
|
-
const CONFIG_PATH =
|
|
13608
|
+
const STATE_PATH = join50(configDir, "listener-state.json");
|
|
13609
|
+
const CONFIG_PATH = join50(configDir, "config.json");
|
|
12375
13610
|
let state = null;
|
|
12376
13611
|
try {
|
|
12377
13612
|
const data = await readFile16(STATE_PATH, "utf-8");
|
|
@@ -12403,7 +13638,7 @@ async function status() {
|
|
|
12403
13638
|
const configData = await readFile16(CONFIG_PATH, "utf-8");
|
|
12404
13639
|
const config2 = JSON.parse(configData);
|
|
12405
13640
|
for (const repo of config2.repos ?? []) {
|
|
12406
|
-
repoNames.set(repo.repoId,
|
|
13641
|
+
repoNames.set(repo.repoId, basename4(repo.localPath));
|
|
12407
13642
|
}
|
|
12408
13643
|
} catch {
|
|
12409
13644
|
}
|
|
@@ -12419,7 +13654,7 @@ async function status() {
|
|
|
12419
13654
|
|
|
12420
13655
|
// src/commands/sync.ts
|
|
12421
13656
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
12422
|
-
import { dirname as dirname18, join as
|
|
13657
|
+
import { dirname as dirname18, join as join51 } from "path";
|
|
12423
13658
|
import chalk12 from "chalk";
|
|
12424
13659
|
import ora4 from "ora";
|
|
12425
13660
|
var POLL_INTERVAL_MS2 = 3e3;
|
|
@@ -12568,7 +13803,7 @@ async function sync() {
|
|
|
12568
13803
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
12569
13804
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
12570
13805
|
if (files.length > 0) {
|
|
12571
|
-
const contextDir =
|
|
13806
|
+
const contextDir = join51(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
12572
13807
|
mkdirSync3(contextDir, { recursive: true });
|
|
12573
13808
|
let downloadedCount = 0;
|
|
12574
13809
|
let failedCount = 0;
|
|
@@ -12582,7 +13817,7 @@ async function sync() {
|
|
|
12582
13817
|
const response = await fetch(presignedUrl);
|
|
12583
13818
|
if (response.ok) {
|
|
12584
13819
|
const content = await response.text();
|
|
12585
|
-
const filePath =
|
|
13820
|
+
const filePath = join51(contextDir, file.fileName);
|
|
12586
13821
|
mkdirSync3(dirname18(filePath), { recursive: true });
|
|
12587
13822
|
writeFileSync4(filePath, content, "utf-8");
|
|
12588
13823
|
downloadedCount++;
|
|
@@ -12603,7 +13838,7 @@ async function sync() {
|
|
|
12603
13838
|
}
|
|
12604
13839
|
try {
|
|
12605
13840
|
const existingConfig = await getConfig();
|
|
12606
|
-
const aiTools = existingConfig.aiTools ?? [];
|
|
13841
|
+
const aiTools = (existingConfig.aiTools ?? []).map((t) => t === "roo-code" ? "kilo" : t).filter((t, i, arr) => arr.indexOf(t) === i);
|
|
12607
13842
|
if (aiTools.length > 0) {
|
|
12608
13843
|
const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
12609
13844
|
if (contextFiles.length > 0) {
|
|
@@ -12834,8 +14069,8 @@ async function config() {
|
|
|
12834
14069
|
|
|
12835
14070
|
// src/commands/mcp-log.ts
|
|
12836
14071
|
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
12837
|
-
import { mkdir as
|
|
12838
|
-
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";
|
|
12839
14074
|
var FLAG_FILE = "mcp-log.flag";
|
|
12840
14075
|
var LOG_FILE = "mcp-log.jsonl.enc";
|
|
12841
14076
|
var KEY_FILE = "mcp-log.key";
|
|
@@ -12843,14 +14078,14 @@ var ENDPOINT_FILE = "listener.endpoint";
|
|
|
12843
14078
|
var IV_BYTES2 = 12;
|
|
12844
14079
|
var TAG_BYTES2 = 16;
|
|
12845
14080
|
function flagPath() {
|
|
12846
|
-
return
|
|
14081
|
+
return join52(getConfigDir2(), FLAG_FILE);
|
|
12847
14082
|
}
|
|
12848
14083
|
function logPath() {
|
|
12849
|
-
return
|
|
14084
|
+
return join52(getConfigDir2(), LOG_FILE);
|
|
12850
14085
|
}
|
|
12851
14086
|
async function writeFlag(flag) {
|
|
12852
14087
|
const path = flagPath();
|
|
12853
|
-
await
|
|
14088
|
+
await mkdir19(dirname19(path), { recursive: true });
|
|
12854
14089
|
await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
|
|
12855
14090
|
}
|
|
12856
14091
|
async function mcpLogOn() {
|
|
@@ -12887,14 +14122,14 @@ async function trySendConsentToServer() {
|
|
|
12887
14122
|
let apiUrl = null;
|
|
12888
14123
|
let token = null;
|
|
12889
14124
|
try {
|
|
12890
|
-
const body = await readFile17(
|
|
14125
|
+
const body = await readFile17(join52(getConfigDir2(), "config.json"), "utf-8");
|
|
12891
14126
|
const parsed = JSON.parse(body);
|
|
12892
14127
|
apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
|
|
12893
14128
|
} catch {
|
|
12894
14129
|
return false;
|
|
12895
14130
|
}
|
|
12896
14131
|
try {
|
|
12897
|
-
const body = await readFile17(
|
|
14132
|
+
const body = await readFile17(join52(getConfigDir2(), "credentials.json"), "utf-8");
|
|
12898
14133
|
const parsed = JSON.parse(body);
|
|
12899
14134
|
token = parsed.idToken ?? null;
|
|
12900
14135
|
} catch {
|
|
@@ -12955,7 +14190,7 @@ async function mcpLogStatus() {
|
|
|
12955
14190
|
process.stderr.write(`Log file: ${logPath()}
|
|
12956
14191
|
`);
|
|
12957
14192
|
try {
|
|
12958
|
-
const s = await
|
|
14193
|
+
const s = await stat7(logPath());
|
|
12959
14194
|
process.stderr.write(
|
|
12960
14195
|
`Log size: ${formatBytes(s.size)} (last modified ${s.mtime.toISOString()})
|
|
12961
14196
|
`
|
|
@@ -12964,7 +14199,7 @@ async function mcpLogStatus() {
|
|
|
12964
14199
|
process.stderr.write("Log size: no file yet\n");
|
|
12965
14200
|
}
|
|
12966
14201
|
try {
|
|
12967
|
-
const endpointBody = await readFile17(
|
|
14202
|
+
const endpointBody = await readFile17(join52(getConfigDir2(), ENDPOINT_FILE), "utf-8");
|
|
12968
14203
|
const match = /endpoint=([^\n]+)/.exec(endpointBody);
|
|
12969
14204
|
process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
|
|
12970
14205
|
`);
|
|
@@ -12995,7 +14230,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
12995
14230
|
const key = await readKey();
|
|
12996
14231
|
if (!key) {
|
|
12997
14232
|
process.stderr.write(
|
|
12998
|
-
`No encryption key at ${
|
|
14233
|
+
`No encryption key at ${join52(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
|
|
12999
14234
|
`
|
|
13000
14235
|
);
|
|
13001
14236
|
return;
|
|
@@ -13041,11 +14276,11 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
13041
14276
|
}
|
|
13042
14277
|
}
|
|
13043
14278
|
if (flags.follow) {
|
|
13044
|
-
let lastSize = (await
|
|
14279
|
+
let lastSize = (await stat7(path)).size;
|
|
13045
14280
|
while (true) {
|
|
13046
14281
|
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
13047
14282
|
try {
|
|
13048
|
-
const s = await
|
|
14283
|
+
const s = await stat7(path);
|
|
13049
14284
|
if (s.size > lastSize) {
|
|
13050
14285
|
const fresh = await readFile17(path, "utf-8");
|
|
13051
14286
|
const tail = fresh.slice(lastSize);
|
|
@@ -13071,7 +14306,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
13071
14306
|
}
|
|
13072
14307
|
async function readKey() {
|
|
13073
14308
|
try {
|
|
13074
|
-
const body = await readFile17(
|
|
14309
|
+
const body = await readFile17(join52(getConfigDir2(), KEY_FILE), "utf-8");
|
|
13075
14310
|
const parsed = Buffer.from(body.trim(), "base64");
|
|
13076
14311
|
if (parsed.length !== 32) return null;
|
|
13077
14312
|
return parsed;
|
|
@@ -13114,8 +14349,8 @@ async function mcpLog(subcommand, flags = {}) {
|
|
|
13114
14349
|
import chalk14 from "chalk";
|
|
13115
14350
|
|
|
13116
14351
|
// src/lib/graph-loader.ts
|
|
13117
|
-
import { promises as
|
|
13118
|
-
import { join as
|
|
14352
|
+
import { promises as fs24 } from "fs";
|
|
14353
|
+
import { join as join53, resolve as resolve2 } from "path";
|
|
13119
14354
|
import { gunzipSync } from "zlib";
|
|
13120
14355
|
var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
|
|
13121
14356
|
var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
|
|
@@ -13132,8 +14367,8 @@ var GraphNotFoundError = class extends Error {
|
|
|
13132
14367
|
var cache = /* @__PURE__ */ new Map();
|
|
13133
14368
|
async function loadGraph(repoRoot = process.cwd()) {
|
|
13134
14369
|
const root = resolve2(repoRoot);
|
|
13135
|
-
const gzPath =
|
|
13136
|
-
const plainPath =
|
|
14370
|
+
const gzPath = join53(root, GZIPPED_GRAPH_PATH);
|
|
14371
|
+
const plainPath = join53(root, RELATIVE_GRAPH_PATH);
|
|
13137
14372
|
const cached = cache.get(root);
|
|
13138
14373
|
if (cached) {
|
|
13139
14374
|
return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
|
|
@@ -13141,14 +14376,14 @@ async function loadGraph(repoRoot = process.cwd()) {
|
|
|
13141
14376
|
let graphPath = null;
|
|
13142
14377
|
let raw = null;
|
|
13143
14378
|
try {
|
|
13144
|
-
raw = await
|
|
14379
|
+
raw = await fs24.readFile(gzPath);
|
|
13145
14380
|
graphPath = gzPath;
|
|
13146
14381
|
} catch (err) {
|
|
13147
14382
|
if (err.code !== "ENOENT") throw err;
|
|
13148
14383
|
}
|
|
13149
14384
|
if (!raw) {
|
|
13150
14385
|
try {
|
|
13151
|
-
raw = await
|
|
14386
|
+
raw = await fs24.readFile(plainPath);
|
|
13152
14387
|
graphPath = plainPath;
|
|
13153
14388
|
} catch (err) {
|
|
13154
14389
|
if (err.code === "ENOENT") {
|
|
@@ -13547,14 +14782,14 @@ function registerQueryCommand(program2) {
|
|
|
13547
14782
|
}
|
|
13548
14783
|
|
|
13549
14784
|
// src/commands/uninstall.ts
|
|
13550
|
-
import { promises as
|
|
14785
|
+
import { promises as fs28 } from "fs";
|
|
13551
14786
|
import { homedir as homedir9 } from "os";
|
|
13552
|
-
import { join as
|
|
14787
|
+
import { join as join57 } from "path";
|
|
13553
14788
|
import chalk15 from "chalk";
|
|
13554
14789
|
|
|
13555
14790
|
// src/lib/cleanup/marker-blocks.ts
|
|
13556
|
-
import { promises as
|
|
13557
|
-
import { join as
|
|
14791
|
+
import { promises as fs25 } from "fs";
|
|
14792
|
+
import { join as join54 } from "path";
|
|
13558
14793
|
var MARKER_START = "<!-- repowise-start -->";
|
|
13559
14794
|
var MARKER_END = "<!-- repowise-end -->";
|
|
13560
14795
|
var CONTEXT_FILES = [
|
|
@@ -13570,7 +14805,7 @@ var CONTEXT_FILES = [
|
|
|
13570
14805
|
async function stripMarkerBlock(filePath) {
|
|
13571
14806
|
let raw;
|
|
13572
14807
|
try {
|
|
13573
|
-
raw = await
|
|
14808
|
+
raw = await fs25.readFile(filePath, "utf-8");
|
|
13574
14809
|
} catch (err) {
|
|
13575
14810
|
if (err.code === "ENOENT")
|
|
13576
14811
|
return { path: filePath, status: "missing" };
|
|
@@ -13585,16 +14820,16 @@ async function stripMarkerBlock(filePath) {
|
|
|
13585
14820
|
const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
|
|
13586
14821
|
const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
|
|
13587
14822
|
if (stripped.length === 0) {
|
|
13588
|
-
await
|
|
14823
|
+
await fs25.unlink(filePath);
|
|
13589
14824
|
return { path: filePath, status: "deleted" };
|
|
13590
14825
|
}
|
|
13591
|
-
await
|
|
14826
|
+
await fs25.writeFile(filePath, stripped + "\n", "utf-8");
|
|
13592
14827
|
return { path: filePath, status: "stripped" };
|
|
13593
14828
|
}
|
|
13594
14829
|
async function stripAllMarkerBlocks(repoRoot) {
|
|
13595
14830
|
const out = [];
|
|
13596
14831
|
for (const relative of CONTEXT_FILES) {
|
|
13597
|
-
const full =
|
|
14832
|
+
const full = join54(repoRoot, relative);
|
|
13598
14833
|
const result = await stripMarkerBlock(full).catch((err) => ({
|
|
13599
14834
|
path: full,
|
|
13600
14835
|
status: "untouched",
|
|
@@ -13606,25 +14841,25 @@ async function stripAllMarkerBlocks(repoRoot) {
|
|
|
13606
14841
|
}
|
|
13607
14842
|
|
|
13608
14843
|
// src/lib/cleanup/mcp-configs.ts
|
|
13609
|
-
import { promises as
|
|
13610
|
-
import { join as
|
|
14844
|
+
import { promises as fs26 } from "fs";
|
|
14845
|
+
import { join as join55 } from "path";
|
|
13611
14846
|
function mcpConfigPaths(repoRoot, home) {
|
|
13612
14847
|
return [
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
14848
|
+
join55(repoRoot, ".mcp.json"),
|
|
14849
|
+
join55(repoRoot, ".cursor", "mcp.json"),
|
|
14850
|
+
join55(repoRoot, ".vscode", "mcp.json"),
|
|
14851
|
+
join55(repoRoot, ".roo", "mcp.json"),
|
|
14852
|
+
join55(home, ".cline", "mcp.json"),
|
|
14853
|
+
join55(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
14854
|
+
join55(home, ".gemini", "settings.json"),
|
|
14855
|
+
join55(home, ".codex", "mcp.json"),
|
|
14856
|
+
join55(home, ".roo", "mcp.json")
|
|
13622
14857
|
];
|
|
13623
14858
|
}
|
|
13624
14859
|
async function removeRepowiseFromConfig(path, serverName) {
|
|
13625
14860
|
let raw;
|
|
13626
14861
|
try {
|
|
13627
|
-
raw = await
|
|
14862
|
+
raw = await fs26.readFile(path, "utf-8");
|
|
13628
14863
|
} catch (err) {
|
|
13629
14864
|
if (err.code === "ENOENT") return { path, status: "not-found" };
|
|
13630
14865
|
return { path, status: "error", error: err.message };
|
|
@@ -13644,7 +14879,7 @@ async function removeRepowiseFromConfig(path, serverName) {
|
|
|
13644
14879
|
} else {
|
|
13645
14880
|
next.mcpServers = servers;
|
|
13646
14881
|
}
|
|
13647
|
-
await
|
|
14882
|
+
await fs26.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
13648
14883
|
return { path, status: "removed" };
|
|
13649
14884
|
}
|
|
13650
14885
|
async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
@@ -13657,17 +14892,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
|
13657
14892
|
}
|
|
13658
14893
|
|
|
13659
14894
|
// src/lib/cleanup/local-state.ts
|
|
13660
|
-
import { promises as
|
|
14895
|
+
import { promises as fs27 } from "fs";
|
|
13661
14896
|
import { homedir as homedir8 } from "os";
|
|
13662
|
-
import { join as
|
|
14897
|
+
import { join as join56, resolve as resolve3 } from "path";
|
|
13663
14898
|
async function clearLocalState(homeOverride) {
|
|
13664
14899
|
const home = homeOverride ?? homedir8();
|
|
13665
|
-
const target = resolve3(
|
|
14900
|
+
const target = resolve3(join56(home, ".repowise"));
|
|
13666
14901
|
if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
|
|
13667
14902
|
return { path: target, status: "error", error: "refused: not under home" };
|
|
13668
14903
|
}
|
|
13669
14904
|
try {
|
|
13670
|
-
await
|
|
14905
|
+
await fs27.rm(target, { recursive: true, force: false });
|
|
13671
14906
|
return { path: target, status: "removed" };
|
|
13672
14907
|
} catch (err) {
|
|
13673
14908
|
if (err.code === "ENOENT")
|
|
@@ -13706,7 +14941,7 @@ async function uninstall2(opts = {}) {
|
|
|
13706
14941
|
else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
|
|
13707
14942
|
if (tier === "stop") return report;
|
|
13708
14943
|
try {
|
|
13709
|
-
await
|
|
14944
|
+
await fs28.unlink(join57(home, ".repowise", "credentials.json"));
|
|
13710
14945
|
report.removed.push("credentials");
|
|
13711
14946
|
} catch (err) {
|
|
13712
14947
|
if (err.code !== "ENOENT") {
|
|
@@ -13733,7 +14968,7 @@ async function uninstall2(opts = {}) {
|
|
|
13733
14968
|
const allPaths = mcpConfigPaths(repoRoot, home);
|
|
13734
14969
|
for (const p of allPaths) {
|
|
13735
14970
|
try {
|
|
13736
|
-
await
|
|
14971
|
+
await fs28.access(p);
|
|
13737
14972
|
} catch {
|
|
13738
14973
|
}
|
|
13739
14974
|
}
|
|
@@ -13745,7 +14980,7 @@ async function uninstall2(opts = {}) {
|
|
|
13745
14980
|
}
|
|
13746
14981
|
async function defaultLoadRepoIds(home) {
|
|
13747
14982
|
try {
|
|
13748
|
-
const raw = await
|
|
14983
|
+
const raw = await fs28.readFile(join57(home, ".repowise", "config.json"), "utf-8");
|
|
13749
14984
|
const parsed = JSON.parse(raw);
|
|
13750
14985
|
return (parsed.repos ?? []).map((r) => r.repoId);
|
|
13751
14986
|
} catch {
|
|
@@ -13792,12 +15027,12 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
|
|
|
13792
15027
|
|
|
13793
15028
|
// src/commands/mcp-shim.ts
|
|
13794
15029
|
init_config_dir();
|
|
13795
|
-
import { promises as
|
|
15030
|
+
import { promises as fs29 } from "fs";
|
|
13796
15031
|
import { createInterface as createInterface2 } from "readline";
|
|
13797
|
-
import { join as
|
|
15032
|
+
import { join as join58 } from "path";
|
|
13798
15033
|
var DEFAULT_MAX = 200 * 1024;
|
|
13799
15034
|
async function mcpShim(opts) {
|
|
13800
|
-
const endpointPath = opts.endpointFile ??
|
|
15035
|
+
const endpointPath = opts.endpointFile ?? join58(getConfigDir(), "listener.endpoint");
|
|
13801
15036
|
const stdin = opts.stdin ?? process.stdin;
|
|
13802
15037
|
const stdout = opts.stdout ?? process.stdout;
|
|
13803
15038
|
const stderr = opts.stderr ?? process.stderr;
|
|
@@ -13870,7 +15105,7 @@ async function mcpShim(opts) {
|
|
|
13870
15105
|
}
|
|
13871
15106
|
async function readEndpoint(path) {
|
|
13872
15107
|
try {
|
|
13873
|
-
const raw = (await
|
|
15108
|
+
const raw = (await fs29.readFile(path, "utf-8")).trim();
|
|
13874
15109
|
if (!raw) return null;
|
|
13875
15110
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
13876
15111
|
return { endpoint: raw.split("\n")[0].trim(), secret: null };
|
|
@@ -14110,7 +15345,7 @@ init_registry();
|
|
|
14110
15345
|
init_native_installer();
|
|
14111
15346
|
init_coursier_installer();
|
|
14112
15347
|
init_toolchain_installer();
|
|
14113
|
-
import { spawn as
|
|
15348
|
+
import { spawn as spawn12 } from "child_process";
|
|
14114
15349
|
import { resolve as resolve4 } from "path";
|
|
14115
15350
|
import chalk16 from "chalk";
|
|
14116
15351
|
async function isOnPath(command) {
|
|
@@ -14118,7 +15353,7 @@ async function isOnPath(command) {
|
|
|
14118
15353
|
const isWin = process.platform === "win32";
|
|
14119
15354
|
const probeCmd = isWin ? "where" : "which";
|
|
14120
15355
|
return new Promise((resolve5) => {
|
|
14121
|
-
const child =
|
|
15356
|
+
const child = spawn12(probeCmd, [command], { stdio: "ignore" });
|
|
14122
15357
|
child.on("close", (code) => {
|
|
14123
15358
|
resolve5(code === 0);
|
|
14124
15359
|
});
|
|
@@ -14285,13 +15520,13 @@ function formatInstallLine(lang, outcome) {
|
|
|
14285
15520
|
}
|
|
14286
15521
|
var MAX_KEEP_WARM_MINUTES = 240;
|
|
14287
15522
|
async function autoDetectLanguages(cwd) {
|
|
14288
|
-
const { readdir:
|
|
15523
|
+
const { readdir: readdir6 } = await import("fs/promises");
|
|
14289
15524
|
const seen = /* @__PURE__ */ new Set();
|
|
14290
15525
|
async function walk(dir, depth) {
|
|
14291
15526
|
if (depth > 1) return;
|
|
14292
15527
|
let entries;
|
|
14293
15528
|
try {
|
|
14294
|
-
entries = await
|
|
15529
|
+
entries = await readdir6(dir, { withFileTypes: true });
|
|
14295
15530
|
} catch {
|
|
14296
15531
|
return;
|
|
14297
15532
|
}
|
|
@@ -14546,7 +15781,7 @@ async function lspOn() {
|
|
|
14546
15781
|
// bin/repowise.ts
|
|
14547
15782
|
var __filename = fileURLToPath4(import.meta.url);
|
|
14548
15783
|
var __dirname = dirname20(__filename);
|
|
14549
|
-
var pkg = JSON.parse(readFileSync3(
|
|
15784
|
+
var pkg = JSON.parse(readFileSync3(join59(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
14550
15785
|
var program = new Command();
|
|
14551
15786
|
program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
|
|
14552
15787
|
await showWelcome(pkg.version);
|