repowisestage 0.0.65 → 0.0.67
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 +1488 -1388
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -22,1391 +22,1398 @@ var init_config_dir = __esm({
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink5 } from "fs/promises";
|
|
29
|
-
import { homedir as homedir2 } from "os";
|
|
30
|
-
import { join as join10 } from "path";
|
|
31
|
-
import { createRequire } from "module";
|
|
32
|
-
import { fileURLToPath } from "url";
|
|
33
|
-
function repowiseDir() {
|
|
34
|
-
return getConfigDir();
|
|
35
|
-
}
|
|
36
|
-
function pidPath() {
|
|
37
|
-
return join10(repowiseDir(), "listener.pid");
|
|
38
|
-
}
|
|
39
|
-
function logDirPath() {
|
|
40
|
-
return join10(repowiseDir(), "logs");
|
|
41
|
-
}
|
|
42
|
-
function resolveListenerCommand() {
|
|
43
|
-
try {
|
|
44
|
-
const require2 = createRequire(import.meta.url);
|
|
45
|
-
const mainPath = require2.resolve("@repowise/listener/main");
|
|
46
|
-
return { script: mainPath, args: [] };
|
|
47
|
-
} catch {
|
|
48
|
-
const bundlePath = fileURLToPath(import.meta.url);
|
|
49
|
-
return { script: bundlePath, args: ["__listener"] };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function readPid() {
|
|
53
|
-
try {
|
|
54
|
-
const content = await readFile6(pidPath(), "utf-8");
|
|
55
|
-
const pid = parseInt(content.trim(), 10);
|
|
56
|
-
return Number.isNaN(pid) ? null : pid;
|
|
57
|
-
} catch (err) {
|
|
58
|
-
if (err.code === "ENOENT")
|
|
59
|
-
return null;
|
|
60
|
-
throw err;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function isAlive(pid) {
|
|
64
|
-
try {
|
|
65
|
-
process.kill(pid, 0);
|
|
66
|
-
return true;
|
|
67
|
-
} catch {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async function startBackground() {
|
|
72
|
-
if (await isRunning()) {
|
|
73
|
-
const pid2 = await readPid();
|
|
74
|
-
if (pid2)
|
|
75
|
-
return pid2;
|
|
76
|
-
}
|
|
77
|
-
const logDir2 = logDirPath();
|
|
78
|
-
await mkdir7(logDir2, { recursive: true });
|
|
79
|
-
const cmd = resolveListenerCommand();
|
|
80
|
-
const stdoutFd = openSync(join10(logDir2, "listener-stdout.log"), "a");
|
|
81
|
-
const stderrFd = openSync(join10(logDir2, "listener-stderr.log"), "a");
|
|
82
|
-
const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
|
|
83
|
-
detached: true,
|
|
84
|
-
stdio: ["ignore", stdoutFd, stderrFd],
|
|
85
|
-
cwd: homedir2(),
|
|
86
|
-
env: { ...process.env }
|
|
87
|
-
});
|
|
88
|
-
child.unref();
|
|
89
|
-
closeSync(stdoutFd);
|
|
90
|
-
closeSync(stderrFd);
|
|
91
|
-
const pid = child.pid;
|
|
92
|
-
if (!pid)
|
|
93
|
-
throw new Error("Failed to spawn listener process");
|
|
94
|
-
await writeFile7(pidPath(), String(pid));
|
|
95
|
-
return pid;
|
|
25
|
+
// ../../packages/shared/src/types/typed-resolution.ts
|
|
26
|
+
function typedResolutionKey(filePath, propertyName, line, column) {
|
|
27
|
+
return `${filePath}\0${propertyName}\0${line.toString()}\0${column.toString()}`;
|
|
96
28
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
process.kill(pid, "SIGTERM");
|
|
107
|
-
} catch {
|
|
29
|
+
var TYPED_RESOLUTION_SCHEMA_VERSION, ACCEPTED_TYPED_RESOLUTION_SCHEMA_VERSIONS;
|
|
30
|
+
var init_typed_resolution = __esm({
|
|
31
|
+
"../../packages/shared/src/types/typed-resolution.ts"() {
|
|
32
|
+
"use strict";
|
|
33
|
+
TYPED_RESOLUTION_SCHEMA_VERSION = 2;
|
|
34
|
+
ACCEPTED_TYPED_RESOLUTION_SCHEMA_VERSIONS = [1, 2];
|
|
108
35
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ../../packages/shared/src/types/billing.ts
|
|
39
|
+
var init_billing = __esm({
|
|
40
|
+
"../../packages/shared/src/types/billing.ts"() {
|
|
41
|
+
"use strict";
|
|
112
42
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// ../../packages/shared/src/constants/platforms.ts
|
|
46
|
+
var init_platforms = __esm({
|
|
47
|
+
"../../packages/shared/src/constants/platforms.ts"() {
|
|
48
|
+
"use strict";
|
|
118
49
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return isAlive(pid);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ../../packages/shared/src/constants/tiers.ts
|
|
53
|
+
function hasFeature(plan, feature) {
|
|
54
|
+
const features = TIER_FEATURES[plan] ?? TIER_FEATURES[SUBSCRIPTION_TIERS.FREE];
|
|
55
|
+
return features[feature] ?? false;
|
|
126
56
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
await unlink5(pidPath());
|
|
130
|
-
} catch {
|
|
131
|
-
}
|
|
57
|
+
function agentInstructionsVariantForPlan(plan) {
|
|
58
|
+
return hasFeature(plan, "context") ? "full" : "graph";
|
|
132
59
|
}
|
|
133
|
-
var
|
|
134
|
-
|
|
60
|
+
var SUBSCRIPTION_TIERS, TIER_LIMITS, TIER_FEATURES;
|
|
61
|
+
var init_tiers = __esm({
|
|
62
|
+
"../../packages/shared/src/constants/tiers.ts"() {
|
|
135
63
|
"use strict";
|
|
136
|
-
|
|
64
|
+
SUBSCRIPTION_TIERS = {
|
|
65
|
+
FREE: "free",
|
|
66
|
+
// `starter` is the legacy $15 tier — kept as a migration alias (with its OLD limits)
|
|
67
|
+
// until existing Starter subs are price-swapped to Free (pricing v3). Removed after M7.
|
|
68
|
+
STARTER: "starter",
|
|
69
|
+
PRO: "pro",
|
|
70
|
+
TEAM: "team"
|
|
71
|
+
};
|
|
72
|
+
TIER_LIMITS = {
|
|
73
|
+
[SUBSCRIPTION_TIERS.FREE]: {
|
|
74
|
+
maxRepos: 1,
|
|
75
|
+
maxSeats: 1,
|
|
76
|
+
minSeats: 1,
|
|
77
|
+
maxSyncsPerMonth: 4,
|
|
78
|
+
// manual-only, hard cap (no overage)
|
|
79
|
+
maxConcurrentSyncs: 1,
|
|
80
|
+
freeFullScans: 0,
|
|
81
|
+
// graph-only: no LLM full scan
|
|
82
|
+
bedrockEndpoint: "public"
|
|
83
|
+
},
|
|
84
|
+
// Legacy alias — retains the OLD Starter limits until existing Starter subs are
|
|
85
|
+
// migrated to Free, then removed (M7). Do NOT reference for new logic.
|
|
86
|
+
[SUBSCRIPTION_TIERS.STARTER]: {
|
|
87
|
+
maxRepos: 1,
|
|
88
|
+
maxSeats: 1,
|
|
89
|
+
minSeats: 1,
|
|
90
|
+
maxSyncsPerMonth: 10,
|
|
91
|
+
maxConcurrentSyncs: 1,
|
|
92
|
+
freeFullScans: 1,
|
|
93
|
+
bedrockEndpoint: "public"
|
|
94
|
+
},
|
|
95
|
+
[SUBSCRIPTION_TIERS.PRO]: {
|
|
96
|
+
maxRepos: 1,
|
|
97
|
+
maxSeats: 1,
|
|
98
|
+
minSeats: 1,
|
|
99
|
+
maxSyncsPerMonth: 20,
|
|
100
|
+
maxConcurrentSyncs: 2,
|
|
101
|
+
freeFullScans: 1,
|
|
102
|
+
bedrockEndpoint: "public"
|
|
103
|
+
},
|
|
104
|
+
[SUBSCRIPTION_TIERS.TEAM]: {
|
|
105
|
+
maxRepos: 1,
|
|
106
|
+
maxSeats: 999999,
|
|
107
|
+
minSeats: 5,
|
|
108
|
+
maxSyncsPerMonth: 30,
|
|
109
|
+
maxConcurrentSyncs: 10,
|
|
110
|
+
freeFullScans: 1,
|
|
111
|
+
bedrockEndpoint: "public"
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
TIER_FEATURES = {
|
|
115
|
+
[SUBSCRIPTION_TIERS.FREE]: {
|
|
116
|
+
knowledgeGraph: true,
|
|
117
|
+
mcp: true,
|
|
118
|
+
context: false,
|
|
119
|
+
// LLM-generated narrative context docs
|
|
120
|
+
docs: false,
|
|
121
|
+
// RepoWise Docs
|
|
122
|
+
autoSync: false,
|
|
123
|
+
// auto-sync on merge (Free is manual-only)
|
|
124
|
+
overage: false
|
|
125
|
+
// pay-as-you-go syncs (Free is hard-capped)
|
|
126
|
+
},
|
|
127
|
+
// Legacy alias — full features until existing Starter subs migrate to Free.
|
|
128
|
+
[SUBSCRIPTION_TIERS.STARTER]: {
|
|
129
|
+
knowledgeGraph: true,
|
|
130
|
+
mcp: true,
|
|
131
|
+
context: true,
|
|
132
|
+
docs: true,
|
|
133
|
+
autoSync: true,
|
|
134
|
+
overage: true
|
|
135
|
+
},
|
|
136
|
+
[SUBSCRIPTION_TIERS.PRO]: {
|
|
137
|
+
knowledgeGraph: true,
|
|
138
|
+
mcp: true,
|
|
139
|
+
context: true,
|
|
140
|
+
docs: true,
|
|
141
|
+
autoSync: true,
|
|
142
|
+
overage: true
|
|
143
|
+
},
|
|
144
|
+
[SUBSCRIPTION_TIERS.TEAM]: {
|
|
145
|
+
knowledgeGraph: true,
|
|
146
|
+
mcp: true,
|
|
147
|
+
context: true,
|
|
148
|
+
docs: true,
|
|
149
|
+
autoSync: true,
|
|
150
|
+
overage: true
|
|
151
|
+
}
|
|
152
|
+
};
|
|
137
153
|
}
|
|
138
154
|
});
|
|
139
155
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
install: () => install,
|
|
144
|
-
isInstalled: () => isInstalled,
|
|
145
|
-
isServiceRunning: () => isServiceRunning,
|
|
146
|
-
stopService: () => stopService,
|
|
147
|
-
uninstall: () => uninstall
|
|
148
|
-
});
|
|
149
|
-
import { execFile as execFile4 } from "child_process";
|
|
150
|
-
import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
|
|
151
|
-
import { homedir as homedir3 } from "os";
|
|
152
|
-
import { join as join13 } from "path";
|
|
153
|
-
function exec(cmd, args) {
|
|
154
|
-
return new Promise((resolve5, reject) => {
|
|
155
|
-
execFile4(cmd, args, (err, stdout) => {
|
|
156
|
-
if (err) {
|
|
157
|
-
reject(err);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
resolve5(String(stdout ?? ""));
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
function plistPath() {
|
|
165
|
-
return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
156
|
+
// ../../packages/shared/src/constants/roles.ts
|
|
157
|
+
function hasPermission(role, permission) {
|
|
158
|
+
return PERMISSIONS[role][permission];
|
|
166
159
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
160
|
+
function resolveRole(groups) {
|
|
161
|
+
for (const role of ROLE_PRIORITY) {
|
|
162
|
+
if (groups.includes(role)) return role;
|
|
163
|
+
}
|
|
164
|
+
return ROLES.MEMBER;
|
|
169
165
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
async function darwinUninstall() {
|
|
230
|
-
try {
|
|
231
|
-
await exec("launchctl", ["unload", plistPath()]);
|
|
232
|
-
} catch {
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
await unlink6(plistPath());
|
|
236
|
-
} catch {
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async function darwinIsInstalled() {
|
|
240
|
-
try {
|
|
241
|
-
const stdout = await exec("launchctl", ["list"]);
|
|
242
|
-
return stdout.includes(PLIST_LABEL);
|
|
243
|
-
} catch {
|
|
244
|
-
return false;
|
|
166
|
+
var ROLES, PERMISSIONS, ROLE_PRIORITY;
|
|
167
|
+
var init_roles = __esm({
|
|
168
|
+
"../../packages/shared/src/constants/roles.ts"() {
|
|
169
|
+
"use strict";
|
|
170
|
+
ROLES = {
|
|
171
|
+
OWNER: "owner",
|
|
172
|
+
ADMIN: "admin",
|
|
173
|
+
REPO_MANAGER: "repo_manager",
|
|
174
|
+
MEMBER: "member"
|
|
175
|
+
};
|
|
176
|
+
PERMISSIONS = {
|
|
177
|
+
[ROLES.OWNER]: {
|
|
178
|
+
connectRepos: true,
|
|
179
|
+
viewSync: true,
|
|
180
|
+
triggerRetry: true,
|
|
181
|
+
configWatcher: true,
|
|
182
|
+
inviteMembers: true,
|
|
183
|
+
manageBilling: true,
|
|
184
|
+
configAiTools: true,
|
|
185
|
+
manageTeam: true
|
|
186
|
+
},
|
|
187
|
+
[ROLES.ADMIN]: {
|
|
188
|
+
connectRepos: true,
|
|
189
|
+
viewSync: true,
|
|
190
|
+
triggerRetry: true,
|
|
191
|
+
configWatcher: true,
|
|
192
|
+
inviteMembers: true,
|
|
193
|
+
manageBilling: false,
|
|
194
|
+
configAiTools: true,
|
|
195
|
+
manageTeam: true
|
|
196
|
+
},
|
|
197
|
+
[ROLES.REPO_MANAGER]: {
|
|
198
|
+
connectRepos: true,
|
|
199
|
+
viewSync: true,
|
|
200
|
+
triggerRetry: true,
|
|
201
|
+
configWatcher: true,
|
|
202
|
+
inviteMembers: false,
|
|
203
|
+
manageBilling: false,
|
|
204
|
+
configAiTools: true,
|
|
205
|
+
manageTeam: false
|
|
206
|
+
},
|
|
207
|
+
[ROLES.MEMBER]: {
|
|
208
|
+
connectRepos: false,
|
|
209
|
+
viewSync: true,
|
|
210
|
+
triggerRetry: true,
|
|
211
|
+
configWatcher: false,
|
|
212
|
+
inviteMembers: false,
|
|
213
|
+
manageBilling: false,
|
|
214
|
+
configAiTools: true,
|
|
215
|
+
manageTeam: false
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
ROLE_PRIORITY = [
|
|
219
|
+
ROLES.OWNER,
|
|
220
|
+
ROLES.ADMIN,
|
|
221
|
+
ROLES.REPO_MANAGER,
|
|
222
|
+
ROLES.MEMBER
|
|
223
|
+
];
|
|
245
224
|
}
|
|
246
|
-
}
|
|
247
|
-
function unitPath() {
|
|
248
|
-
return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
249
|
-
}
|
|
250
|
-
function buildUnit() {
|
|
251
|
-
const cmd = resolveListenerCommand();
|
|
252
|
-
const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
|
|
253
|
-
const logs = logDir();
|
|
254
|
-
return `[Unit]
|
|
255
|
-
Description=RepoWise Listener
|
|
256
|
-
After=network-online.target
|
|
257
|
-
Wants=network-online.target
|
|
258
|
-
|
|
259
|
-
[Service]
|
|
260
|
-
Type=simple
|
|
261
|
-
ExecStart=${execStart}
|
|
262
|
-
Restart=always
|
|
263
|
-
RestartSec=10
|
|
264
|
-
StandardOutput=append:${join13(logs, "listener-stdout.log")}
|
|
265
|
-
StandardError=append:${join13(logs, "listener-stderr.log")}
|
|
225
|
+
});
|
|
266
226
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
try {
|
|
288
|
-
await unlink6(unitPath());
|
|
289
|
-
} catch {
|
|
290
|
-
}
|
|
291
|
-
try {
|
|
292
|
-
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
293
|
-
} catch {
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
async function linuxIsInstalled() {
|
|
297
|
-
try {
|
|
298
|
-
const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
|
|
299
|
-
return stdout.trim() === "enabled";
|
|
300
|
-
} catch {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
async function win32Install() {
|
|
305
|
-
await mkdir8(logDir(), { recursive: true });
|
|
306
|
-
const cmd = resolveListenerCommand();
|
|
307
|
-
const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
|
|
308
|
-
await exec("schtasks", [
|
|
309
|
-
"/create",
|
|
310
|
-
"/tn",
|
|
311
|
-
TASK_NAME,
|
|
312
|
-
"/tr",
|
|
313
|
-
taskCmd,
|
|
314
|
-
"/sc",
|
|
315
|
-
"onlogon",
|
|
316
|
-
"/ru",
|
|
317
|
-
process.env.USERNAME ?? "",
|
|
318
|
-
"/f"
|
|
319
|
-
]);
|
|
320
|
-
await exec("schtasks", ["/run", "/tn", TASK_NAME]);
|
|
321
|
-
}
|
|
322
|
-
async function win32Uninstall() {
|
|
323
|
-
try {
|
|
324
|
-
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
325
|
-
} catch {
|
|
326
|
-
}
|
|
327
|
-
try {
|
|
328
|
-
await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
|
|
329
|
-
} catch {
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
async function win32IsInstalled() {
|
|
333
|
-
try {
|
|
334
|
-
await exec("schtasks", ["/query", "/tn", TASK_NAME]);
|
|
335
|
-
return true;
|
|
336
|
-
} catch {
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async function darwinStop() {
|
|
341
|
-
try {
|
|
342
|
-
await exec("launchctl", ["unload", plistPath()]);
|
|
343
|
-
} catch {
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
async function linuxStop() {
|
|
347
|
-
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
348
|
-
}
|
|
349
|
-
async function win32Stop() {
|
|
350
|
-
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
227
|
+
// ../../packages/shared/src/constants/lsp-versions.ts
|
|
228
|
+
function getCurrentPlatformKey(platform = process.platform, arch = process.arch) {
|
|
229
|
+
const platformMap = {
|
|
230
|
+
darwin: "darwin",
|
|
231
|
+
linux: "linux",
|
|
232
|
+
win32: "win32"
|
|
233
|
+
};
|
|
234
|
+
const archMap = {
|
|
235
|
+
x64: "x64",
|
|
236
|
+
arm64: "arm64"
|
|
237
|
+
};
|
|
238
|
+
const p = platformMap[platform];
|
|
239
|
+
const a = archMap[arch];
|
|
240
|
+
if (!p || !a) return null;
|
|
241
|
+
return `${p}-${a}`;
|
|
351
242
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
|
|
356
|
-
return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
|
|
357
|
-
} catch {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
async function linuxIsServiceRunning() {
|
|
362
|
-
try {
|
|
363
|
-
const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
|
|
364
|
-
return stdout.trim() === "active";
|
|
365
|
-
} catch {
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
async function win32IsServiceRunning() {
|
|
370
|
-
try {
|
|
371
|
-
const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
|
|
372
|
-
return stdout.includes("Running");
|
|
373
|
-
} catch {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
async function stopService() {
|
|
378
|
-
switch (process.platform) {
|
|
379
|
-
case "darwin":
|
|
380
|
-
await darwinStop();
|
|
381
|
-
break;
|
|
382
|
-
case "linux":
|
|
383
|
-
await linuxStop();
|
|
384
|
-
break;
|
|
385
|
-
case "win32":
|
|
386
|
-
await win32Stop();
|
|
387
|
-
break;
|
|
388
|
-
default:
|
|
389
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
async function install() {
|
|
393
|
-
switch (process.platform) {
|
|
394
|
-
case "darwin":
|
|
395
|
-
await darwinInstall();
|
|
396
|
-
break;
|
|
397
|
-
case "linux":
|
|
398
|
-
await linuxInstall();
|
|
399
|
-
break;
|
|
400
|
-
case "win32":
|
|
401
|
-
await win32Install();
|
|
402
|
-
break;
|
|
403
|
-
default:
|
|
404
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
async function uninstall() {
|
|
408
|
-
switch (process.platform) {
|
|
409
|
-
case "darwin":
|
|
410
|
-
await darwinUninstall();
|
|
411
|
-
break;
|
|
412
|
-
case "linux":
|
|
413
|
-
await linuxUninstall();
|
|
414
|
-
break;
|
|
415
|
-
case "win32":
|
|
416
|
-
await win32Uninstall();
|
|
417
|
-
break;
|
|
418
|
-
default:
|
|
419
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
async function isInstalled() {
|
|
423
|
-
switch (process.platform) {
|
|
424
|
-
case "darwin":
|
|
425
|
-
return darwinIsInstalled();
|
|
426
|
-
case "linux":
|
|
427
|
-
return linuxIsInstalled();
|
|
428
|
-
case "win32":
|
|
429
|
-
return win32IsInstalled();
|
|
430
|
-
default:
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
async function isServiceRunning() {
|
|
435
|
-
switch (process.platform) {
|
|
436
|
-
case "darwin":
|
|
437
|
-
return darwinIsServiceRunning();
|
|
438
|
-
case "linux":
|
|
439
|
-
return linuxIsServiceRunning();
|
|
440
|
-
case "win32":
|
|
441
|
-
return win32IsServiceRunning();
|
|
442
|
-
default:
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
|
|
447
|
-
var init_service_installer = __esm({
|
|
448
|
-
"../listener/dist/service-installer.js"() {
|
|
449
|
-
"use strict";
|
|
450
|
-
init_config_dir();
|
|
451
|
-
init_process_manager();
|
|
452
|
-
IS_STAGING = true ? true : false;
|
|
453
|
-
PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
|
|
454
|
-
SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
|
|
455
|
-
TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// ../listener/dist/lsp/registry.js
|
|
460
|
-
function resolvePhpLspOverride(env = process.env, configOverrides) {
|
|
461
|
-
const envChoice = env["PHP_LSP"];
|
|
462
|
-
const configChoice = configOverrides?.["php"];
|
|
463
|
-
const choice = envChoice ?? configChoice;
|
|
464
|
-
if (choice !== "intelephense")
|
|
465
|
-
return null;
|
|
466
|
-
return {
|
|
467
|
-
id: "intelephense",
|
|
468
|
-
displayName: "intelephense (user-provided)",
|
|
469
|
-
command: "intelephense",
|
|
470
|
-
args: ["--stdio"],
|
|
471
|
-
extensions: [".php"],
|
|
472
|
-
lspLanguageId: "php",
|
|
473
|
-
installHint: "Intelephense override is enabled \u2014 install it yourself: `npm i -g intelephense` (commercial licence required for premium features)."
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function getEffectiveConfig(lang, env = process.env, configOverrides) {
|
|
477
|
-
if (lang === "php") {
|
|
478
|
-
const override = resolvePhpLspOverride(env, configOverrides);
|
|
479
|
-
if (override)
|
|
480
|
-
return override;
|
|
481
|
-
}
|
|
482
|
-
return LSP_REGISTRY[lang]?.[0];
|
|
483
|
-
}
|
|
484
|
-
function getEffectiveConfigList(lang, env = process.env, configOverrides) {
|
|
485
|
-
if (lang === "php") {
|
|
486
|
-
const override = resolvePhpLspOverride(env, configOverrides);
|
|
487
|
-
if (override)
|
|
488
|
-
return [override];
|
|
489
|
-
}
|
|
490
|
-
return LSP_REGISTRY[lang] ?? [];
|
|
491
|
-
}
|
|
492
|
-
function detectLanguage(path) {
|
|
493
|
-
const dot = path.lastIndexOf(".");
|
|
494
|
-
if (dot < 0)
|
|
495
|
-
return null;
|
|
496
|
-
const ext = path.slice(dot).toLowerCase();
|
|
497
|
-
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
498
|
-
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
499
|
-
return lang;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
|
-
async function probeServers(isAvailable, env = process.env, configOverrides) {
|
|
505
|
-
const out = [];
|
|
506
|
-
for (const lang of Object.keys(LSP_REGISTRY)) {
|
|
507
|
-
const configs = getEffectiveConfigList(lang, env, configOverrides);
|
|
508
|
-
const checkedCommands = configs.map((c) => c.command);
|
|
509
|
-
let found = null;
|
|
510
|
-
for (const config2 of configs) {
|
|
511
|
-
if (await isAvailable(config2.command)) {
|
|
512
|
-
found = config2;
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
out.push({ language: lang, config: found, checkedCommands });
|
|
517
|
-
}
|
|
518
|
-
return out;
|
|
519
|
-
}
|
|
520
|
-
function getBuildSpec(language) {
|
|
521
|
-
return BUILD_SPECS[language] ?? null;
|
|
522
|
-
}
|
|
523
|
-
var LSP_REGISTRY, BUILD_SPECS;
|
|
524
|
-
var init_registry = __esm({
|
|
525
|
-
"../listener/dist/lsp/registry.js"() {
|
|
243
|
+
var NATIVE_LSP_VERSIONS, COURSIER_LSPS, TOOLCHAIN_LSPS;
|
|
244
|
+
var init_lsp_versions = __esm({
|
|
245
|
+
"../../packages/shared/src/constants/lsp-versions.ts"() {
|
|
526
246
|
"use strict";
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
{
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
247
|
+
NATIVE_LSP_VERSIONS = {
|
|
248
|
+
"rust-analyzer": {
|
|
249
|
+
source: { kind: "github-release", repo: "rust-lang/rust-analyzer" },
|
|
250
|
+
version: "2026-05-11",
|
|
251
|
+
license: "MIT OR Apache-2.0",
|
|
252
|
+
assets: {
|
|
253
|
+
"darwin-arm64": {
|
|
254
|
+
filename: "rust-analyzer-aarch64-apple-darwin.gz",
|
|
255
|
+
sha256: "eeb6367cd2c6cc9217ae06cea2e0c7e9cf20cdac5e934a1b272b13c76ece5aee",
|
|
256
|
+
binaryPath: "rust-analyzer-aarch64-apple-darwin"
|
|
257
|
+
},
|
|
258
|
+
"darwin-x64": {
|
|
259
|
+
filename: "rust-analyzer-x86_64-apple-darwin.gz",
|
|
260
|
+
sha256: "718398822ec2f282bca36f6f109bbbb33746437e1a63946fe7fd3d84d502a67d",
|
|
261
|
+
binaryPath: "rust-analyzer-x86_64-apple-darwin"
|
|
262
|
+
},
|
|
263
|
+
"linux-x64": {
|
|
264
|
+
filename: "rust-analyzer-x86_64-unknown-linux-gnu.gz",
|
|
265
|
+
sha256: "ede62b8536a37c90d804850d0bed3623e3d6af90a1195e4930a1627e5d8ae33f",
|
|
266
|
+
binaryPath: "rust-analyzer-x86_64-unknown-linux-gnu"
|
|
267
|
+
},
|
|
268
|
+
"linux-arm64": {
|
|
269
|
+
filename: "rust-analyzer-aarch64-unknown-linux-gnu.gz",
|
|
270
|
+
sha256: "dbe8c4c0bed79f36133c9d0a0eb629709d91343e065a179cd4a5f8137817de1c",
|
|
271
|
+
binaryPath: "rust-analyzer-aarch64-unknown-linux-gnu"
|
|
272
|
+
},
|
|
273
|
+
"win32-x64": {
|
|
274
|
+
filename: "rust-analyzer-x86_64-pc-windows-msvc.zip",
|
|
275
|
+
sha256: "1600365f2d8b0e42a811eafb5e129b75b991ddf9d9955d19d8fc2f4f2b9c19ef",
|
|
276
|
+
binaryPath: "rust-analyzer.exe"
|
|
277
|
+
},
|
|
278
|
+
"win32-arm64": {
|
|
279
|
+
filename: "rust-analyzer-aarch64-pc-windows-msvc.zip",
|
|
280
|
+
sha256: "3cd63d31e9abfea89488eebfd2c29d41550af6cb6f6b32f52e455fb783215fd0",
|
|
281
|
+
binaryPath: "rust-analyzer.exe"
|
|
282
|
+
}
|
|
550
283
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
{
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
useLibraryCodeForTypes: true,
|
|
579
|
-
autoSearchPaths: true,
|
|
580
|
-
diagnosticMode: "workspace"
|
|
581
|
-
// Pyright honours either `disableLanguageServices` or
|
|
582
|
-
// simply not subscribing to diagnostics; the conservative
|
|
583
|
-
// path is to send the request handshake without
|
|
584
|
-
// publishDiagnostics in our capabilities (already the
|
|
585
|
-
// case in workspace-session.ts) and avoid extra knobs
|
|
586
|
-
// here that would diverge from Pyright defaults.
|
|
587
|
-
}
|
|
588
|
-
}
|
|
284
|
+
},
|
|
285
|
+
clangd: {
|
|
286
|
+
source: { kind: "github-release", repo: "clangd/clangd" },
|
|
287
|
+
version: "18.1.3",
|
|
288
|
+
license: "Apache-2.0 WITH LLVM-exception",
|
|
289
|
+
assets: {
|
|
290
|
+
// clangd ships a single universal macOS binary covering both
|
|
291
|
+
// architectures — same filename for darwin-arm64 and darwin-x64.
|
|
292
|
+
"darwin-arm64": {
|
|
293
|
+
filename: "clangd-mac-18.1.3.zip",
|
|
294
|
+
sha256: "442c18d98671d8b5d811b25002ad1f2fd0b9efb07e7905a41502f1f1311dc566",
|
|
295
|
+
binaryPath: "clangd_18.1.3/bin/clangd"
|
|
296
|
+
},
|
|
297
|
+
"darwin-x64": {
|
|
298
|
+
filename: "clangd-mac-18.1.3.zip",
|
|
299
|
+
sha256: "442c18d98671d8b5d811b25002ad1f2fd0b9efb07e7905a41502f1f1311dc566",
|
|
300
|
+
binaryPath: "clangd_18.1.3/bin/clangd"
|
|
301
|
+
},
|
|
302
|
+
"linux-x64": {
|
|
303
|
+
filename: "clangd-linux-18.1.3.zip",
|
|
304
|
+
sha256: "775f6ac798d9c599258e3f4fa14d0af908b1801d38250e72a18fb907b28a1be6",
|
|
305
|
+
binaryPath: "clangd_18.1.3/bin/clangd"
|
|
306
|
+
},
|
|
307
|
+
"win32-x64": {
|
|
308
|
+
filename: "clangd-windows-18.1.3.zip",
|
|
309
|
+
sha256: "3d287d39bab29406f002ff80b939668e59e25ef72346d8f38826305016e45896",
|
|
310
|
+
binaryPath: "clangd_18.1.3/bin/clangd.exe"
|
|
589
311
|
}
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
id: "pylsp",
|
|
593
|
-
displayName: "python-lsp-server",
|
|
594
|
-
command: "pylsp",
|
|
595
|
-
args: [],
|
|
596
|
-
extensions: [".py", ".pyi"],
|
|
597
|
-
lspLanguageId: "python",
|
|
598
|
-
installHint: 'pipx install "python-lsp-server[all]"'
|
|
599
|
-
}
|
|
600
|
-
],
|
|
601
|
-
go: [
|
|
602
|
-
{
|
|
603
|
-
id: "gopls",
|
|
604
|
-
displayName: "gopls",
|
|
605
|
-
command: "gopls",
|
|
606
|
-
// Modern gopls uses stdio by default; `serve` is the legacy
|
|
607
|
-
// subcommand and no longer needed (review finding).
|
|
608
|
-
args: [],
|
|
609
|
-
extensions: [".go"],
|
|
610
|
-
lspLanguageId: "go",
|
|
611
|
-
installHint: "go install golang.org/x/tools/gopls@latest",
|
|
612
|
-
toolchainInstallKey: "gopls"
|
|
613
312
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
313
|
+
},
|
|
314
|
+
// Coursier — single-file JVM launcher that we install once and then
|
|
315
|
+
// use to install any JVM-based LSP (metals, future kotlin LSPs, etc.).
|
|
316
|
+
// Tier 2 of the install plan: the binary itself is just another
|
|
317
|
+
// GitHub-release Tier 1 entry; the magic is what we DO with it after
|
|
318
|
+
// install (see coursier-installer.ts).
|
|
319
|
+
coursier: {
|
|
320
|
+
source: { kind: "github-release", repo: "coursier/coursier" },
|
|
321
|
+
version: "v2.1.24",
|
|
322
|
+
license: "Apache-2.0",
|
|
323
|
+
assets: {
|
|
324
|
+
"darwin-arm64": {
|
|
325
|
+
filename: "cs-aarch64-apple-darwin.gz",
|
|
326
|
+
sha256: "53a5728c2016118c8296fa7d5678ddfe122e22dc6c36deb554d771b3b9295b4f",
|
|
327
|
+
binaryPath: "cs"
|
|
328
|
+
},
|
|
329
|
+
"darwin-x64": {
|
|
330
|
+
filename: "cs-x86_64-apple-darwin.gz",
|
|
331
|
+
sha256: "33913cd6b61658035d9e6fe971e919cb0ef1f659aa7bff7deeded963a2d36385",
|
|
332
|
+
binaryPath: "cs"
|
|
333
|
+
},
|
|
334
|
+
"linux-x64": {
|
|
335
|
+
filename: "cs-x86_64-pc-linux.gz",
|
|
336
|
+
sha256: "d2c0572a17fb6146ea65349b59dd216b38beff60ae22bce6e549867c6ed2eda6",
|
|
337
|
+
binaryPath: "cs"
|
|
338
|
+
},
|
|
339
|
+
// coursier v2.1.24 ships no aarch64-linux build — Scala dev on
|
|
340
|
+
// ARM Linux falls back to the toolchain-installed Metals path.
|
|
341
|
+
"win32-x64": {
|
|
342
|
+
filename: "cs-x86_64-pc-win32.zip",
|
|
343
|
+
sha256: "c16b4f95b59fbe035cdda4353cfca78befcbbef5a20a3b151c679717c1504c9f",
|
|
344
|
+
binaryPath: "cs.exe"
|
|
640
345
|
}
|
|
641
346
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
347
|
+
},
|
|
348
|
+
// Eclipse JDT Language Server — single cross-platform tarball
|
|
349
|
+
// hosted at download.eclipse.org. Ships as an Equinox launcher jar
|
|
350
|
+
// that must be invoked via `java -jar`, so the installer generates
|
|
351
|
+
// a `bin/jdtls` shim that selects the right config_{platform} dir.
|
|
352
|
+
// Requires JDK 17+ (jdtls 1.51 minimum).
|
|
353
|
+
jdtls: {
|
|
354
|
+
source: { kind: "direct-url" },
|
|
355
|
+
version: "1.51.0",
|
|
356
|
+
license: "EPL-2.0",
|
|
357
|
+
requiresJdk: 17,
|
|
358
|
+
assets: {
|
|
359
|
+
"darwin-arm64": {
|
|
360
|
+
url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
361
|
+
filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
362
|
+
sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
|
|
363
|
+
binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
364
|
+
launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
365
|
+
wrapperBinaryName: "jdtls"
|
|
366
|
+
},
|
|
367
|
+
"darwin-x64": {
|
|
368
|
+
url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
369
|
+
filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
370
|
+
sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
|
|
371
|
+
binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
372
|
+
launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
373
|
+
wrapperBinaryName: "jdtls"
|
|
374
|
+
},
|
|
375
|
+
"linux-x64": {
|
|
376
|
+
url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
377
|
+
filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
378
|
+
sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
|
|
379
|
+
binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
380
|
+
launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
381
|
+
wrapperBinaryName: "jdtls"
|
|
382
|
+
},
|
|
383
|
+
"linux-arm64": {
|
|
384
|
+
url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
385
|
+
filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
386
|
+
sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
|
|
387
|
+
binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
388
|
+
launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
389
|
+
wrapperBinaryName: "jdtls"
|
|
390
|
+
},
|
|
391
|
+
"win32-x64": {
|
|
392
|
+
url: "https://download.eclipse.org/jdtls/milestones/1.51.0/jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
393
|
+
filename: "jdt-language-server-1.51.0-202510022025.tar.gz",
|
|
394
|
+
sha256: "8a59372117881bf5bdc0220f2254472846b88137c058f344b00a7d41427745a1",
|
|
395
|
+
binaryPath: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
396
|
+
launcherJarGlob: "plugins/org.eclipse.equinox.launcher_*.jar",
|
|
397
|
+
wrapperBinaryName: "jdtls"
|
|
691
398
|
}
|
|
692
399
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
// runtime (see apps/cli/src/commands/lsp.ts step 7). The escape
|
|
730
|
-
// hatch requires the user to provision an `intelephense` binary
|
|
731
|
-
// on PATH themselves.
|
|
732
|
-
//
|
|
733
|
-
// Confidence floor on PHP is 0.7 (see lsp-upgrade-strategy.ts).
|
|
734
|
-
// Phpactor's static analysis is solid for PSR-4 resolvable calls
|
|
735
|
-
// but stumbles on Laravel-style facades / magic accessors — those
|
|
736
|
-
// surface as null definitions, recorded as `kind: 'definition'`
|
|
737
|
-
// with `targets.length === 0` (no upgrade, telemetry preserved).
|
|
738
|
-
{
|
|
739
|
-
id: "phpactor",
|
|
740
|
-
displayName: "phpactor",
|
|
741
|
-
command: "phpactor",
|
|
742
|
-
args: ["language-server"],
|
|
743
|
-
extensions: [".php"],
|
|
744
|
-
lspLanguageId: "php",
|
|
745
|
-
installHint: "composer global require phpactor/phpactor (see https://phpactor.readthedocs.io/en/master/usage/standalone.html)",
|
|
746
|
-
toolchainInstallKey: "phpactor"
|
|
747
|
-
}
|
|
748
|
-
],
|
|
749
|
-
ruby: [
|
|
750
|
-
{
|
|
751
|
-
id: "ruby-lsp",
|
|
752
|
-
displayName: "ruby-lsp",
|
|
753
|
-
command: "ruby-lsp",
|
|
754
|
-
args: [],
|
|
755
|
-
extensions: [".rb"],
|
|
756
|
-
lspLanguageId: "ruby",
|
|
757
|
-
installHint: "gem install ruby-lsp",
|
|
758
|
-
toolchainInstallKey: "ruby-lsp"
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
id: "solargraph",
|
|
762
|
-
displayName: "solargraph",
|
|
763
|
-
command: "solargraph",
|
|
764
|
-
args: ["stdio"],
|
|
765
|
-
extensions: [".rb"],
|
|
766
|
-
lspLanguageId: "ruby",
|
|
767
|
-
installHint: "gem install solargraph"
|
|
768
|
-
}
|
|
769
|
-
],
|
|
770
|
-
kotlin: [
|
|
771
|
-
// Phase 7L++: fwcd Kotlin LSP. JVM cold-start is ~30s on the first
|
|
772
|
-
// Gradle/Maven import — the language server materialises the
|
|
773
|
-
// build's classpath before symbol lookups return. On mixed
|
|
774
|
-
// Kotlin/Java repos the WorkspaceManager spawns BOTH `jdtls` and
|
|
775
|
-
// `kotlin-language-server` — each owns its own file-type filter
|
|
776
|
-
// and they coexist without contention. Per-language LRU eviction
|
|
777
|
-
// (5-min idle, RSS soft-cap) prevents the two JVMs from pinning
|
|
778
|
-
// memory on long-lived listeners.
|
|
779
|
-
//
|
|
780
|
-
// Future: when JetBrains' Kotlin LSP exits preview, drop a
|
|
781
|
-
// higher-priority entry above this one with the JB binary; we
|
|
782
|
-
// keep fwcd as the fallback because JB's LSP requires a JetBrains
|
|
783
|
-
// account.
|
|
784
|
-
{
|
|
785
|
-
id: "kotlin-language-server",
|
|
786
|
-
displayName: "kotlin-language-server (fwcd)",
|
|
787
|
-
command: "kotlin-language-server",
|
|
788
|
-
args: [],
|
|
789
|
-
extensions: [".kt", ".kts"],
|
|
790
|
-
lspLanguageId: "kotlin",
|
|
791
|
-
installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)",
|
|
792
|
-
nativeInstallKey: "kotlin-language-server"
|
|
793
|
-
}
|
|
794
|
-
],
|
|
795
|
-
swift: [
|
|
796
|
-
{
|
|
797
|
-
id: "sourcekit-lsp",
|
|
798
|
-
displayName: "sourcekit-lsp",
|
|
799
|
-
command: "sourcekit-lsp",
|
|
800
|
-
args: [],
|
|
801
|
-
extensions: [".swift"],
|
|
802
|
-
lspLanguageId: "swift",
|
|
803
|
-
installHint: "Bundled with Xcode Command Line Tools on macOS",
|
|
804
|
-
toolchainInstallKey: "sourcekit-lsp",
|
|
805
|
-
requiresBuild: true
|
|
806
|
-
}
|
|
807
|
-
],
|
|
808
|
-
dart: [
|
|
809
|
-
// Phase 7L++: the official Dart Analysis Server ships inside the
|
|
810
|
-
// Dart SDK as `dart language-server --protocol=lsp`. There's no
|
|
811
|
-
// separate binary to install — installing Dart (or Flutter, which
|
|
812
|
-
// bundles Dart) is sufficient. Confidence floor is 0.9: the Dart
|
|
813
|
-
// analyzer is authoritative, derives types from sound null safety,
|
|
814
|
-
// and is the same server that powers the official IDE plugins.
|
|
815
|
-
// No `initializationOptions` — Dart's analyzer derives everything
|
|
816
|
-
// from `pubspec.yaml` / `analysis_options.yaml` at the repo root.
|
|
817
|
-
{
|
|
818
|
-
id: "dart-language-server",
|
|
819
|
-
displayName: "dart language-server",
|
|
820
|
-
command: "dart",
|
|
821
|
-
args: ["language-server", "--protocol=lsp"],
|
|
822
|
-
extensions: [".dart"],
|
|
823
|
-
lspLanguageId: "dart",
|
|
824
|
-
installHint: "Install Dart SDK from https://dart.dev/get-dart (or Flutter from https://flutter.dev \u2014 bundles Dart).",
|
|
825
|
-
toolchainInstallKey: "dart-language-server"
|
|
826
|
-
}
|
|
827
|
-
],
|
|
828
|
-
// Scala via Metals (Phase 7L+).
|
|
829
|
-
//
|
|
830
|
-
// Metals is the de-facto Scala language server. It coordinates with
|
|
831
|
-
// a build server (Bloop by default; SBT/Mill/Gradle alternatives are
|
|
832
|
-
// discovered from the project's build files) to obtain the project's
|
|
833
|
-
// classpath, semantic database, and compiler diagnostics. The Bloop
|
|
834
|
-
// daemon is a separate JVM process that Metals spawns on its first
|
|
835
|
-
// `initialize`, then keeps warm; the listener's WorkspaceManager
|
|
836
|
-
// pairs Bloop lifecycle to Metals' (see bloop-lifecycle.ts) so they
|
|
837
|
-
// are spawned together on lazy `getOrOpen` and torn down together
|
|
838
|
-
// on idle eviction.
|
|
839
|
-
//
|
|
840
|
-
// Install hint: `coursier install metals` — Coursier (`cs`) is the
|
|
841
|
-
// canonical Scala launcher and is typically already on a Scala
|
|
842
|
-
// developer's machine. The Coursier-managed `metals` shim resolves
|
|
843
|
-
// Bloop on first use, so no separate Bloop install is required.
|
|
844
|
-
// Macro-expanded code can mis-locate definitions (Scala 3 inline /
|
|
845
|
-
// quote macros, scala-compat shims) — the per-language strategy
|
|
846
|
-
// pins confidence at 0.85 to reflect that limitation.
|
|
847
|
-
scala: [
|
|
848
|
-
{
|
|
849
|
-
id: "metals",
|
|
850
|
-
displayName: "Metals (Scala)",
|
|
851
|
-
command: "metals",
|
|
852
|
-
args: [],
|
|
853
|
-
extensions: [".scala", ".sc", ".sbt"],
|
|
854
|
-
lspLanguageId: "scala",
|
|
855
|
-
installHint: "coursier install metals (see https://scalameta.org/metals \u2014 Coursier-managed binary coordinates with Bloop automatically)",
|
|
856
|
-
coursierInstallKey: "metals"
|
|
857
|
-
}
|
|
858
|
-
],
|
|
859
|
-
c: [
|
|
860
|
-
{
|
|
861
|
-
id: "clangd-c",
|
|
862
|
-
displayName: "clangd",
|
|
863
|
-
command: "clangd",
|
|
864
|
-
// `--compile-commands-dir=.repowise` lets clangd pick up the
|
|
865
|
-
// stub compile_commands.json the listener writes for repos
|
|
866
|
-
// that don't ship one (header-only libs, single-file repos).
|
|
867
|
-
// clangd still finds repo-root or build/ compile_commands.json
|
|
868
|
-
// first when present — this is a fallback location, not an
|
|
869
|
-
// override (task #1).
|
|
870
|
-
args: ["--background-index", "--compile-commands-dir=.repowise"],
|
|
871
|
-
extensions: [".c", ".h"],
|
|
872
|
-
lspLanguageId: "c",
|
|
873
|
-
installHint: "brew install llvm (includes clangd)",
|
|
874
|
-
nativeInstallKey: "clangd"
|
|
875
|
-
}
|
|
876
|
-
],
|
|
877
|
-
cpp: [
|
|
878
|
-
{
|
|
879
|
-
id: "clangd-cpp",
|
|
880
|
-
displayName: "clangd",
|
|
881
|
-
command: "clangd",
|
|
882
|
-
// `--compile-commands-dir=.repowise` lets clangd pick up the
|
|
883
|
-
// stub compile_commands.json the listener writes for repos
|
|
884
|
-
// that don't ship one (header-only libs, single-file repos).
|
|
885
|
-
// clangd still finds repo-root or build/ compile_commands.json
|
|
886
|
-
// first when present — this is a fallback location, not an
|
|
887
|
-
// override (task #1).
|
|
888
|
-
args: ["--background-index", "--compile-commands-dir=.repowise"],
|
|
889
|
-
extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"],
|
|
890
|
-
lspLanguageId: "cpp",
|
|
891
|
-
installHint: "brew install llvm (includes clangd)",
|
|
892
|
-
nativeInstallKey: "clangd"
|
|
893
|
-
}
|
|
894
|
-
],
|
|
895
|
-
vue: [
|
|
896
|
-
{
|
|
897
|
-
id: "vue-language-server",
|
|
898
|
-
displayName: "Vue Language Server (volar)",
|
|
899
|
-
command: "vue-language-server",
|
|
900
|
-
args: ["--stdio"],
|
|
901
|
-
extensions: [".vue"],
|
|
902
|
-
lspLanguageId: "vue",
|
|
903
|
-
installHint: "npm i -g @vue/language-server",
|
|
904
|
-
npmPackage: "@vue/language-server"
|
|
905
|
-
}
|
|
906
|
-
],
|
|
907
|
-
svelte: [
|
|
908
|
-
{
|
|
909
|
-
id: "svelte-language-server",
|
|
910
|
-
displayName: "svelte-language-server",
|
|
911
|
-
command: "svelteserver",
|
|
912
|
-
args: ["--stdio"],
|
|
913
|
-
extensions: [".svelte"],
|
|
914
|
-
lspLanguageId: "svelte",
|
|
915
|
-
installHint: "npm i -g svelte-language-server",
|
|
916
|
-
npmPackage: "svelte-language-server"
|
|
400
|
+
},
|
|
401
|
+
// fwcd/kotlin-language-server — single cross-platform zip on GitHub
|
|
402
|
+
// releases. Ships a `bin/kotlin-language-server` shell script + a
|
|
403
|
+
// matching `.bat` for Windows — both invoke `java` internally, so
|
|
404
|
+
// we just chmod+x and symlink (no shim needed). Requires JDK 11+.
|
|
405
|
+
"kotlin-language-server": {
|
|
406
|
+
source: { kind: "github-release", repo: "fwcd/kotlin-language-server" },
|
|
407
|
+
version: "1.3.13",
|
|
408
|
+
license: "MIT",
|
|
409
|
+
requiresJdk: 11,
|
|
410
|
+
assets: {
|
|
411
|
+
"darwin-arm64": {
|
|
412
|
+
filename: "server.zip",
|
|
413
|
+
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
414
|
+
binaryPath: "server/bin/kotlin-language-server"
|
|
415
|
+
},
|
|
416
|
+
"darwin-x64": {
|
|
417
|
+
filename: "server.zip",
|
|
418
|
+
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
419
|
+
binaryPath: "server/bin/kotlin-language-server"
|
|
420
|
+
},
|
|
421
|
+
"linux-x64": {
|
|
422
|
+
filename: "server.zip",
|
|
423
|
+
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
424
|
+
binaryPath: "server/bin/kotlin-language-server"
|
|
425
|
+
},
|
|
426
|
+
"linux-arm64": {
|
|
427
|
+
filename: "server.zip",
|
|
428
|
+
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
429
|
+
binaryPath: "server/bin/kotlin-language-server"
|
|
430
|
+
},
|
|
431
|
+
"win32-x64": {
|
|
432
|
+
filename: "server.zip",
|
|
433
|
+
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
434
|
+
binaryPath: "server/bin/kotlin-language-server.bat"
|
|
435
|
+
}
|
|
917
436
|
}
|
|
918
|
-
|
|
437
|
+
}
|
|
919
438
|
};
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
platform: "darwin",
|
|
926
|
-
command: "swift",
|
|
927
|
-
args: ["build"],
|
|
928
|
-
indexStoreProbe: ".build/index/store"
|
|
439
|
+
COURSIER_LSPS = {
|
|
440
|
+
metals: {
|
|
441
|
+
appName: "metals",
|
|
442
|
+
displayName: "Metals (Scala)",
|
|
443
|
+
license: "Apache-2.0"
|
|
929
444
|
}
|
|
930
445
|
};
|
|
446
|
+
TOOLCHAIN_LSPS = {
|
|
447
|
+
gopls: {
|
|
448
|
+
toolchain: "go",
|
|
449
|
+
installArgs: ["install", "golang.org/x/tools/gopls@latest"],
|
|
450
|
+
binaryName: "gopls",
|
|
451
|
+
toolchainBinDir: "go/bin",
|
|
452
|
+
displayName: "gopls",
|
|
453
|
+
license: "BSD-3-Clause"
|
|
454
|
+
},
|
|
455
|
+
"ruby-lsp": {
|
|
456
|
+
toolchain: "gem",
|
|
457
|
+
installArgs: ["install", "--user-install", "ruby-lsp"],
|
|
458
|
+
binaryName: "ruby-lsp",
|
|
459
|
+
// gem userdir is version-dependent — resolved at install time.
|
|
460
|
+
displayName: "ruby-lsp",
|
|
461
|
+
license: "MIT"
|
|
462
|
+
},
|
|
463
|
+
"csharp-ls": {
|
|
464
|
+
toolchain: "dotnet",
|
|
465
|
+
installArgs: ["tool", "install", "-g", "csharp-ls"],
|
|
466
|
+
binaryName: "csharp-ls",
|
|
467
|
+
toolchainBinDir: ".dotnet/tools",
|
|
468
|
+
displayName: "csharp-ls",
|
|
469
|
+
license: "MIT"
|
|
470
|
+
},
|
|
471
|
+
phpactor: {
|
|
472
|
+
toolchain: "composer",
|
|
473
|
+
installArgs: ["global", "require", "phpactor/phpactor"],
|
|
474
|
+
binaryName: "phpactor",
|
|
475
|
+
toolchainBinDir: ".composer/vendor/bin",
|
|
476
|
+
displayName: "phpactor",
|
|
477
|
+
license: "MIT"
|
|
478
|
+
},
|
|
479
|
+
"dart-language-server": {
|
|
480
|
+
bundled: true,
|
|
481
|
+
binaryName: "dart",
|
|
482
|
+
displayName: "dart language-server (Dart SDK)",
|
|
483
|
+
license: "BSD-3-Clause"
|
|
484
|
+
},
|
|
485
|
+
"sourcekit-lsp": {
|
|
486
|
+
bundled: true,
|
|
487
|
+
binaryName: "sourcekit-lsp",
|
|
488
|
+
displayName: "sourcekit-lsp (Xcode CLT)",
|
|
489
|
+
license: "Apache-2.0"
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// ../../packages/shared/src/index.ts
|
|
496
|
+
var init_src = __esm({
|
|
497
|
+
"../../packages/shared/src/index.ts"() {
|
|
498
|
+
"use strict";
|
|
499
|
+
init_typed_resolution();
|
|
500
|
+
init_billing();
|
|
501
|
+
init_platforms();
|
|
502
|
+
init_tiers();
|
|
503
|
+
init_roles();
|
|
504
|
+
init_lsp_versions();
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// ../listener/dist/process-manager.js
|
|
509
|
+
import { spawn } from "child_process";
|
|
510
|
+
import { openSync, closeSync } from "fs";
|
|
511
|
+
import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink5 } from "fs/promises";
|
|
512
|
+
import { homedir as homedir2 } from "os";
|
|
513
|
+
import { join as join10 } from "path";
|
|
514
|
+
import { createRequire } from "module";
|
|
515
|
+
import { fileURLToPath } from "url";
|
|
516
|
+
function repowiseDir() {
|
|
517
|
+
return getConfigDir();
|
|
518
|
+
}
|
|
519
|
+
function pidPath() {
|
|
520
|
+
return join10(repowiseDir(), "listener.pid");
|
|
521
|
+
}
|
|
522
|
+
function logDirPath() {
|
|
523
|
+
return join10(repowiseDir(), "logs");
|
|
524
|
+
}
|
|
525
|
+
function resolveListenerCommand() {
|
|
526
|
+
try {
|
|
527
|
+
const require2 = createRequire(import.meta.url);
|
|
528
|
+
const mainPath = require2.resolve("@repowise/listener/main");
|
|
529
|
+
return { script: mainPath, args: [] };
|
|
530
|
+
} catch {
|
|
531
|
+
const bundlePath = fileURLToPath(import.meta.url);
|
|
532
|
+
return { script: bundlePath, args: ["__listener"] };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function readPid() {
|
|
536
|
+
try {
|
|
537
|
+
const content = await readFile6(pidPath(), "utf-8");
|
|
538
|
+
const pid = parseInt(content.trim(), 10);
|
|
539
|
+
return Number.isNaN(pid) ? null : pid;
|
|
540
|
+
} catch (err) {
|
|
541
|
+
if (err.code === "ENOENT")
|
|
542
|
+
return null;
|
|
543
|
+
throw err;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function isAlive(pid) {
|
|
547
|
+
try {
|
|
548
|
+
process.kill(pid, 0);
|
|
549
|
+
return true;
|
|
550
|
+
} catch {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
async function startBackground() {
|
|
555
|
+
if (await isRunning()) {
|
|
556
|
+
const pid2 = await readPid();
|
|
557
|
+
if (pid2)
|
|
558
|
+
return pid2;
|
|
559
|
+
}
|
|
560
|
+
const logDir2 = logDirPath();
|
|
561
|
+
await mkdir7(logDir2, { recursive: true });
|
|
562
|
+
const cmd = resolveListenerCommand();
|
|
563
|
+
const stdoutFd = openSync(join10(logDir2, "listener-stdout.log"), "a");
|
|
564
|
+
const stderrFd = openSync(join10(logDir2, "listener-stderr.log"), "a");
|
|
565
|
+
const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
|
|
566
|
+
detached: true,
|
|
567
|
+
stdio: ["ignore", stdoutFd, stderrFd],
|
|
568
|
+
cwd: homedir2(),
|
|
569
|
+
env: { ...process.env }
|
|
570
|
+
});
|
|
571
|
+
child.unref();
|
|
572
|
+
closeSync(stdoutFd);
|
|
573
|
+
closeSync(stderrFd);
|
|
574
|
+
const pid = child.pid;
|
|
575
|
+
if (!pid)
|
|
576
|
+
throw new Error("Failed to spawn listener process");
|
|
577
|
+
await writeFile7(pidPath(), String(pid));
|
|
578
|
+
return pid;
|
|
579
|
+
}
|
|
580
|
+
async function stopProcess() {
|
|
581
|
+
const pid = await readPid();
|
|
582
|
+
if (pid === null)
|
|
583
|
+
return;
|
|
584
|
+
if (!isAlive(pid)) {
|
|
585
|
+
await removePidFile();
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
process.kill(pid, "SIGTERM");
|
|
590
|
+
} catch {
|
|
591
|
+
}
|
|
592
|
+
const deadline = Date.now() + 5e3;
|
|
593
|
+
while (Date.now() < deadline && isAlive(pid)) {
|
|
594
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
595
|
+
}
|
|
596
|
+
if (isAlive(pid)) {
|
|
597
|
+
try {
|
|
598
|
+
process.kill(pid, "SIGKILL");
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
await removePidFile();
|
|
603
|
+
}
|
|
604
|
+
async function isRunning() {
|
|
605
|
+
const pid = await readPid();
|
|
606
|
+
if (pid === null)
|
|
607
|
+
return false;
|
|
608
|
+
return isAlive(pid);
|
|
609
|
+
}
|
|
610
|
+
async function removePidFile() {
|
|
611
|
+
try {
|
|
612
|
+
await unlink5(pidPath());
|
|
613
|
+
} catch {
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
var init_process_manager = __esm({
|
|
617
|
+
"../listener/dist/process-manager.js"() {
|
|
618
|
+
"use strict";
|
|
619
|
+
init_config_dir();
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
// ../listener/dist/service-installer.js
|
|
624
|
+
var service_installer_exports = {};
|
|
625
|
+
__export(service_installer_exports, {
|
|
626
|
+
install: () => install,
|
|
627
|
+
isInstalled: () => isInstalled,
|
|
628
|
+
isServiceRunning: () => isServiceRunning,
|
|
629
|
+
stopService: () => stopService,
|
|
630
|
+
uninstall: () => uninstall
|
|
631
|
+
});
|
|
632
|
+
import { execFile as execFile4 } from "child_process";
|
|
633
|
+
import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
|
|
634
|
+
import { homedir as homedir3 } from "os";
|
|
635
|
+
import { join as join13 } from "path";
|
|
636
|
+
function exec(cmd, args) {
|
|
637
|
+
return new Promise((resolve5, reject) => {
|
|
638
|
+
execFile4(cmd, args, (err, stdout) => {
|
|
639
|
+
if (err) {
|
|
640
|
+
reject(err);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
resolve5(String(stdout ?? ""));
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
function plistPath() {
|
|
648
|
+
return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
649
|
+
}
|
|
650
|
+
function logDir() {
|
|
651
|
+
return join13(getConfigDir(), "logs");
|
|
652
|
+
}
|
|
653
|
+
function buildPlist() {
|
|
654
|
+
const cmd = resolveListenerCommand();
|
|
655
|
+
const logs = logDir();
|
|
656
|
+
const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${a}</string>`).join("\n");
|
|
657
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
658
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
659
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
660
|
+
<plist version="1.0">
|
|
661
|
+
<dict>
|
|
662
|
+
<key>Label</key>
|
|
663
|
+
<string>${PLIST_LABEL}</string>
|
|
664
|
+
<key>ProgramArguments</key>
|
|
665
|
+
<array>
|
|
666
|
+
${programArgs}
|
|
667
|
+
</array>
|
|
668
|
+
<key>RunAtLoad</key>
|
|
669
|
+
<true/>
|
|
670
|
+
<key>KeepAlive</key>
|
|
671
|
+
<true/>
|
|
672
|
+
<key>StandardOutPath</key>
|
|
673
|
+
<string>${join13(logs, "listener-stdout.log")}</string>
|
|
674
|
+
<key>StandardErrorPath</key>
|
|
675
|
+
<string>${join13(logs, "listener-stderr.log")}</string>
|
|
676
|
+
<key>ProcessType</key>
|
|
677
|
+
<string>Background</string>
|
|
678
|
+
<!--
|
|
679
|
+
SoftResourceLimits: raise the open-file descriptor limit from the macOS
|
|
680
|
+
default of 256 to 4096. When multiple LSP child processes (rust-analyzer,
|
|
681
|
+
jdtls, etc.) share the listener's fd budget they can exhaust the default
|
|
682
|
+
very quickly, causing EMFILE errors. 4096 matches the system-wide default
|
|
683
|
+
set by /etc/launchd.conf on modern macOS and gives enough headroom for
|
|
684
|
+
~10 concurrent LSP servers each opening ~300 fds.
|
|
685
|
+
|
|
686
|
+
MemoryLimit: intentionally NOT set here. The launchd MemoryLimit key
|
|
687
|
+
(available since macOS 12.3) terminates the entire LaunchAgent when RSS
|
|
688
|
+
exceeds the threshold \u2014 it cannot be scoped to individual child processes.
|
|
689
|
+
Killing the listener to evict a memory-hungry rust-analyzer instance is
|
|
690
|
+
undesirable. Per-child memory pressure is better handled by the LSP
|
|
691
|
+
process manager (process-manager.ts) or by OS-level memory pressure
|
|
692
|
+
signals (SIGTERM on macOS, OOM killer on Linux).
|
|
693
|
+
-->
|
|
694
|
+
<key>SoftResourceLimits</key>
|
|
695
|
+
<dict>
|
|
696
|
+
<key>NumberOfFiles</key>
|
|
697
|
+
<integer>4096</integer>
|
|
698
|
+
</dict>
|
|
699
|
+
</dict>
|
|
700
|
+
</plist>`;
|
|
701
|
+
}
|
|
702
|
+
async function darwinInstall() {
|
|
703
|
+
await mkdir8(logDir(), { recursive: true });
|
|
704
|
+
await mkdir8(join13(homedir3(), "Library", "LaunchAgents"), { recursive: true });
|
|
705
|
+
try {
|
|
706
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
await writeFile8(plistPath(), buildPlist());
|
|
710
|
+
await exec("launchctl", ["load", plistPath()]);
|
|
711
|
+
}
|
|
712
|
+
async function darwinUninstall() {
|
|
713
|
+
try {
|
|
714
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
await unlink6(plistPath());
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async function darwinIsInstalled() {
|
|
723
|
+
try {
|
|
724
|
+
const stdout = await exec("launchctl", ["list"]);
|
|
725
|
+
return stdout.includes(PLIST_LABEL);
|
|
726
|
+
} catch {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function unitPath() {
|
|
731
|
+
return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
732
|
+
}
|
|
733
|
+
function buildUnit() {
|
|
734
|
+
const cmd = resolveListenerCommand();
|
|
735
|
+
const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
|
|
736
|
+
const logs = logDir();
|
|
737
|
+
return `[Unit]
|
|
738
|
+
Description=RepoWise Listener
|
|
739
|
+
After=network-online.target
|
|
740
|
+
Wants=network-online.target
|
|
741
|
+
|
|
742
|
+
[Service]
|
|
743
|
+
Type=simple
|
|
744
|
+
ExecStart=${execStart}
|
|
745
|
+
Restart=always
|
|
746
|
+
RestartSec=10
|
|
747
|
+
StandardOutput=append:${join13(logs, "listener-stdout.log")}
|
|
748
|
+
StandardError=append:${join13(logs, "listener-stderr.log")}
|
|
749
|
+
|
|
750
|
+
[Install]
|
|
751
|
+
WantedBy=default.target`;
|
|
752
|
+
}
|
|
753
|
+
async function linuxInstall() {
|
|
754
|
+
await mkdir8(logDir(), { recursive: true });
|
|
755
|
+
await mkdir8(join13(homedir3(), ".config", "systemd", "user"), { recursive: true });
|
|
756
|
+
await writeFile8(unitPath(), buildUnit());
|
|
757
|
+
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
758
|
+
await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
|
|
759
|
+
await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
|
|
760
|
+
}
|
|
761
|
+
async function linuxUninstall() {
|
|
762
|
+
try {
|
|
763
|
+
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
764
|
+
} catch {
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
|
|
768
|
+
} catch {
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
await unlink6(unitPath());
|
|
772
|
+
} catch {
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
async function linuxIsInstalled() {
|
|
780
|
+
try {
|
|
781
|
+
const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
|
|
782
|
+
return stdout.trim() === "enabled";
|
|
783
|
+
} catch {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
async function win32Install() {
|
|
788
|
+
await mkdir8(logDir(), { recursive: true });
|
|
789
|
+
const cmd = resolveListenerCommand();
|
|
790
|
+
const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
|
|
791
|
+
await exec("schtasks", [
|
|
792
|
+
"/create",
|
|
793
|
+
"/tn",
|
|
794
|
+
TASK_NAME,
|
|
795
|
+
"/tr",
|
|
796
|
+
taskCmd,
|
|
797
|
+
"/sc",
|
|
798
|
+
"onlogon",
|
|
799
|
+
"/ru",
|
|
800
|
+
process.env.USERNAME ?? "",
|
|
801
|
+
"/f"
|
|
802
|
+
]);
|
|
803
|
+
await exec("schtasks", ["/run", "/tn", TASK_NAME]);
|
|
804
|
+
}
|
|
805
|
+
async function win32Uninstall() {
|
|
806
|
+
try {
|
|
807
|
+
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
808
|
+
} catch {
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
async function win32IsInstalled() {
|
|
816
|
+
try {
|
|
817
|
+
await exec("schtasks", ["/query", "/tn", TASK_NAME]);
|
|
818
|
+
return true;
|
|
819
|
+
} catch {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
async function darwinStop() {
|
|
824
|
+
try {
|
|
825
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
async function linuxStop() {
|
|
830
|
+
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
831
|
+
}
|
|
832
|
+
async function win32Stop() {
|
|
833
|
+
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
834
|
+
}
|
|
835
|
+
async function darwinIsServiceRunning() {
|
|
836
|
+
try {
|
|
837
|
+
const stdout = await exec("launchctl", ["list", PLIST_LABEL]);
|
|
838
|
+
const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
|
|
839
|
+
return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
|
|
840
|
+
} catch {
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
async function linuxIsServiceRunning() {
|
|
845
|
+
try {
|
|
846
|
+
const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
|
|
847
|
+
return stdout.trim() === "active";
|
|
848
|
+
} catch {
|
|
849
|
+
return false;
|
|
931
850
|
}
|
|
932
|
-
});
|
|
933
|
-
|
|
934
|
-
// ../../packages/shared/src/types/typed-resolution.ts
|
|
935
|
-
function typedResolutionKey(filePath, propertyName, line, column) {
|
|
936
|
-
return `${filePath}\0${propertyName}\0${line.toString()}\0${column.toString()}`;
|
|
937
851
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
852
|
+
async function win32IsServiceRunning() {
|
|
853
|
+
try {
|
|
854
|
+
const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
|
|
855
|
+
return stdout.includes("Running");
|
|
856
|
+
} catch {
|
|
857
|
+
return false;
|
|
944
858
|
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
859
|
+
}
|
|
860
|
+
async function stopService() {
|
|
861
|
+
switch (process.platform) {
|
|
862
|
+
case "darwin":
|
|
863
|
+
await darwinStop();
|
|
864
|
+
break;
|
|
865
|
+
case "linux":
|
|
866
|
+
await linuxStop();
|
|
867
|
+
break;
|
|
868
|
+
case "win32":
|
|
869
|
+
await win32Stop();
|
|
870
|
+
break;
|
|
871
|
+
default:
|
|
872
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
951
873
|
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
874
|
+
}
|
|
875
|
+
async function install() {
|
|
876
|
+
switch (process.platform) {
|
|
877
|
+
case "darwin":
|
|
878
|
+
await darwinInstall();
|
|
879
|
+
break;
|
|
880
|
+
case "linux":
|
|
881
|
+
await linuxInstall();
|
|
882
|
+
break;
|
|
883
|
+
case "win32":
|
|
884
|
+
await win32Install();
|
|
885
|
+
break;
|
|
886
|
+
default:
|
|
887
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
958
888
|
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
};
|
|
974
|
-
TIER_LIMITS = {
|
|
975
|
-
[SUBSCRIPTION_TIERS.FREE]: {
|
|
976
|
-
maxRepos: 1,
|
|
977
|
-
maxSeats: 1,
|
|
978
|
-
minSeats: 1,
|
|
979
|
-
maxSyncsPerMonth: 4,
|
|
980
|
-
// manual-only, hard cap (no overage)
|
|
981
|
-
maxConcurrentSyncs: 1,
|
|
982
|
-
freeFullScans: 0,
|
|
983
|
-
// graph-only: no LLM full scan
|
|
984
|
-
bedrockEndpoint: "public"
|
|
985
|
-
},
|
|
986
|
-
// Legacy alias — retains the OLD Starter limits until existing Starter subs are
|
|
987
|
-
// migrated to Free, then removed (M7). Do NOT reference for new logic.
|
|
988
|
-
[SUBSCRIPTION_TIERS.STARTER]: {
|
|
989
|
-
maxRepos: 1,
|
|
990
|
-
maxSeats: 1,
|
|
991
|
-
minSeats: 1,
|
|
992
|
-
maxSyncsPerMonth: 10,
|
|
993
|
-
maxConcurrentSyncs: 1,
|
|
994
|
-
freeFullScans: 1,
|
|
995
|
-
bedrockEndpoint: "public"
|
|
996
|
-
},
|
|
997
|
-
[SUBSCRIPTION_TIERS.PRO]: {
|
|
998
|
-
maxRepos: 1,
|
|
999
|
-
maxSeats: 1,
|
|
1000
|
-
minSeats: 1,
|
|
1001
|
-
maxSyncsPerMonth: 20,
|
|
1002
|
-
maxConcurrentSyncs: 2,
|
|
1003
|
-
freeFullScans: 1,
|
|
1004
|
-
bedrockEndpoint: "public"
|
|
1005
|
-
},
|
|
1006
|
-
[SUBSCRIPTION_TIERS.TEAM]: {
|
|
1007
|
-
maxRepos: 1,
|
|
1008
|
-
maxSeats: 999999,
|
|
1009
|
-
minSeats: 3,
|
|
1010
|
-
maxSyncsPerMonth: 30,
|
|
1011
|
-
maxConcurrentSyncs: 10,
|
|
1012
|
-
freeFullScans: 1,
|
|
1013
|
-
bedrockEndpoint: "public"
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
TIER_FEATURES = {
|
|
1017
|
-
[SUBSCRIPTION_TIERS.FREE]: {
|
|
1018
|
-
knowledgeGraph: true,
|
|
1019
|
-
mcp: true,
|
|
1020
|
-
context: false,
|
|
1021
|
-
// LLM-generated narrative context docs
|
|
1022
|
-
docs: false,
|
|
1023
|
-
// RepoWise Docs
|
|
1024
|
-
autoSync: false,
|
|
1025
|
-
// auto-sync on merge (Free is manual-only)
|
|
1026
|
-
overage: false
|
|
1027
|
-
// pay-as-you-go syncs (Free is hard-capped)
|
|
1028
|
-
},
|
|
1029
|
-
// Legacy alias — full features until existing Starter subs migrate to Free.
|
|
1030
|
-
[SUBSCRIPTION_TIERS.STARTER]: {
|
|
1031
|
-
knowledgeGraph: true,
|
|
1032
|
-
mcp: true,
|
|
1033
|
-
context: true,
|
|
1034
|
-
docs: true,
|
|
1035
|
-
autoSync: true,
|
|
1036
|
-
overage: true
|
|
1037
|
-
},
|
|
1038
|
-
[SUBSCRIPTION_TIERS.PRO]: {
|
|
1039
|
-
knowledgeGraph: true,
|
|
1040
|
-
mcp: true,
|
|
1041
|
-
context: true,
|
|
1042
|
-
docs: true,
|
|
1043
|
-
autoSync: true,
|
|
1044
|
-
overage: true
|
|
1045
|
-
},
|
|
1046
|
-
[SUBSCRIPTION_TIERS.TEAM]: {
|
|
1047
|
-
knowledgeGraph: true,
|
|
1048
|
-
mcp: true,
|
|
1049
|
-
context: true,
|
|
1050
|
-
docs: true,
|
|
1051
|
-
autoSync: true,
|
|
1052
|
-
overage: true
|
|
1053
|
-
}
|
|
1054
|
-
};
|
|
889
|
+
}
|
|
890
|
+
async function uninstall() {
|
|
891
|
+
switch (process.platform) {
|
|
892
|
+
case "darwin":
|
|
893
|
+
await darwinUninstall();
|
|
894
|
+
break;
|
|
895
|
+
case "linux":
|
|
896
|
+
await linuxUninstall();
|
|
897
|
+
break;
|
|
898
|
+
case "win32":
|
|
899
|
+
await win32Uninstall();
|
|
900
|
+
break;
|
|
901
|
+
default:
|
|
902
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
1055
903
|
}
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
// ../../packages/shared/src/constants/roles.ts
|
|
1059
|
-
function hasPermission(role, permission) {
|
|
1060
|
-
return PERMISSIONS[role][permission];
|
|
1061
904
|
}
|
|
1062
|
-
function
|
|
1063
|
-
|
|
1064
|
-
|
|
905
|
+
async function isInstalled() {
|
|
906
|
+
switch (process.platform) {
|
|
907
|
+
case "darwin":
|
|
908
|
+
return darwinIsInstalled();
|
|
909
|
+
case "linux":
|
|
910
|
+
return linuxIsInstalled();
|
|
911
|
+
case "win32":
|
|
912
|
+
return win32IsInstalled();
|
|
913
|
+
default:
|
|
914
|
+
return false;
|
|
1065
915
|
}
|
|
1066
|
-
return ROLES.MEMBER;
|
|
1067
916
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
connectRepos: true,
|
|
1091
|
-
viewSync: true,
|
|
1092
|
-
triggerRetry: true,
|
|
1093
|
-
configWatcher: true,
|
|
1094
|
-
inviteMembers: true,
|
|
1095
|
-
manageBilling: false,
|
|
1096
|
-
configAiTools: true,
|
|
1097
|
-
manageTeam: true
|
|
1098
|
-
},
|
|
1099
|
-
[ROLES.REPO_MANAGER]: {
|
|
1100
|
-
connectRepos: true,
|
|
1101
|
-
viewSync: true,
|
|
1102
|
-
triggerRetry: true,
|
|
1103
|
-
configWatcher: true,
|
|
1104
|
-
inviteMembers: false,
|
|
1105
|
-
manageBilling: false,
|
|
1106
|
-
configAiTools: true,
|
|
1107
|
-
manageTeam: false
|
|
1108
|
-
},
|
|
1109
|
-
[ROLES.MEMBER]: {
|
|
1110
|
-
connectRepos: false,
|
|
1111
|
-
viewSync: true,
|
|
1112
|
-
triggerRetry: true,
|
|
1113
|
-
configWatcher: false,
|
|
1114
|
-
inviteMembers: false,
|
|
1115
|
-
manageBilling: false,
|
|
1116
|
-
configAiTools: true,
|
|
1117
|
-
manageTeam: false
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
ROLE_PRIORITY = [
|
|
1121
|
-
ROLES.OWNER,
|
|
1122
|
-
ROLES.ADMIN,
|
|
1123
|
-
ROLES.REPO_MANAGER,
|
|
1124
|
-
ROLES.MEMBER
|
|
1125
|
-
];
|
|
917
|
+
async function isServiceRunning() {
|
|
918
|
+
switch (process.platform) {
|
|
919
|
+
case "darwin":
|
|
920
|
+
return darwinIsServiceRunning();
|
|
921
|
+
case "linux":
|
|
922
|
+
return linuxIsServiceRunning();
|
|
923
|
+
case "win32":
|
|
924
|
+
return win32IsServiceRunning();
|
|
925
|
+
default:
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
|
|
930
|
+
var init_service_installer = __esm({
|
|
931
|
+
"../listener/dist/service-installer.js"() {
|
|
932
|
+
"use strict";
|
|
933
|
+
init_config_dir();
|
|
934
|
+
init_process_manager();
|
|
935
|
+
IS_STAGING = true ? true : false;
|
|
936
|
+
PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
|
|
937
|
+
SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
|
|
938
|
+
TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
|
|
1126
939
|
}
|
|
1127
940
|
});
|
|
1128
941
|
|
|
1129
|
-
//
|
|
1130
|
-
function
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
942
|
+
// ../listener/dist/lsp/registry.js
|
|
943
|
+
function resolvePhpLspOverride(env = process.env, configOverrides) {
|
|
944
|
+
const envChoice = env["PHP_LSP"];
|
|
945
|
+
const configChoice = configOverrides?.["php"];
|
|
946
|
+
const choice = envChoice ?? configChoice;
|
|
947
|
+
if (choice !== "intelephense")
|
|
948
|
+
return null;
|
|
949
|
+
return {
|
|
950
|
+
id: "intelephense",
|
|
951
|
+
displayName: "intelephense (user-provided)",
|
|
952
|
+
command: "intelephense",
|
|
953
|
+
args: ["--stdio"],
|
|
954
|
+
extensions: [".php"],
|
|
955
|
+
lspLanguageId: "php",
|
|
956
|
+
installHint: "Intelephense override is enabled \u2014 install it yourself: `npm i -g intelephense` (commercial licence required for premium features)."
|
|
1139
957
|
};
|
|
1140
|
-
const p = platformMap[platform];
|
|
1141
|
-
const a = archMap[arch];
|
|
1142
|
-
if (!p || !a) return null;
|
|
1143
|
-
return `${p}-${a}`;
|
|
1144
958
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
959
|
+
function getEffectiveConfig(lang, env = process.env, configOverrides) {
|
|
960
|
+
if (lang === "php") {
|
|
961
|
+
const override = resolvePhpLspOverride(env, configOverrides);
|
|
962
|
+
if (override)
|
|
963
|
+
return override;
|
|
964
|
+
}
|
|
965
|
+
return LSP_REGISTRY[lang]?.[0];
|
|
966
|
+
}
|
|
967
|
+
function getEffectiveConfigList(lang, env = process.env, configOverrides) {
|
|
968
|
+
if (lang === "php") {
|
|
969
|
+
const override = resolvePhpLspOverride(env, configOverrides);
|
|
970
|
+
if (override)
|
|
971
|
+
return [override];
|
|
972
|
+
}
|
|
973
|
+
return LSP_REGISTRY[lang] ?? [];
|
|
974
|
+
}
|
|
975
|
+
function detectLanguage(path) {
|
|
976
|
+
const dot = path.lastIndexOf(".");
|
|
977
|
+
if (dot < 0)
|
|
978
|
+
return null;
|
|
979
|
+
const ext = path.slice(dot).toLowerCase();
|
|
980
|
+
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
981
|
+
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
982
|
+
return lang;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
async function probeServers(isAvailable, env = process.env, configOverrides) {
|
|
988
|
+
const out = [];
|
|
989
|
+
for (const lang of Object.keys(LSP_REGISTRY)) {
|
|
990
|
+
const configs = getEffectiveConfigList(lang, env, configOverrides);
|
|
991
|
+
const checkedCommands = configs.map((c) => c.command);
|
|
992
|
+
let found = null;
|
|
993
|
+
for (const config2 of configs) {
|
|
994
|
+
if (await isAvailable(config2.command)) {
|
|
995
|
+
found = config2;
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
out.push({ language: lang, config: found, checkedCommands });
|
|
1000
|
+
}
|
|
1001
|
+
return out;
|
|
1002
|
+
}
|
|
1003
|
+
function getBuildSpec(language) {
|
|
1004
|
+
return BUILD_SPECS[language] ?? null;
|
|
1005
|
+
}
|
|
1006
|
+
var LSP_REGISTRY, BUILD_SPECS;
|
|
1007
|
+
var init_registry = __esm({
|
|
1008
|
+
"../listener/dist/lsp/registry.js"() {
|
|
1148
1009
|
"use strict";
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
"
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
"
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
"
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
"
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1010
|
+
LSP_REGISTRY = {
|
|
1011
|
+
typescript: [
|
|
1012
|
+
{
|
|
1013
|
+
id: "typescript-language-server",
|
|
1014
|
+
displayName: "typescript-language-server",
|
|
1015
|
+
command: "typescript-language-server",
|
|
1016
|
+
args: ["--stdio"],
|
|
1017
|
+
extensions: [".ts", ".tsx", ".mts", ".cts"],
|
|
1018
|
+
lspLanguageId: "typescript",
|
|
1019
|
+
installHint: "npm i -g typescript typescript-language-server",
|
|
1020
|
+
npmPackage: "typescript-language-server"
|
|
1021
|
+
}
|
|
1022
|
+
],
|
|
1023
|
+
javascript: [
|
|
1024
|
+
{
|
|
1025
|
+
id: "typescript-language-server-js",
|
|
1026
|
+
displayName: "typescript-language-server (for JS)",
|
|
1027
|
+
command: "typescript-language-server",
|
|
1028
|
+
args: ["--stdio"],
|
|
1029
|
+
extensions: [".js", ".jsx", ".mjs", ".cjs"],
|
|
1030
|
+
lspLanguageId: "javascript",
|
|
1031
|
+
installHint: "npm i -g typescript typescript-language-server",
|
|
1032
|
+
npmPackage: "typescript-language-server"
|
|
1033
|
+
}
|
|
1034
|
+
],
|
|
1035
|
+
python: [
|
|
1036
|
+
{
|
|
1037
|
+
id: "pyright",
|
|
1038
|
+
displayName: "pyright",
|
|
1039
|
+
command: "pyright-langserver",
|
|
1040
|
+
args: ["--stdio"],
|
|
1041
|
+
extensions: [".py", ".pyi"],
|
|
1042
|
+
lspLanguageId: "python",
|
|
1043
|
+
installHint: "npm i -g pyright",
|
|
1044
|
+
npmPackage: "pyright",
|
|
1045
|
+
// Phase 7L: strict-mode Pyright. Default `basic` mode suppresses
|
|
1046
|
+
// definition/hover for un-annotated locals which kneecaps the
|
|
1047
|
+
// upgrade rate on real codebases. `useLibraryCodeForTypes`
|
|
1048
|
+
// unlocks stdlib member lookups, `autoSearchPaths` lets Pyright
|
|
1049
|
+
// find venv site-packages without per-project pyrightconfig.json,
|
|
1050
|
+
// and `diagnosticMode: 'workspace'` forces full-workspace symbol
|
|
1051
|
+
// materialization so the first `workspace/symbol` query doesn't
|
|
1052
|
+
// race the indexer.
|
|
1053
|
+
//
|
|
1054
|
+
// Disable `publishDiagnostics` — we don't render lint output
|
|
1055
|
+
// and the noise slows Pyright on large projects (plan §Python
|
|
1056
|
+
// risks).
|
|
1057
|
+
initializationOptions: {
|
|
1058
|
+
python: {
|
|
1059
|
+
analysis: {
|
|
1060
|
+
typeCheckingMode: "strict",
|
|
1061
|
+
useLibraryCodeForTypes: true,
|
|
1062
|
+
autoSearchPaths: true,
|
|
1063
|
+
diagnosticMode: "workspace"
|
|
1064
|
+
// Pyright honours either `disableLanguageServices` or
|
|
1065
|
+
// simply not subscribing to diagnostics; the conservative
|
|
1066
|
+
// path is to send the request handshake without
|
|
1067
|
+
// publishDiagnostics in our capabilities (already the
|
|
1068
|
+
// case in workspace-session.ts) and avoid extra knobs
|
|
1069
|
+
// here that would diverge from Pyright defaults.
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1184
1072
|
}
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
id: "pylsp",
|
|
1076
|
+
displayName: "python-lsp-server",
|
|
1077
|
+
command: "pylsp",
|
|
1078
|
+
args: [],
|
|
1079
|
+
extensions: [".py", ".pyi"],
|
|
1080
|
+
lspLanguageId: "python",
|
|
1081
|
+
installHint: 'pipx install "python-lsp-server[all]"'
|
|
1185
1082
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
//
|
|
1193
|
-
//
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
"
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
"
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1083
|
+
],
|
|
1084
|
+
go: [
|
|
1085
|
+
{
|
|
1086
|
+
id: "gopls",
|
|
1087
|
+
displayName: "gopls",
|
|
1088
|
+
command: "gopls",
|
|
1089
|
+
// Modern gopls uses stdio by default; `serve` is the legacy
|
|
1090
|
+
// subcommand and no longer needed (review finding).
|
|
1091
|
+
args: [],
|
|
1092
|
+
extensions: [".go"],
|
|
1093
|
+
lspLanguageId: "go",
|
|
1094
|
+
installHint: "go install golang.org/x/tools/gopls@latest",
|
|
1095
|
+
toolchainInstallKey: "gopls"
|
|
1096
|
+
}
|
|
1097
|
+
],
|
|
1098
|
+
rust: [
|
|
1099
|
+
{
|
|
1100
|
+
id: "rust-analyzer",
|
|
1101
|
+
displayName: "rust-analyzer",
|
|
1102
|
+
command: "rust-analyzer",
|
|
1103
|
+
args: [],
|
|
1104
|
+
extensions: [".rs"],
|
|
1105
|
+
lspLanguageId: "rust",
|
|
1106
|
+
nativeInstallKey: "rust-analyzer",
|
|
1107
|
+
// `rustup component add rust-analyzer` installs a rustup proxy
|
|
1108
|
+
// — the binary may not be on PATH directly. `brew install
|
|
1109
|
+
// rust-analyzer` or a release tarball is more reliable.
|
|
1110
|
+
installHint: "brew install rust-analyzer (or 'rustup component add rust-analyzer' and ensure it is on PATH)",
|
|
1111
|
+
// Phase 7L: rust-analyzer init options. `cargo.buildScripts.enable`
|
|
1112
|
+
// and `procMacro.enable` are critical — without them, half of
|
|
1113
|
+
// Serde's call graph is invisible (proc-macro-expanded code is
|
|
1114
|
+
// skipped and `derive(Serialize)` impl trait methods disappear).
|
|
1115
|
+
// `checkOnSave.enable: false` keeps the server quiet on workspace
|
|
1116
|
+
// edits since we don't render diagnostics.
|
|
1117
|
+
initializationOptions: {
|
|
1118
|
+
cargo: {
|
|
1119
|
+
buildScripts: { enable: true }
|
|
1120
|
+
},
|
|
1121
|
+
procMacro: { enable: true },
|
|
1122
|
+
checkOnSave: { enable: false }
|
|
1213
1123
|
}
|
|
1214
1124
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
"
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1125
|
+
],
|
|
1126
|
+
java: [
|
|
1127
|
+
// The `jdtls` binary exists when installed via Homebrew (which
|
|
1128
|
+
// ships a wrapper over the Eclipse JDT launcher) or the nvim
|
|
1129
|
+
// jdtls scripts. Raw Eclipse downloads require invoking
|
|
1130
|
+
// `java -jar org.eclipse.equinox.launcher_*.jar`; doctor needs
|
|
1131
|
+
// to surface that when the bare `jdtls` binary is absent.
|
|
1132
|
+
//
|
|
1133
|
+
// Phase 7L init options:
|
|
1134
|
+
// - autobuild disabled — per-edit work the listener doesn't
|
|
1135
|
+
// need; saves CPU + memory on the cold start path.
|
|
1136
|
+
// - completion disabled — same rationale.
|
|
1137
|
+
// - progressReportProvider true — needed so waitForIndex can
|
|
1138
|
+
// subscribe to "Importing projects" / "Building workspace"
|
|
1139
|
+
// progress and block receiver queries until end-of-progress.
|
|
1140
|
+
//
|
|
1141
|
+
// JVM args:
|
|
1142
|
+
// - `-Xmx2G` — JDTLS OOMs on real-world repos with the default
|
|
1143
|
+
// `-Xmx256m`. Without this the OOM kills the server inside
|
|
1144
|
+
// workspace import, which opens the Java per-repo circuit
|
|
1145
|
+
// breaker (plan §Java failure modes).
|
|
1146
|
+
// - `-data <repoSha-scoped workspace>` — added at spawn time by
|
|
1147
|
+
// workspace-session.ts so JDTLS skips the Maven/Gradle import
|
|
1148
|
+
// on subsequent runs. Watch pom.xml/build.gradle mtime to
|
|
1149
|
+
// invalidate stale incremental state.
|
|
1150
|
+
{
|
|
1151
|
+
id: "jdtls",
|
|
1152
|
+
displayName: "Eclipse JDT Language Server",
|
|
1153
|
+
command: "jdtls",
|
|
1154
|
+
args: ["--jvm-arg=-Xmx2G"],
|
|
1155
|
+
extensions: [".java"],
|
|
1156
|
+
lspLanguageId: "java",
|
|
1157
|
+
installHint: "brew install jdtls (see https://github.com/eclipse-jdtls/eclipse.jdt.ls)",
|
|
1158
|
+
nativeInstallKey: "jdtls",
|
|
1159
|
+
initializationOptions: {
|
|
1160
|
+
bundles: [],
|
|
1161
|
+
extendedClientCapabilities: {
|
|
1162
|
+
progressReportProvider: true,
|
|
1163
|
+
classFileContentsSupport: true
|
|
1164
|
+
},
|
|
1165
|
+
settings: {
|
|
1166
|
+
java: {
|
|
1167
|
+
autobuild: { enabled: false },
|
|
1168
|
+
completion: { enabled: false },
|
|
1169
|
+
// Don't render diagnostics — listener doesn't surface
|
|
1170
|
+
// Java lint output and the firehose slows JDTLS.
|
|
1171
|
+
errors: { incompleteClasspath: { severity: "ignore" } }
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1247
1174
|
}
|
|
1248
1175
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
"
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
"
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1176
|
+
],
|
|
1177
|
+
csharp: [
|
|
1178
|
+
// Issue #352 item #5 — csharp-ls listed FIRST. Both are MIT-OSS
|
|
1179
|
+
// and Roslyn-backed, but csharp-ls is significantly lighter (~10 MB
|
|
1180
|
+
// dotnet-tool install vs. OmniSharp's ~200 MB native binary). On
|
|
1181
|
+
// user machines with `dotnet` on PATH, csharp-ls is installable in
|
|
1182
|
+
// one command with no native-binary fetch. OmniSharp stays as the
|
|
1183
|
+
// installed-elsewhere fallback (Roslyn-build farms, JetBrains
|
|
1184
|
+
// Rider tool dirs, etc.).
|
|
1185
|
+
{
|
|
1186
|
+
id: "csharp-ls",
|
|
1187
|
+
displayName: "csharp-ls",
|
|
1188
|
+
command: "csharp-ls",
|
|
1189
|
+
args: [],
|
|
1190
|
+
extensions: [".cs"],
|
|
1191
|
+
lspLanguageId: "csharp",
|
|
1192
|
+
installHint: "dotnet tool install -g csharp-ls",
|
|
1193
|
+
toolchainInstallKey: "csharp-ls"
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
id: "omnisharp",
|
|
1197
|
+
displayName: "OmniSharp",
|
|
1198
|
+
command: "omnisharp",
|
|
1199
|
+
args: ["-lsp"],
|
|
1200
|
+
extensions: [".cs"],
|
|
1201
|
+
lspLanguageId: "csharp",
|
|
1202
|
+
installHint: "https://github.com/OmniSharp/omnisharp-roslyn/releases"
|
|
1203
|
+
}
|
|
1204
|
+
],
|
|
1205
|
+
php: [
|
|
1206
|
+
// Phase 7L++: Phpactor only. Phpactor is MIT-licensed, free, and
|
|
1207
|
+
// ships as a single phar via Composer. We DO NOT bundle
|
|
1208
|
+
// Intelephense — its premium tier is commercial and its community
|
|
1209
|
+
// licence is not redistributable. Customers who already own an
|
|
1210
|
+
// Intelephense licence can opt-in via the `PHP_LSP=intelephense`
|
|
1211
|
+
// environment variable, which the CLI swaps into the registry at
|
|
1212
|
+
// runtime (see apps/cli/src/commands/lsp.ts step 7). The escape
|
|
1213
|
+
// hatch requires the user to provision an `intelephense` binary
|
|
1214
|
+
// on PATH themselves.
|
|
1215
|
+
//
|
|
1216
|
+
// Confidence floor on PHP is 0.7 (see lsp-upgrade-strategy.ts).
|
|
1217
|
+
// Phpactor's static analysis is solid for PSR-4 resolvable calls
|
|
1218
|
+
// but stumbles on Laravel-style facades / magic accessors — those
|
|
1219
|
+
// surface as null definitions, recorded as `kind: 'definition'`
|
|
1220
|
+
// with `targets.length === 0` (no upgrade, telemetry preserved).
|
|
1221
|
+
{
|
|
1222
|
+
id: "phpactor",
|
|
1223
|
+
displayName: "phpactor",
|
|
1224
|
+
command: "phpactor",
|
|
1225
|
+
args: ["language-server"],
|
|
1226
|
+
extensions: [".php"],
|
|
1227
|
+
lspLanguageId: "php",
|
|
1228
|
+
installHint: "composer global require phpactor/phpactor (see https://phpactor.readthedocs.io/en/master/usage/standalone.html)",
|
|
1229
|
+
toolchainInstallKey: "phpactor"
|
|
1230
|
+
}
|
|
1231
|
+
],
|
|
1232
|
+
ruby: [
|
|
1233
|
+
{
|
|
1234
|
+
id: "ruby-lsp",
|
|
1235
|
+
displayName: "ruby-lsp",
|
|
1236
|
+
command: "ruby-lsp",
|
|
1237
|
+
args: [],
|
|
1238
|
+
extensions: [".rb"],
|
|
1239
|
+
lspLanguageId: "ruby",
|
|
1240
|
+
installHint: "gem install ruby-lsp",
|
|
1241
|
+
toolchainInstallKey: "ruby-lsp"
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
id: "solargraph",
|
|
1245
|
+
displayName: "solargraph",
|
|
1246
|
+
command: "solargraph",
|
|
1247
|
+
args: ["stdio"],
|
|
1248
|
+
extensions: [".rb"],
|
|
1249
|
+
lspLanguageId: "ruby",
|
|
1250
|
+
installHint: "gem install solargraph"
|
|
1251
|
+
}
|
|
1252
|
+
],
|
|
1253
|
+
kotlin: [
|
|
1254
|
+
// Phase 7L++: fwcd Kotlin LSP. JVM cold-start is ~30s on the first
|
|
1255
|
+
// Gradle/Maven import — the language server materialises the
|
|
1256
|
+
// build's classpath before symbol lookups return. On mixed
|
|
1257
|
+
// Kotlin/Java repos the WorkspaceManager spawns BOTH `jdtls` and
|
|
1258
|
+
// `kotlin-language-server` — each owns its own file-type filter
|
|
1259
|
+
// and they coexist without contention. Per-language LRU eviction
|
|
1260
|
+
// (5-min idle, RSS soft-cap) prevents the two JVMs from pinning
|
|
1261
|
+
// memory on long-lived listeners.
|
|
1262
|
+
//
|
|
1263
|
+
// Future: when JetBrains' Kotlin LSP exits preview, drop a
|
|
1264
|
+
// higher-priority entry above this one with the JB binary; we
|
|
1265
|
+
// keep fwcd as the fallback because JB's LSP requires a JetBrains
|
|
1266
|
+
// account.
|
|
1267
|
+
{
|
|
1268
|
+
id: "kotlin-language-server",
|
|
1269
|
+
displayName: "kotlin-language-server (fwcd)",
|
|
1270
|
+
command: "kotlin-language-server",
|
|
1271
|
+
args: [],
|
|
1272
|
+
extensions: [".kt", ".kts"],
|
|
1273
|
+
lspLanguageId: "kotlin",
|
|
1274
|
+
installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)",
|
|
1275
|
+
nativeInstallKey: "kotlin-language-server"
|
|
1276
|
+
}
|
|
1277
|
+
],
|
|
1278
|
+
swift: [
|
|
1279
|
+
{
|
|
1280
|
+
id: "sourcekit-lsp",
|
|
1281
|
+
displayName: "sourcekit-lsp",
|
|
1282
|
+
command: "sourcekit-lsp",
|
|
1283
|
+
args: [],
|
|
1284
|
+
extensions: [".swift"],
|
|
1285
|
+
lspLanguageId: "swift",
|
|
1286
|
+
installHint: "Bundled with Xcode Command Line Tools on macOS",
|
|
1287
|
+
toolchainInstallKey: "sourcekit-lsp",
|
|
1288
|
+
requiresBuild: true
|
|
1289
|
+
}
|
|
1290
|
+
],
|
|
1291
|
+
dart: [
|
|
1292
|
+
// Phase 7L++: the official Dart Analysis Server ships inside the
|
|
1293
|
+
// Dart SDK as `dart language-server --protocol=lsp`. There's no
|
|
1294
|
+
// separate binary to install — installing Dart (or Flutter, which
|
|
1295
|
+
// bundles Dart) is sufficient. Confidence floor is 0.9: the Dart
|
|
1296
|
+
// analyzer is authoritative, derives types from sound null safety,
|
|
1297
|
+
// and is the same server that powers the official IDE plugins.
|
|
1298
|
+
// No `initializationOptions` — Dart's analyzer derives everything
|
|
1299
|
+
// from `pubspec.yaml` / `analysis_options.yaml` at the repo root.
|
|
1300
|
+
{
|
|
1301
|
+
id: "dart-language-server",
|
|
1302
|
+
displayName: "dart language-server",
|
|
1303
|
+
command: "dart",
|
|
1304
|
+
args: ["language-server", "--protocol=lsp"],
|
|
1305
|
+
extensions: [".dart"],
|
|
1306
|
+
lspLanguageId: "dart",
|
|
1307
|
+
installHint: "Install Dart SDK from https://dart.dev/get-dart (or Flutter from https://flutter.dev \u2014 bundles Dart).",
|
|
1308
|
+
toolchainInstallKey: "dart-language-server"
|
|
1309
|
+
}
|
|
1310
|
+
],
|
|
1311
|
+
// Scala via Metals (Phase 7L+).
|
|
1312
|
+
//
|
|
1313
|
+
// Metals is the de-facto Scala language server. It coordinates with
|
|
1314
|
+
// a build server (Bloop by default; SBT/Mill/Gradle alternatives are
|
|
1315
|
+
// discovered from the project's build files) to obtain the project's
|
|
1316
|
+
// classpath, semantic database, and compiler diagnostics. The Bloop
|
|
1317
|
+
// daemon is a separate JVM process that Metals spawns on its first
|
|
1318
|
+
// `initialize`, then keeps warm; the listener's WorkspaceManager
|
|
1319
|
+
// pairs Bloop lifecycle to Metals' (see bloop-lifecycle.ts) so they
|
|
1320
|
+
// are spawned together on lazy `getOrOpen` and torn down together
|
|
1321
|
+
// on idle eviction.
|
|
1322
|
+
//
|
|
1323
|
+
// Install hint: `coursier install metals` — Coursier (`cs`) is the
|
|
1324
|
+
// canonical Scala launcher and is typically already on a Scala
|
|
1325
|
+
// developer's machine. The Coursier-managed `metals` shim resolves
|
|
1326
|
+
// Bloop on first use, so no separate Bloop install is required.
|
|
1327
|
+
// Macro-expanded code can mis-locate definitions (Scala 3 inline /
|
|
1328
|
+
// quote macros, scala-compat shims) — the per-language strategy
|
|
1329
|
+
// pins confidence at 0.85 to reflect that limitation.
|
|
1330
|
+
scala: [
|
|
1331
|
+
{
|
|
1332
|
+
id: "metals",
|
|
1333
|
+
displayName: "Metals (Scala)",
|
|
1334
|
+
command: "metals",
|
|
1335
|
+
args: [],
|
|
1336
|
+
extensions: [".scala", ".sc", ".sbt"],
|
|
1337
|
+
lspLanguageId: "scala",
|
|
1338
|
+
installHint: "coursier install metals (see https://scalameta.org/metals \u2014 Coursier-managed binary coordinates with Bloop automatically)",
|
|
1339
|
+
coursierInstallKey: "metals"
|
|
1340
|
+
}
|
|
1341
|
+
],
|
|
1342
|
+
c: [
|
|
1343
|
+
{
|
|
1344
|
+
id: "clangd-c",
|
|
1345
|
+
displayName: "clangd",
|
|
1346
|
+
command: "clangd",
|
|
1347
|
+
// `--compile-commands-dir=.repowise` lets clangd pick up the
|
|
1348
|
+
// stub compile_commands.json the listener writes for repos
|
|
1349
|
+
// that don't ship one (header-only libs, single-file repos).
|
|
1350
|
+
// clangd still finds repo-root or build/ compile_commands.json
|
|
1351
|
+
// first when present — this is a fallback location, not an
|
|
1352
|
+
// override (task #1).
|
|
1353
|
+
args: ["--background-index", "--compile-commands-dir=.repowise"],
|
|
1354
|
+
extensions: [".c", ".h"],
|
|
1355
|
+
lspLanguageId: "c",
|
|
1356
|
+
installHint: "brew install llvm (includes clangd)",
|
|
1357
|
+
nativeInstallKey: "clangd"
|
|
1301
1358
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
"
|
|
1319
|
-
filename: "server.zip",
|
|
1320
|
-
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
1321
|
-
binaryPath: "server/bin/kotlin-language-server"
|
|
1322
|
-
},
|
|
1323
|
-
"linux-x64": {
|
|
1324
|
-
filename: "server.zip",
|
|
1325
|
-
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
1326
|
-
binaryPath: "server/bin/kotlin-language-server"
|
|
1327
|
-
},
|
|
1328
|
-
"linux-arm64": {
|
|
1329
|
-
filename: "server.zip",
|
|
1330
|
-
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
1331
|
-
binaryPath: "server/bin/kotlin-language-server"
|
|
1332
|
-
},
|
|
1333
|
-
"win32-x64": {
|
|
1334
|
-
filename: "server.zip",
|
|
1335
|
-
sha256: "4fe7d71d087b307c7869036171bd9d8c6a4284cd7c25b89098b0a24eb2d9b6d2",
|
|
1336
|
-
binaryPath: "server/bin/kotlin-language-server.bat"
|
|
1337
|
-
}
|
|
1359
|
+
],
|
|
1360
|
+
cpp: [
|
|
1361
|
+
{
|
|
1362
|
+
id: "clangd-cpp",
|
|
1363
|
+
displayName: "clangd",
|
|
1364
|
+
command: "clangd",
|
|
1365
|
+
// `--compile-commands-dir=.repowise` lets clangd pick up the
|
|
1366
|
+
// stub compile_commands.json the listener writes for repos
|
|
1367
|
+
// that don't ship one (header-only libs, single-file repos).
|
|
1368
|
+
// clangd still finds repo-root or build/ compile_commands.json
|
|
1369
|
+
// first when present — this is a fallback location, not an
|
|
1370
|
+
// override (task #1).
|
|
1371
|
+
args: ["--background-index", "--compile-commands-dir=.repowise"],
|
|
1372
|
+
extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"],
|
|
1373
|
+
lspLanguageId: "cpp",
|
|
1374
|
+
installHint: "brew install llvm (includes clangd)",
|
|
1375
|
+
nativeInstallKey: "clangd"
|
|
1338
1376
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1377
|
+
],
|
|
1378
|
+
vue: [
|
|
1379
|
+
{
|
|
1380
|
+
id: "vue-language-server",
|
|
1381
|
+
displayName: "Vue Language Server (volar)",
|
|
1382
|
+
command: "vue-language-server",
|
|
1383
|
+
args: ["--stdio"],
|
|
1384
|
+
extensions: [".vue"],
|
|
1385
|
+
lspLanguageId: "vue",
|
|
1386
|
+
installHint: "npm i -g @vue/language-server",
|
|
1387
|
+
npmPackage: "@vue/language-server"
|
|
1388
|
+
}
|
|
1389
|
+
],
|
|
1390
|
+
svelte: [
|
|
1391
|
+
{
|
|
1392
|
+
id: "svelte-language-server",
|
|
1393
|
+
displayName: "svelte-language-server",
|
|
1394
|
+
command: "svelteserver",
|
|
1395
|
+
args: ["--stdio"],
|
|
1396
|
+
extensions: [".svelte"],
|
|
1397
|
+
lspLanguageId: "svelte",
|
|
1398
|
+
installHint: "npm i -g svelte-language-server",
|
|
1399
|
+
npmPackage: "svelte-language-server"
|
|
1400
|
+
}
|
|
1401
|
+
]
|
|
1347
1402
|
};
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
"ruby-lsp": {
|
|
1358
|
-
toolchain: "gem",
|
|
1359
|
-
installArgs: ["install", "--user-install", "ruby-lsp"],
|
|
1360
|
-
binaryName: "ruby-lsp",
|
|
1361
|
-
// gem userdir is version-dependent — resolved at install time.
|
|
1362
|
-
displayName: "ruby-lsp",
|
|
1363
|
-
license: "MIT"
|
|
1364
|
-
},
|
|
1365
|
-
"csharp-ls": {
|
|
1366
|
-
toolchain: "dotnet",
|
|
1367
|
-
installArgs: ["tool", "install", "-g", "csharp-ls"],
|
|
1368
|
-
binaryName: "csharp-ls",
|
|
1369
|
-
toolchainBinDir: ".dotnet/tools",
|
|
1370
|
-
displayName: "csharp-ls",
|
|
1371
|
-
license: "MIT"
|
|
1372
|
-
},
|
|
1373
|
-
phpactor: {
|
|
1374
|
-
toolchain: "composer",
|
|
1375
|
-
installArgs: ["global", "require", "phpactor/phpactor"],
|
|
1376
|
-
binaryName: "phpactor",
|
|
1377
|
-
toolchainBinDir: ".composer/vendor/bin",
|
|
1378
|
-
displayName: "phpactor",
|
|
1379
|
-
license: "MIT"
|
|
1380
|
-
},
|
|
1381
|
-
"dart-language-server": {
|
|
1382
|
-
bundled: true,
|
|
1383
|
-
binaryName: "dart",
|
|
1384
|
-
displayName: "dart language-server (Dart SDK)",
|
|
1385
|
-
license: "BSD-3-Clause"
|
|
1386
|
-
},
|
|
1387
|
-
"sourcekit-lsp": {
|
|
1388
|
-
bundled: true,
|
|
1389
|
-
binaryName: "sourcekit-lsp",
|
|
1390
|
-
displayName: "sourcekit-lsp (Xcode CLT)",
|
|
1391
|
-
license: "Apache-2.0"
|
|
1403
|
+
BUILD_SPECS = {
|
|
1404
|
+
swift: {
|
|
1405
|
+
gateCommands: ["swift", "xcrun"],
|
|
1406
|
+
gateExecs: [{ command: "xcode-select", args: ["-p"] }],
|
|
1407
|
+
manifests: ["Package.swift", "*.xcodeproj", "*.xcworkspace"],
|
|
1408
|
+
platform: "darwin",
|
|
1409
|
+
command: "swift",
|
|
1410
|
+
args: ["build"],
|
|
1411
|
+
indexStoreProbe: ".build/index/store"
|
|
1392
1412
|
}
|
|
1393
1413
|
};
|
|
1394
1414
|
}
|
|
1395
1415
|
});
|
|
1396
1416
|
|
|
1397
|
-
// ../../packages/shared/src/index.ts
|
|
1398
|
-
var init_src = __esm({
|
|
1399
|
-
"../../packages/shared/src/index.ts"() {
|
|
1400
|
-
"use strict";
|
|
1401
|
-
init_typed_resolution();
|
|
1402
|
-
init_billing();
|
|
1403
|
-
init_platforms();
|
|
1404
|
-
init_tiers();
|
|
1405
|
-
init_roles();
|
|
1406
|
-
init_lsp_versions();
|
|
1407
|
-
}
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
1417
|
// ../listener/dist/lsp/jdk-probe.js
|
|
1411
1418
|
import { spawn as spawn3 } from "child_process";
|
|
1412
1419
|
function parseJavaMajorVersion(stderr) {
|
|
@@ -3105,17 +3112,53 @@ Treat RepoWise as your default starting point for every task that touches this c
|
|
|
3105
3112
|
|
|
3106
3113
|
6. **Don't edit \`repowise-context/\` files manually.** They're regenerated by RepoWise on every sync. Manual edits will be overwritten. Update source code; let the next sync regenerate.
|
|
3107
3114
|
|
|
3108
|
-
7. **Mark
|
|
3115
|
+
7. **Mark a response with \`_Powered by RepoWise._\` \u2014 and ONLY when you actually used RepoWise this turn.** Begin the response with a single tag line:
|
|
3109
3116
|
|
|
3110
3117
|
_Powered by RepoWise._
|
|
3111
3118
|
|
|
3112
|
-
|
|
3119
|
+
ONLY on a turn where you genuinely used a RepoWise resource THIS turn: a RepoWise MCP/LSP tool call, OR a read of a \`repowise-context/\` file. One short line, no longer preamble. The host's tool-call UI already shows MCP/LSP calls visibly; this tag adds the same surface for the otherwise-invisible context-file reads. If the turn did not actually use RepoWise, omit the tag entirely \u2014 never add it out of habit, never fake it.
|
|
3113
3120
|
|
|
3114
3121
|
8. **MCP unavailable vs. a RepoWise miss \u2014 handle them differently.** If RepoWise MCP is entirely unavailable (no \`.mcp.json\` configured, listener not running), tell the user. But if RepoWise IS available and simply does not cover a specific question, just continue with Tier 3 \u2014 do NOT announce that "RepoWise doesn't have it." That fallback is normal and expected; the system records it on its own to improve coverage.
|
|
3115
3122
|
${END_MARKER}`;
|
|
3116
|
-
|
|
3123
|
+
var AI_AGENT_USAGE_INSTRUCTIONS_GRAPH = `${START_MARKER}
|
|
3124
|
+
## How AI Agents Should Use This Context
|
|
3125
|
+
|
|
3126
|
+
RepoWise provides a live, pre-analyzed **knowledge graph** of this codebase through its MCP server \u2014 symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, and type info \u2014 plus language-server (LSP) intelligence. Use it to navigate and reason about the code structurally instead of guessing from raw text.
|
|
3127
|
+
|
|
3128
|
+
Treat RepoWise as your default starting point for every task that touches this codebase, not a Q&A reference.
|
|
3129
|
+
|
|
3130
|
+
**Rules for using RepoWise:**
|
|
3131
|
+
|
|
3132
|
+
1. **Always start with RepoWise.** Before any task \u2014 questions, planning, coding, debugging, refactoring, tests, code review \u2014 resolve the relevant symbols and their blast radius via RepoWise FIRST. Skipping it leads to wrong assumptions, duplicated abstractions, and missed call sites.
|
|
3133
|
+
|
|
3134
|
+
2. **Use the two-tier hierarchy in this order:**
|
|
3135
|
+
- **Tier 1 \u2014 RepoWise MCP + LSP** for all structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`find_symbol\`, \`get_impact\`, \`find_callers\`, \`find_references\`, \`lsp_definition\`, \`lsp_references\`, \`lsp_hover\`, 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.
|
|
3136
|
+
- **Tier 2 \u2014 Native code search** (\`read_file\` / \`grep\` / \`glob\` on raw source) to read implementation bodies and for anything the graph does not cover (recent commits, in-flight branches, exact strings). An enforcement hook may DENY a native search until you have used a RepoWise MCP/LSP tool this turn \u2014 that is expected, not an error: query RepoWise first, then retry the same search and it will proceed.
|
|
3137
|
+
|
|
3138
|
+
3. **Batch tool calls in parallel.** Whenever you need multiple independent lookups, invoke all relevant tools SIMULTANEOUSLY rather than sequentially (e.g. confirm a symbol's shape AND its callers in one turn). Only chain serially when one tool's output is genuinely the input to another (e.g. \`find_symbol\` \u2192 then \`lsp_definition\` on the match).
|
|
3139
|
+
|
|
3140
|
+
4. **Apply per-task patterns** (always batch the gathering step):
|
|
3141
|
+
- Planning / refactoring \u2192 \`get_impact\`, \`get_deps\`, \`find_references\` to size the blast radius first.
|
|
3142
|
+
- Implementing \u2192 \`find_symbol\`, \`lsp_definition\`, \`lsp_hover\` to land on the exact definition and its type.
|
|
3143
|
+
- Debugging \u2192 \`find_callers\`, \`get_call_graph\` to trace the path.
|
|
3144
|
+
- Writing tests \u2192 \`find_tests_for_symbol\` to find existing coverage.
|
|
3145
|
+
|
|
3146
|
+
5. **Propagate to subagents.** When delegating work, explicitly instruct the sub-task to use the RepoWise MCP/LSP tools FIRST. Subagents don't inherit parent instructions automatically.
|
|
3147
|
+
|
|
3148
|
+
6. **Mark a response with \`_Powered by RepoWise._\` \u2014 and ONLY when you actually used RepoWise this turn.** Begin the response with a single tag line:
|
|
3149
|
+
|
|
3150
|
+
_Powered by RepoWise._
|
|
3151
|
+
|
|
3152
|
+
ONLY on a turn where you genuinely called a RepoWise MCP/LSP tool. One short line, no longer preamble. The host's tool-call UI already shows MCP/LSP calls visibly, so this only confirms what the turn did \u2014 if the turn did not call a RepoWise tool, omit the tag entirely; never add it out of habit, never fake it.
|
|
3153
|
+
|
|
3154
|
+
7. **MCP unavailable vs. a RepoWise miss \u2014 handle them differently.** If RepoWise MCP is entirely unavailable (no \`.mcp.json\` configured, listener not running), tell the user. But if RepoWise IS available and simply does not cover a specific question, just continue with native search \u2014 do NOT announce that "RepoWise doesn't have it." That fallback is normal and expected; the system records it on its own to improve coverage.
|
|
3155
|
+
${END_MARKER}`;
|
|
3156
|
+
function agentInstructionsFor(variant = "full") {
|
|
3157
|
+
return variant === "graph" ? AI_AGENT_USAGE_INSTRUCTIONS_GRAPH : AI_AGENT_USAGE_INSTRUCTIONS;
|
|
3158
|
+
}
|
|
3159
|
+
function injectAgentInstructions(content, variant = "full") {
|
|
3117
3160
|
const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
|
|
3118
|
-
const canonicalBlock =
|
|
3161
|
+
const canonicalBlock = agentInstructionsFor(variant).replace(/\n/g, lineEnding);
|
|
3119
3162
|
const startIdx = content.indexOf(START_MARKER);
|
|
3120
3163
|
const endIdx = content.indexOf(END_MARKER);
|
|
3121
3164
|
if (startIdx >= 0 && endIdx > startIdx) {
|
|
@@ -3159,14 +3202,20 @@ function claudeToolPrefix(repoName) {
|
|
|
3159
3202
|
}
|
|
3160
3203
|
var GRAPH_TOOLS = "find_symbol, get_symbol, get_impact, find_callers, find_references, batch_query";
|
|
3161
3204
|
var LSP_TOOLS = "lsp_definition, lsp_references, lsp_hover";
|
|
3162
|
-
function buildRepoWiseFirstDirective(repoName, contextFolder, style = "generic") {
|
|
3205
|
+
function buildRepoWiseFirstDirective(repoName, contextFolder, style = "generic", variant = "full") {
|
|
3163
3206
|
const repo = sanitizeForShellText(repoName);
|
|
3164
3207
|
const folder = sanitizeForShellText(contextFolder);
|
|
3165
3208
|
const toolNaming = style === "claude" ? `${claudeToolPrefix(repo)}<tool>, e.g. ${claudeToolPrefix(repo)}find_symbol` : `the "${mcpServerName(repo)}" MCP server`;
|
|
3209
|
+
if (variant === "graph") {
|
|
3210
|
+
return `RepoWise-first: before any codebase task, query the RepoWise knowledge graph first. Tier 1: RepoWise MCP graph tools (${toolNaming}; tools: ${GRAPH_TOOLS}) and LSP tools (${LSP_TOOLS}) for symbol resolution and blast radius. Tier 2: native file search only when the graph does not cover the question. If these tools are deferred in your environment, load them via tool search before calling.`;
|
|
3211
|
+
}
|
|
3166
3212
|
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.`;
|
|
3167
3213
|
}
|
|
3168
|
-
function buildSubagentDirective(contextFolder) {
|
|
3214
|
+
function buildSubagentDirective(contextFolder, variant = "full") {
|
|
3169
3215
|
const folder = sanitizeForShellText(contextFolder);
|
|
3216
|
+
if (variant === "graph") {
|
|
3217
|
+
return `IMPORTANT: Use the RepoWise MCP/LSP tools (find_symbol, get_impact, find_callers, lsp_definition) to resolve symbols and blast radius before performing any work.`;
|
|
3218
|
+
}
|
|
3170
3219
|
return `IMPORTANT: Read ${folder}/project-overview.md before performing any work. This file maps every context file to its domain.`;
|
|
3171
3220
|
}
|
|
3172
3221
|
var GATE_SCRIPT_REL = ".claude/repowise-gate.cjs";
|
|
@@ -3175,9 +3224,10 @@ var GATE_MAX_DENIES = 2;
|
|
|
3175
3224
|
function buildGateHookCommand() {
|
|
3176
3225
|
return `node "$CLAUDE_PROJECT_DIR/${GATE_SCRIPT_REL}" ${REPOWISE_MANAGED_COMMENT}`;
|
|
3177
3226
|
}
|
|
3178
|
-
function buildGateScript(contextFolder) {
|
|
3227
|
+
function buildGateScript(contextFolder, variant = "full") {
|
|
3179
3228
|
const folder = contextFolder.replace(/[^A-Za-z0-9_./-]/g, "");
|
|
3180
|
-
|
|
3229
|
+
const denyReason = variant === "graph" ? "RepoWise-first: this codebase has RepoWise. Query its MCP tools (find_symbol/get_impact/lsp_*) to answer this before native search. Retry this search and it will be allowed." : "RepoWise-first: this codebase has RepoWise. Use Tier 1 (RepoWise MCP tools find_symbol/get_impact/lsp_*) or Tier 2 (" + folder + "/ docs) to answer this before native search. Retry this search and it will be allowed.";
|
|
3230
|
+
return GATE_SCRIPT_TEMPLATE.replace(/__CONTEXT_FOLDER__/g, folder).replace(/__MAX_DENIES__/g, String(GATE_MAX_DENIES)).replace(/__DENY_REASON__/g, () => denyReason);
|
|
3181
3231
|
}
|
|
3182
3232
|
var GATE_SCRIPT_TEMPLATE = [
|
|
3183
3233
|
"#!/usr/bin/env node",
|
|
@@ -3216,7 +3266,7 @@ var GATE_SCRIPT_TEMPLATE = [
|
|
|
3216
3266
|
"if (state.consulted) { logMiss(); process.exit(0); }",
|
|
3217
3267
|
"if ((state.denies || 0) >= MAX_DENIES) { process.exit(0); }",
|
|
3218
3268
|
"state.denies = (state.denies || 0) + 1; writeState(state);",
|
|
3219
|
-
'var reason = "
|
|
3269
|
+
'var reason = "__DENY_REASON__";',
|
|
3220
3270
|
'process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason } }));',
|
|
3221
3271
|
"process.exit(0);",
|
|
3222
3272
|
""
|
|
@@ -3325,7 +3375,7 @@ function mergeClaudeHookSettings(existingContent, params) {
|
|
|
3325
3375
|
hooks: [
|
|
3326
3376
|
{
|
|
3327
3377
|
type: "command",
|
|
3328
|
-
command: buildHookEchoCommand("SubagentStart", buildSubagentDirective(params.contextFolder))
|
|
3378
|
+
command: buildHookEchoCommand("SubagentStart", buildSubagentDirective(params.contextFolder, params.variant))
|
|
3329
3379
|
}
|
|
3330
3380
|
]
|
|
3331
3381
|
},
|
|
@@ -3333,7 +3383,7 @@ function mergeClaudeHookSettings(existingContent, params) {
|
|
|
3333
3383
|
hooks: [
|
|
3334
3384
|
{
|
|
3335
3385
|
type: "command",
|
|
3336
|
-
command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "claude"))
|
|
3386
|
+
command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "claude", params.variant))
|
|
3337
3387
|
},
|
|
3338
3388
|
// Re-arm the per-question gate (silent; the echo above carries the reminder).
|
|
3339
3389
|
{ type: "command", command: buildGateHookCommand() }
|
|
@@ -3358,7 +3408,7 @@ function mergeGeminiHookSettings(existingContent, params) {
|
|
|
3358
3408
|
name: "repowise-first",
|
|
3359
3409
|
command: buildPlainJsonEchoCommand({
|
|
3360
3410
|
hookSpecificOutput: {
|
|
3361
|
-
additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
|
|
3411
|
+
additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "generic", params.variant)
|
|
3362
3412
|
}
|
|
3363
3413
|
})
|
|
3364
3414
|
}
|
|
@@ -3375,7 +3425,7 @@ function mergeCodexHookSettings(existingContent, params) {
|
|
|
3375
3425
|
hooks: [
|
|
3376
3426
|
{
|
|
3377
3427
|
type: "command",
|
|
3378
|
-
command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder))
|
|
3428
|
+
command: buildHookEchoCommand("UserPromptSubmit", buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "generic", params.variant))
|
|
3379
3429
|
}
|
|
3380
3430
|
]
|
|
3381
3431
|
}
|
|
@@ -3386,7 +3436,7 @@ function removeCodexHookSettings(existingContent) {
|
|
|
3386
3436
|
}
|
|
3387
3437
|
function buildCopilotHooksFile(params) {
|
|
3388
3438
|
const command = buildPlainJsonEchoCommand({
|
|
3389
|
-
additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
|
|
3439
|
+
additionalContext: buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "generic", params.variant)
|
|
3390
3440
|
});
|
|
3391
3441
|
return JSON.stringify({
|
|
3392
3442
|
version: 1,
|
|
@@ -3397,7 +3447,7 @@ function buildCopilotHooksFile(params) {
|
|
|
3397
3447
|
}
|
|
3398
3448
|
function buildClineHookScript(params) {
|
|
3399
3449
|
const command = buildPlainJsonEchoCommand({
|
|
3400
|
-
contextModification: buildRepoWiseFirstDirective(params.repoName, params.contextFolder)
|
|
3450
|
+
contextModification: buildRepoWiseFirstDirective(params.repoName, params.contextFolder, "generic", params.variant)
|
|
3401
3451
|
});
|
|
3402
3452
|
return [
|
|
3403
3453
|
"#!/bin/sh",
|
|
@@ -3482,7 +3532,7 @@ async function applyGitignoreChanges(repoRoot, changes) {
|
|
|
3482
3532
|
await writeFile(path, next, "utf-8");
|
|
3483
3533
|
}
|
|
3484
3534
|
}
|
|
3485
|
-
async function writeClaudeHooksToRepo(repoRoot, contextFolder) {
|
|
3535
|
+
async function writeClaudeHooksToRepo(repoRoot, contextFolder, variant = "full") {
|
|
3486
3536
|
const repoName = basename(repoRoot);
|
|
3487
3537
|
const projectTracked = await isGitTracked(repoRoot, CLAUDE_PROJECT_SETTINGS);
|
|
3488
3538
|
if (projectTracked) {
|
|
@@ -3506,14 +3556,14 @@ async function writeClaudeHooksToRepo(repoRoot, contextFolder) {
|
|
|
3506
3556
|
existing = await readFile(targetPath, "utf-8");
|
|
3507
3557
|
} catch {
|
|
3508
3558
|
}
|
|
3509
|
-
const merged = mergeClaudeHookSettings(existing, { repoName, contextFolder });
|
|
3559
|
+
const merged = mergeClaudeHookSettings(existing, { repoName, contextFolder, variant });
|
|
3510
3560
|
let status2 = "unchanged";
|
|
3511
3561
|
if (merged !== existing) {
|
|
3512
3562
|
await mkdir(join2(repoRoot, ".claude"), { recursive: true });
|
|
3513
3563
|
await writeFile(targetPath, merged, "utf-8");
|
|
3514
3564
|
status2 = "written";
|
|
3515
3565
|
}
|
|
3516
|
-
const gateScript = buildGateScript(contextFolder);
|
|
3566
|
+
const gateScript = buildGateScript(contextFolder, variant);
|
|
3517
3567
|
const gatePath = join2(repoRoot, GATE_SCRIPT_REL);
|
|
3518
3568
|
let gateExisting = null;
|
|
3519
3569
|
try {
|
|
@@ -3559,8 +3609,8 @@ async function removeClaudeHooksFromRepo(repoRoot) {
|
|
|
3559
3609
|
}
|
|
3560
3610
|
|
|
3561
3611
|
// ../../packages/shared/dist/lib/ai-tools.js
|
|
3562
|
-
var AI_TOOLS_REFERENCE_REV =
|
|
3563
|
-
var AGENT_INSTRUCTIONS_HASH = createHash("sha256").update(AI_AGENT_USAGE_INSTRUCTIONS).digest("hex").slice(0, 12);
|
|
3612
|
+
var AI_TOOLS_REFERENCE_REV = 4;
|
|
3613
|
+
var AGENT_INSTRUCTIONS_HASH = createHash("sha256").update(AI_AGENT_USAGE_INSTRUCTIONS).update(" ").update(AI_AGENT_USAGE_INSTRUCTIONS_GRAPH).digest("hex").slice(0, 12);
|
|
3564
3614
|
var AI_TOOL_CONFIG = {
|
|
3565
3615
|
cursor: {
|
|
3566
3616
|
label: "Cursor",
|
|
@@ -3702,7 +3752,7 @@ function sanitizeRepoName(name) {
|
|
|
3702
3752
|
function fileDescriptionFromName(fileName) {
|
|
3703
3753
|
return fileName.replace(/\.md$/, "").split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3704
3754
|
}
|
|
3705
|
-
function generateReference(tool, repoName, contextFolder, contextFiles) {
|
|
3755
|
+
function generateReference(tool, repoName, contextFolder, contextFiles, variant = "full") {
|
|
3706
3756
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
3707
3757
|
const safeName = sanitizeRepoName(repoName);
|
|
3708
3758
|
const fileLines = contextFiles.map((f) => {
|
|
@@ -3714,19 +3764,21 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
|
|
|
3714
3764
|
const hasFiles = fileLines.length > 0;
|
|
3715
3765
|
const mcpName = repoName.replace(/`/g, "");
|
|
3716
3766
|
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.`;
|
|
3767
|
+
const isGraph = variant === "graph";
|
|
3768
|
+
const introLine = isGraph ? `This repository has a RepoWise knowledge graph (MCP + LSP) available to AI tools.` : `This repository has AI-optimized context files generated by RepoWise.`;
|
|
3717
3769
|
const contentLines = [
|
|
3718
3770
|
`## Project Context \u2014 ${safeName}`,
|
|
3719
3771
|
"",
|
|
3720
|
-
|
|
3721
|
-
"",
|
|
3722
|
-
AI_AGENT_USAGE_INSTRUCTIONS,
|
|
3772
|
+
introLine,
|
|
3723
3773
|
"",
|
|
3724
|
-
|
|
3725
|
-
"",
|
|
3726
|
-
mcpNamingNote,
|
|
3774
|
+
agentInstructionsFor(variant),
|
|
3727
3775
|
""
|
|
3728
3776
|
];
|
|
3729
|
-
if (
|
|
3777
|
+
if (!isGraph) {
|
|
3778
|
+
contentLines.push(`**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that maps every context file to its domain.`, "");
|
|
3779
|
+
}
|
|
3780
|
+
contentLines.push(mcpNamingNote, "");
|
|
3781
|
+
if (!isGraph && hasFiles) {
|
|
3730
3782
|
contentLines.push(`**Context File Routing Map:**`, "", ...fileLines.map((f) => `- \`${f.path}\` \u2014 ${f.desc}`), "", `> Additional context files may exist beyond this list. Check \`project-overview.md\` for the complete index.`);
|
|
3731
3783
|
}
|
|
3732
3784
|
if (config2.owned) {
|
|
@@ -3739,14 +3791,14 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
|
|
|
3739
3791
|
}
|
|
3740
3792
|
return [config2.markerStart, "", ...contentLines, "", config2.markerEnd].join("\n");
|
|
3741
3793
|
}
|
|
3742
|
-
async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
|
|
3794
|
+
async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant = "full") {
|
|
3743
3795
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
3744
3796
|
const fullPath = join3(repoRoot, config2.filePath);
|
|
3745
3797
|
const dir = dirname(fullPath);
|
|
3746
3798
|
if (dir !== repoRoot) {
|
|
3747
3799
|
await mkdir2(dir, { recursive: true });
|
|
3748
3800
|
}
|
|
3749
|
-
const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
|
|
3801
|
+
const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles, variant);
|
|
3750
3802
|
if (config2.owned) {
|
|
3751
3803
|
let created2 = true;
|
|
3752
3804
|
try {
|
|
@@ -3809,7 +3861,7 @@ async function removeToolConfig(repoRoot, tool) {
|
|
|
3809
3861
|
const content = (before + after).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
3810
3862
|
await writeFile2(fullPath, content, "utf-8");
|
|
3811
3863
|
}
|
|
3812
|
-
async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
|
|
3864
|
+
async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant = "full") {
|
|
3813
3865
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
3814
3866
|
if (!config2.legacyFilePath)
|
|
3815
3867
|
return { migrated: false, legacyRemoved: false };
|
|
@@ -3819,11 +3871,11 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
|
|
|
3819
3871
|
const s = await stat(legacyPath);
|
|
3820
3872
|
legacyIsFile = s.isFile();
|
|
3821
3873
|
} catch {
|
|
3822
|
-
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
|
|
3874
|
+
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant);
|
|
3823
3875
|
return { migrated: false, legacyRemoved: false };
|
|
3824
3876
|
}
|
|
3825
3877
|
if (!legacyIsFile) {
|
|
3826
|
-
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
|
|
3878
|
+
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant);
|
|
3827
3879
|
return { migrated: false, legacyRemoved: false };
|
|
3828
3880
|
}
|
|
3829
3881
|
const legacyContent = await readFile2(legacyPath, "utf-8");
|
|
@@ -3853,7 +3905,7 @@ async function migrateToolConfig(repoRoot, tool, repoName, contextFolder, contex
|
|
|
3853
3905
|
} else {
|
|
3854
3906
|
await unlink2(legacyPath);
|
|
3855
3907
|
}
|
|
3856
|
-
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles);
|
|
3908
|
+
await updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant);
|
|
3857
3909
|
return { migrated: true, legacyRemoved: cleaned.length === 0 };
|
|
3858
3910
|
}
|
|
3859
3911
|
async function fileExists(path) {
|
|
@@ -3929,6 +3981,9 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
|
|
|
3929
3981
|
}
|
|
3930
3982
|
}
|
|
3931
3983
|
|
|
3984
|
+
// ../listener/dist/main.js
|
|
3985
|
+
init_src();
|
|
3986
|
+
|
|
3932
3987
|
// ../listener/dist/lib/config.js
|
|
3933
3988
|
init_config_dir();
|
|
3934
3989
|
import { readFile as readFile3, writeFile as writeFile3, rename, unlink as unlink3, mkdir as mkdir3, chmod, open } from "fs/promises";
|
|
@@ -3964,7 +4019,8 @@ async function getListenerConfig() {
|
|
|
3964
4019
|
noAutoUpdate: raw.noAutoUpdate ?? false,
|
|
3965
4020
|
lspEnabled: raw.lspEnabled ?? true,
|
|
3966
4021
|
lspAutoWarm: raw.lspAutoWarm ?? true,
|
|
3967
|
-
...lspOverrides ? { lspOverrides } : {}
|
|
4022
|
+
...lspOverrides ? { lspOverrides } : {},
|
|
4023
|
+
...typeof raw.graphOnly === "boolean" ? { graphOnly: raw.graphOnly } : {}
|
|
3968
4024
|
};
|
|
3969
4025
|
} catch {
|
|
3970
4026
|
return {
|
|
@@ -4004,7 +4060,10 @@ async function saveListenerConfig(config2) {
|
|
|
4004
4060
|
// the listener round-trips them here unchanged.
|
|
4005
4061
|
lspEnabled: config2.lspEnabled,
|
|
4006
4062
|
lspAutoWarm: config2.lspAutoWarm,
|
|
4007
|
-
...config2.lspOverrides ? { lspOverrides: config2.lspOverrides } : {}
|
|
4063
|
+
...config2.lspOverrides ? { lspOverrides: config2.lspOverrides } : {},
|
|
4064
|
+
// Persist the tier signal when set so a listener-driven plan-change save
|
|
4065
|
+
// round-trips it; absent ⇒ `...raw` preserves any CLI-written value.
|
|
4066
|
+
...config2.graphOnly !== void 0 ? { graphOnly: config2.graphOnly } : {}
|
|
4008
4067
|
};
|
|
4009
4068
|
const tmpPath = configPath + ".tmp";
|
|
4010
4069
|
try {
|
|
@@ -8015,7 +8074,7 @@ var claudeCodeHooksWriter = {
|
|
|
8015
8074
|
return await fileExists3(join24(repoRoot, "CLAUDE.md")) || await fileExists3(join24(home, ".claude.json"));
|
|
8016
8075
|
},
|
|
8017
8076
|
async write(ctx) {
|
|
8018
|
-
const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context");
|
|
8077
|
+
const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context", ctx.variant);
|
|
8019
8078
|
return { status: result.status, path: join24(ctx.repoRoot, result.relPath) };
|
|
8020
8079
|
},
|
|
8021
8080
|
async remove(ctx) {
|
|
@@ -8085,7 +8144,8 @@ var clineHooksWriter = {
|
|
|
8085
8144
|
const path = join26(ctx.repoRoot, SCRIPT_REL);
|
|
8086
8145
|
const next = buildClineHookScript({
|
|
8087
8146
|
repoName: ctx.repoName,
|
|
8088
|
-
contextFolder: ctx.contextFolder ?? "repowise-context"
|
|
8147
|
+
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
8148
|
+
variant: ctx.variant
|
|
8089
8149
|
});
|
|
8090
8150
|
const existing = await readOrNull(path);
|
|
8091
8151
|
if (existing !== null && !existing.includes(OWNERSHIP_MARKER)) {
|
|
@@ -8197,7 +8257,8 @@ var codexHooksWriter = {
|
|
|
8197
8257
|
const existing = await readOrNull2(path);
|
|
8198
8258
|
const merged = mergeCodexHookSettings(existing, {
|
|
8199
8259
|
repoName: ctx.repoName,
|
|
8200
|
-
contextFolder: ctx.contextFolder ?? "repowise-context"
|
|
8260
|
+
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
8261
|
+
variant: ctx.variant
|
|
8201
8262
|
});
|
|
8202
8263
|
if (merged === existing) {
|
|
8203
8264
|
return { status: "unchanged", path };
|
|
@@ -8283,7 +8344,8 @@ var copilotHooksWriter = {
|
|
|
8283
8344
|
const path = join30(ctx.repoRoot, HOOKS_REL2);
|
|
8284
8345
|
const next = buildCopilotHooksFile({
|
|
8285
8346
|
repoName: ctx.repoName,
|
|
8286
|
-
contextFolder: ctx.contextFolder ?? "repowise-context"
|
|
8347
|
+
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
8348
|
+
variant: ctx.variant
|
|
8287
8349
|
});
|
|
8288
8350
|
const existing = await readOrNull3(path);
|
|
8289
8351
|
if (await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
|
|
@@ -8422,7 +8484,8 @@ var geminiHooksWriter = {
|
|
|
8422
8484
|
const existing = await readOrNull4(path);
|
|
8423
8485
|
const merged = mergeGeminiHookSettings(existing, {
|
|
8424
8486
|
repoName: ctx.repoName,
|
|
8425
|
-
contextFolder: ctx.contextFolder ?? "repowise-context"
|
|
8487
|
+
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
8488
|
+
variant: ctx.variant
|
|
8426
8489
|
});
|
|
8427
8490
|
if (merged === existing) {
|
|
8428
8491
|
return { status: "unchanged", path };
|
|
@@ -8587,7 +8650,8 @@ async function runAutoConfig(opts) {
|
|
|
8587
8650
|
shimCmd: opts.shimCmd,
|
|
8588
8651
|
home,
|
|
8589
8652
|
contextFolder: opts.contextFolder,
|
|
8590
|
-
selectedTools: opts.selectedTools
|
|
8653
|
+
selectedTools: opts.selectedTools,
|
|
8654
|
+
variant: opts.variant
|
|
8591
8655
|
});
|
|
8592
8656
|
results.push({ tool: writer.tool, detected: true, outcome });
|
|
8593
8657
|
} catch (err) {
|
|
@@ -10233,7 +10297,8 @@ async function readRawToolConfig() {
|
|
|
10233
10297
|
const raw = JSON.parse(data);
|
|
10234
10298
|
return {
|
|
10235
10299
|
aiTools: Array.isArray(raw["aiTools"]) ? raw["aiTools"] : void 0,
|
|
10236
|
-
contextFolder: typeof raw["contextFolder"] === "string" ? raw["contextFolder"] : void 0
|
|
10300
|
+
contextFolder: typeof raw["contextFolder"] === "string" ? raw["contextFolder"] : void 0,
|
|
10301
|
+
graphOnly: typeof raw["graphOnly"] === "boolean" ? raw["graphOnly"] : void 0
|
|
10237
10302
|
};
|
|
10238
10303
|
} catch {
|
|
10239
10304
|
return {};
|
|
@@ -10254,12 +10319,14 @@ async function updateToolConfigsForRepo(localPath, config2, state, repoId) {
|
|
|
10254
10319
|
}
|
|
10255
10320
|
await migrateRooToKilo(localPath).catch(() => {
|
|
10256
10321
|
});
|
|
10322
|
+
const variant = config2.graphOnly ? "graph" : "full";
|
|
10257
10323
|
const contextFiles = await scanLocalContextFiles(localPath, contextFolder);
|
|
10258
|
-
if (contextFiles.length === 0)
|
|
10324
|
+
if (contextFiles.length === 0 && variant === "full")
|
|
10259
10325
|
return;
|
|
10260
10326
|
const hash = JSON.stringify({
|
|
10261
10327
|
rev: AI_TOOLS_REFERENCE_REV,
|
|
10262
10328
|
block: AGENT_INSTRUCTIONS_HASH,
|
|
10329
|
+
variant,
|
|
10263
10330
|
tools,
|
|
10264
10331
|
files: contextFiles.map((f) => f.fileName)
|
|
10265
10332
|
});
|
|
@@ -10274,13 +10341,13 @@ async function updateToolConfigsForRepo(localPath, config2, state, repoId) {
|
|
|
10274
10341
|
continue;
|
|
10275
10342
|
written.add(toolConfig.filePath);
|
|
10276
10343
|
if (toolConfig.legacyFilePath) {
|
|
10277
|
-
await migrateToolConfig(localPath, tool, repoName, contextFolder, contextFiles);
|
|
10344
|
+
await migrateToolConfig(localPath, tool, repoName, contextFolder, contextFiles, variant);
|
|
10278
10345
|
} else {
|
|
10279
|
-
await updateToolConfig(localPath, tool, repoName, contextFolder, contextFiles);
|
|
10346
|
+
await updateToolConfig(localPath, tool, repoName, contextFolder, contextFiles, variant);
|
|
10280
10347
|
}
|
|
10281
10348
|
}
|
|
10282
10349
|
if (!written.has("AGENTS.md")) {
|
|
10283
|
-
await updateToolConfig(localPath, "codex", repoName, contextFolder, contextFiles);
|
|
10350
|
+
await updateToolConfig(localPath, "codex", repoName, contextFolder, contextFiles, variant);
|
|
10284
10351
|
}
|
|
10285
10352
|
if (!state.repos[repoId]) {
|
|
10286
10353
|
state.repos[repoId] = { lastSyncTimestamp: "", lastSyncCommitSha: null };
|
|
@@ -10599,7 +10666,8 @@ async function checkStaleContext(repos, state, groups) {
|
|
|
10599
10666
|
}
|
|
10600
10667
|
async function reconcileMcpConfigs(repos, packageName) {
|
|
10601
10668
|
const shimCmd = packageName;
|
|
10602
|
-
const { contextFolder, aiTools } = await readRawToolConfig();
|
|
10669
|
+
const { contextFolder, aiTools, graphOnly } = await readRawToolConfig();
|
|
10670
|
+
const variant = graphOnly ? "graph" : "full";
|
|
10603
10671
|
for (const repo of repos) {
|
|
10604
10672
|
if (!repo.localPath)
|
|
10605
10673
|
continue;
|
|
@@ -10616,7 +10684,8 @@ async function reconcileMcpConfigs(repos, packageName) {
|
|
|
10616
10684
|
repoId: repo.repoId,
|
|
10617
10685
|
shimCmd,
|
|
10618
10686
|
contextFolder,
|
|
10619
|
-
selectedTools: aiTools
|
|
10687
|
+
selectedTools: aiTools,
|
|
10688
|
+
variant
|
|
10620
10689
|
});
|
|
10621
10690
|
const written = results.filter((r) => r.detected && r.outcome?.status === "written");
|
|
10622
10691
|
if (written.length > 0) {
|
|
@@ -10628,6 +10697,8 @@ async function reconcileMcpConfigs(repos, packageName) {
|
|
|
10628
10697
|
}
|
|
10629
10698
|
}
|
|
10630
10699
|
async function reconcileAgentInstructions(repos) {
|
|
10700
|
+
const { graphOnly } = await readRawToolConfig();
|
|
10701
|
+
const variant = graphOnly ? "graph" : "full";
|
|
10631
10702
|
for (const repo of repos) {
|
|
10632
10703
|
const path = join40(repo.localPath, "repowise-context", "project-overview.md");
|
|
10633
10704
|
let content;
|
|
@@ -10641,7 +10712,7 @@ async function reconcileAgentInstructions(repos) {
|
|
|
10641
10712
|
console.warn(`[reconcile] read failed for ${repo.repoId}:`, err instanceof Error ? err.message : String(err));
|
|
10642
10713
|
continue;
|
|
10643
10714
|
}
|
|
10644
|
-
const injected = injectAgentInstructions(content);
|
|
10715
|
+
const injected = injectAgentInstructions(content, variant);
|
|
10645
10716
|
if (injected === content)
|
|
10646
10717
|
continue;
|
|
10647
10718
|
try {
|
|
@@ -11075,6 +11146,22 @@ async function startListener() {
|
|
|
11075
11146
|
if (response.latestCliVersion && /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?(?:\+[A-Za-z0-9.-]+)?$/.test(response.latestCliVersion)) {
|
|
11076
11147
|
latestCliVersion = response.latestCliVersion;
|
|
11077
11148
|
}
|
|
11149
|
+
if (typeof response.plan === "string") {
|
|
11150
|
+
const planGraphOnly = agentInstructionsVariantForPlan(response.plan) === "graph";
|
|
11151
|
+
const currentGraphOnly = config2.graphOnly ?? false;
|
|
11152
|
+
if (currentGraphOnly !== planGraphOnly) {
|
|
11153
|
+
console.log(`[tier] Plan change detected (plan=${response.plan}); graphOnly ${String(currentGraphOnly)} \u2192 ${String(planGraphOnly)} \u2014 re-reconciling`);
|
|
11154
|
+
try {
|
|
11155
|
+
await saveListenerConfig({ ...config2, graphOnly: planGraphOnly });
|
|
11156
|
+
config2.graphOnly = planGraphOnly;
|
|
11157
|
+
await reconcileToolConfigs(config2.repos, state);
|
|
11158
|
+
await reconcileAgentInstructions(config2.repos);
|
|
11159
|
+
await reconcileMcpConfigs(config2.repos, packageName);
|
|
11160
|
+
} catch (err) {
|
|
11161
|
+
console.warn("[tier] Plan-change reconcile failed:", err instanceof Error ? err.message : String(err));
|
|
11162
|
+
}
|
|
11163
|
+
}
|
|
11164
|
+
}
|
|
11078
11165
|
if (shouldReconcile) {
|
|
11079
11166
|
try {
|
|
11080
11167
|
const freshConfig = await getListenerConfig();
|
|
@@ -13117,7 +13204,7 @@ async function create() {
|
|
|
13117
13204
|
let graphOnly = false;
|
|
13118
13205
|
try {
|
|
13119
13206
|
const usage = await apiRequest("/v1/account/usage");
|
|
13120
|
-
graphOnly = usage.plan
|
|
13207
|
+
graphOnly = agentInstructionsVariantForPlan(usage.plan ?? "free") === "graph";
|
|
13121
13208
|
} catch {
|
|
13122
13209
|
}
|
|
13123
13210
|
let pollAttempts = 0;
|
|
@@ -13369,19 +13456,28 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13369
13456
|
spinner.start("Configuring AI tools...");
|
|
13370
13457
|
const results = [];
|
|
13371
13458
|
const written = /* @__PURE__ */ new Set();
|
|
13459
|
+
const toolVariant = graphOnly ? "graph" : "full";
|
|
13372
13460
|
for (const tool of tools) {
|
|
13373
13461
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
13374
13462
|
if (written.has(config2.filePath)) continue;
|
|
13375
13463
|
written.add(config2.filePath);
|
|
13376
13464
|
if (config2.legacyFilePath) {
|
|
13377
|
-
await migrateToolConfig(
|
|
13465
|
+
await migrateToolConfig(
|
|
13466
|
+
repoRoot,
|
|
13467
|
+
tool,
|
|
13468
|
+
repoName,
|
|
13469
|
+
contextFolder,
|
|
13470
|
+
contextFiles,
|
|
13471
|
+
toolVariant
|
|
13472
|
+
);
|
|
13378
13473
|
}
|
|
13379
13474
|
const { created: wasCreated } = await updateToolConfig(
|
|
13380
13475
|
repoRoot,
|
|
13381
13476
|
tool,
|
|
13382
13477
|
repoName,
|
|
13383
13478
|
contextFolder,
|
|
13384
|
-
contextFiles
|
|
13479
|
+
contextFiles,
|
|
13480
|
+
toolVariant
|
|
13385
13481
|
);
|
|
13386
13482
|
const action = wasCreated ? "Created" : "Updated";
|
|
13387
13483
|
results.push(` ${action} ${config2.filePath}`);
|
|
@@ -13392,12 +13488,13 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13392
13488
|
"codex",
|
|
13393
13489
|
repoName,
|
|
13394
13490
|
contextFolder,
|
|
13395
|
-
contextFiles
|
|
13491
|
+
contextFiles,
|
|
13492
|
+
toolVariant
|
|
13396
13493
|
);
|
|
13397
13494
|
results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
|
|
13398
13495
|
}
|
|
13399
13496
|
if (tools.includes("claude-code")) {
|
|
13400
|
-
const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder);
|
|
13497
|
+
const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder, toolVariant);
|
|
13401
13498
|
results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
|
|
13402
13499
|
}
|
|
13403
13500
|
spinner.succeed("AI tools configured");
|
|
@@ -13416,7 +13513,7 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13416
13513
|
})
|
|
13417
13514
|
);
|
|
13418
13515
|
}
|
|
13419
|
-
const configUpdate = { aiTools: tools, contextFolder };
|
|
13516
|
+
const configUpdate = { aiTools: tools, contextFolder, graphOnly };
|
|
13420
13517
|
if (updatedRepos.length > 0) configUpdate.repos = updatedRepos;
|
|
13421
13518
|
await mergeAndSaveConfig(configUpdate);
|
|
13422
13519
|
let listenerRunning = false;
|
|
@@ -14230,7 +14327,8 @@ async function sync() {
|
|
|
14230
14327
|
const aiTools = (existingConfig.aiTools ?? []).map((t) => t === "roo-code" ? "kilo" : t).filter((t, i, arr) => arr.indexOf(t) === i);
|
|
14231
14328
|
if (aiTools.length > 0) {
|
|
14232
14329
|
const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
14233
|
-
|
|
14330
|
+
const toolVariant = existingConfig.graphOnly ? "graph" : "full";
|
|
14331
|
+
if (toolVariant === "graph" || contextFiles.length > 0) {
|
|
14234
14332
|
const written = /* @__PURE__ */ new Set();
|
|
14235
14333
|
for (const tool of aiTools) {
|
|
14236
14334
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
@@ -14241,7 +14339,8 @@ async function sync() {
|
|
|
14241
14339
|
tool,
|
|
14242
14340
|
repoName,
|
|
14243
14341
|
DEFAULT_CONTEXT_FOLDER3,
|
|
14244
|
-
contextFiles
|
|
14342
|
+
contextFiles,
|
|
14343
|
+
toolVariant
|
|
14245
14344
|
);
|
|
14246
14345
|
}
|
|
14247
14346
|
if (!written.has("AGENTS.md")) {
|
|
@@ -14250,7 +14349,8 @@ async function sync() {
|
|
|
14250
14349
|
"codex",
|
|
14251
14350
|
repoName,
|
|
14252
14351
|
DEFAULT_CONTEXT_FOLDER3,
|
|
14253
|
-
contextFiles
|
|
14352
|
+
contextFiles,
|
|
14353
|
+
toolVariant
|
|
14254
14354
|
);
|
|
14255
14355
|
}
|
|
14256
14356
|
}
|