@dennisrepa/elevate 1.3.0 → 1.4.0

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/cli.js CHANGED
@@ -4,74 +4,25 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/config.ts
7
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
8
- import { dirname as dirname8, join as join16, resolve as resolve10 } from "path";
7
+ import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
8
+ import { dirname as dirname8, join as join18, resolve as resolve10 } from "path";
9
9
 
10
- // src/adapters/npm/npm-discovery.ts
11
- import { readFileSync } from "fs";
12
- import { dirname, join, relative, resolve } from "path";
13
- import mapWorkspaces from "@npmcli/map-workspaces";
14
- function readJson(filePath) {
15
- try {
16
- return JSON.parse(readFileSync(filePath, "utf8"));
17
- } catch {
18
- return null;
10
+ // src/adapters/npm/npm-package-manager.ts
11
+ import { existsSync as existsSync2, readFileSync } from "fs";
12
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
13
+
14
+ // src/adapters/shared/checkout.ts
15
+ import { existsSync } from "fs";
16
+ import { dirname, join, resolve } from "path";
17
+ function findCheckoutBoundary(startDir) {
18
+ let dir = resolve(startDir);
19
+ for (; ; ) {
20
+ if (existsSync(join(dir, ".git"))) return dir;
21
+ const parent = dirname(dir);
22
+ if (parent === dir) return void 0;
23
+ dir = parent;
19
24
  }
20
25
  }
21
- var NpmModuleDiscoveryAdapter = class {
22
- /**
23
- * The nearest directory, from `startDir` upwards to the boundary, whose
24
- * package.json is valid JSON and declares `workspaces`.
25
- */
26
- findRoot(startDir, boundaryDir) {
27
- let dir = resolve(startDir);
28
- const boundary = boundaryDir ? resolve(boundaryDir) : void 0;
29
- for (; ; ) {
30
- if (readJson(join(dir, "package.json"))?.workspaces) return dir;
31
- const parent = dirname(dir);
32
- if (dir === boundary || parent === dir) return void 0;
33
- dir = parent;
34
- }
35
- }
36
- async discover(rootDir) {
37
- const rootPkg = readJson(join(rootDir, "package.json"));
38
- const modules = [
39
- {
40
- id: rootPkg?.name ?? "root",
41
- name: rootPkg?.name ?? "Root Workspace (npm)",
42
- path: rootDir,
43
- relPath: "Root",
44
- ecosystem: "npm",
45
- isRoot: true,
46
- version: typeof rootPkg?.version === "string" ? rootPkg.version : void 0
47
- }
48
- ];
49
- if (!rootPkg?.workspaces) return modules;
50
- let workspaces;
51
- try {
52
- workspaces = await mapWorkspaces({ cwd: rootDir, pkg: rootPkg });
53
- } catch {
54
- return modules;
55
- }
56
- for (const [name, path] of workspaces) {
57
- const pkg = readJson(join(path, "package.json"));
58
- modules.push({
59
- id: name,
60
- name,
61
- path,
62
- relPath: relative(rootDir, path).replace(/\\/g, "/"),
63
- ecosystem: "npm",
64
- isRoot: false,
65
- version: typeof pkg?.version === "string" ? pkg.version : void 0
66
- });
67
- }
68
- modules.sort((a, b) => a.isRoot ? -1 : b.isRoot ? 1 : a.relPath.localeCompare(b.relPath));
69
- return modules;
70
- }
71
- };
72
-
73
- // src/adapters/npm/npm-registry.ts
74
- import semver from "semver";
75
26
 
76
27
  // src/adapters/shared/process.ts
77
28
  import { exec, spawn } from "child_process";
@@ -148,6 +99,213 @@ function quoteWindows(arg) {
148
99
  return /^[\w.:\\/@+-]+$/.test(arg) ? arg : `"${arg}"`;
149
100
  }
150
101
 
102
+ // src/adapters/npm/npm-package-manager.ts
103
+ var MARKER_FILES = [
104
+ ["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
105
+ ["pnpm", ["pnpm-lock.yaml", "pnpm-workspace.yaml"]],
106
+ ["yarn", ["yarn.lock", ".yarnrc.yml"]],
107
+ ["bun", ["bun.lock", "bun.lockb"]]
108
+ ];
109
+ var UnsupportedPackageManagerError = class extends Error {
110
+ constructor(detected) {
111
+ super(
112
+ `This repository is managed by ${detected.name} (${detected.evidence}). Elevate supports npm and pnpm for Node.js projects; using either here would create a lockfile next to ${detected.name}'s. Nothing was changed.`
113
+ );
114
+ this.name = "UnsupportedPackageManagerError";
115
+ }
116
+ };
117
+ function detectPackageManager(dir) {
118
+ const boundary = findCheckoutBoundary(dir);
119
+ let current = resolve2(dir);
120
+ for (; ; ) {
121
+ const found = detectIn(current);
122
+ if (found) return found;
123
+ const parent = dirname2(current);
124
+ if (boundary === void 0 || current === boundary || parent === current) break;
125
+ current = parent;
126
+ }
127
+ return { name: "npm", evidence: "no other package manager detected" };
128
+ }
129
+ function detectIn(dir) {
130
+ const declared = readPackageManagerField(dir);
131
+ const name = declared?.match(/^(npm|pnpm|yarn|bun)@/)?.[1];
132
+ if (declared && name) return { name, evidence: `"packageManager": "${declared}" in package.json` };
133
+ for (const [manager, files] of MARKER_FILES) {
134
+ const found = files.find((file) => existsSync2(join2(dir, file)));
135
+ if (found) return { name: manager, evidence: found };
136
+ }
137
+ return void 0;
138
+ }
139
+ function assertSupportedPackageManager(rootDir) {
140
+ const detected = detectPackageManager(rootDir);
141
+ if (detected.name === "npm" || detected.name === "pnpm") return detected.name;
142
+ throw new UnsupportedPackageManagerError(detected);
143
+ }
144
+ var PackageManagerNotFoundError = class extends Error {
145
+ constructor(reason, detected) {
146
+ super(
147
+ `pnpm could not be run in this repository (${reason}). This repository is managed by pnpm (${detected.evidence}); install pnpm or enable Corepack (corepack enable). Nothing was changed.`
148
+ );
149
+ this.name = "PackageManagerNotFoundError";
150
+ }
151
+ };
152
+ var VERSION_TIMEOUT_MS = 15e3;
153
+ var verifiedRoots = /* @__PURE__ */ new Set();
154
+ async function ensurePnpmRunnable(rootDir, detected) {
155
+ const key = resolve2(rootDir);
156
+ if (verifiedRoots.has(key)) return;
157
+ let reason;
158
+ try {
159
+ const result = await runCommand("pnpm", ["--version"], { cwd: rootDir, timeoutMs: VERSION_TIMEOUT_MS });
160
+ if (result.timedOut) reason = "pnpm --version timed out";
161
+ else if (result.exitCode !== 0) {
162
+ const line = firstLine(result.stderr) || firstLine(result.stdout);
163
+ reason = `pnpm --version exited with code ${result.exitCode}: ${line}`;
164
+ }
165
+ } catch {
166
+ reason = "command not found";
167
+ }
168
+ if (reason !== void 0) throw new PackageManagerNotFoundError(reason, detected);
169
+ verifiedRoots.add(key);
170
+ }
171
+ async function prepareSupportedPackageManager(rootDir) {
172
+ const manager = assertSupportedPackageManager(rootDir);
173
+ if (manager === "pnpm") await ensurePnpmRunnable(rootDir, detectPackageManager(rootDir));
174
+ return manager;
175
+ }
176
+ function firstLine(text) {
177
+ return text.split(/\r?\n/).find((l) => l.trim() !== "")?.trim() ?? "";
178
+ }
179
+ function readPackageManagerField(rootDir) {
180
+ try {
181
+ const field = JSON.parse(readFileSync(join2(rootDir, "package.json"), "utf8"))?.packageManager;
182
+ return typeof field === "string" ? field : void 0;
183
+ } catch {
184
+ return void 0;
185
+ }
186
+ }
187
+
188
+ // src/adapters/npm/npm-discovery.ts
189
+ import { readFileSync as readFileSync3 } from "fs";
190
+ import { dirname as dirname3, join as join4, relative, resolve as resolve3 } from "path";
191
+ import mapWorkspaces from "@npmcli/map-workspaces";
192
+
193
+ // src/adapters/npm/pnpm-workspace.ts
194
+ import { readFileSync as readFileSync2 } from "fs";
195
+ import { join as join3 } from "path";
196
+ import { isMap, isScalar, parse, parseDocument } from "yaml";
197
+ var PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
198
+ function isRecord(value) {
199
+ return typeof value === "object" && value !== null && !Array.isArray(value);
200
+ }
201
+ function entries(value) {
202
+ const map = /* @__PURE__ */ new Map();
203
+ if (isRecord(value)) {
204
+ for (const [name, range] of Object.entries(value)) {
205
+ if (typeof range === "string") map.set(name, range);
206
+ }
207
+ }
208
+ return map;
209
+ }
210
+ function readPnpmWorkspace(rootDir) {
211
+ const file = join3(rootDir, PNPM_WORKSPACE_FILE);
212
+ let data;
213
+ try {
214
+ data = parse(readFileSync2(file, "utf8"));
215
+ } catch {
216
+ return void 0;
217
+ }
218
+ if (data !== null && !isRecord(data)) return void 0;
219
+ const doc = data ?? {};
220
+ const packages = Array.isArray(doc.packages) ? doc.packages.filter((p) => typeof p === "string") : [];
221
+ const catalogs = /* @__PURE__ */ new Map();
222
+ const named = isRecord(doc.catalogs) ? doc.catalogs : {};
223
+ for (const [name, content] of Object.entries(named)) catalogs.set(name, entries(content));
224
+ const hasTopLevel = isRecord(doc.catalog);
225
+ const hasNamedDefault = isRecord(named.default);
226
+ if (hasTopLevel) catalogs.set("default", entries(doc.catalog));
227
+ return { file, packages, catalogs, ambiguousDefaultCatalog: hasTopLevel && hasNamedDefault };
228
+ }
229
+ function setCatalogRange(source, catalog, name, newRange) {
230
+ const doc = parseDocument(source);
231
+ const holders = catalog === "default" ? [doc.get("catalog", true), doc.getIn(["catalogs", "default"], true)] : [doc.getIn(["catalogs", catalog], true)];
232
+ for (const holder of holders) {
233
+ if (!isMap(holder)) continue;
234
+ const node = holder.get(name, true);
235
+ if (!isScalar(node) || !node.range) continue;
236
+ const scalar = node;
237
+ const [start, end] = scalar.range;
238
+ const quote = scalar.type === "QUOTE_DOUBLE" ? '"' : scalar.type === "QUOTE_SINGLE" ? "'" : "";
239
+ return source.slice(0, start) + quote + newRange + quote + source.slice(end);
240
+ }
241
+ throw new Error(`No entry for '${name}' in catalog '${catalog}' of ${PNPM_WORKSPACE_FILE}.`);
242
+ }
243
+
244
+ // src/adapters/npm/npm-discovery.ts
245
+ function readJson(filePath) {
246
+ try {
247
+ return JSON.parse(readFileSync3(filePath, "utf8"));
248
+ } catch {
249
+ return null;
250
+ }
251
+ }
252
+ var NpmModuleDiscoveryAdapter = class {
253
+ /**
254
+ * The nearest directory, from `startDir` upwards to the boundary, whose
255
+ * package.json is valid JSON and declares `workspaces`, or that contains a
256
+ * valid `pnpm-workspace.yaml`.
257
+ */
258
+ findRoot(startDir, boundaryDir) {
259
+ let dir = resolve3(startDir);
260
+ const boundary = boundaryDir ? resolve3(boundaryDir) : void 0;
261
+ for (; ; ) {
262
+ if (readJson(join4(dir, "package.json"))?.workspaces || readPnpmWorkspace(dir)) return dir;
263
+ const parent = dirname3(dir);
264
+ if (dir === boundary || parent === dir) return void 0;
265
+ dir = parent;
266
+ }
267
+ }
268
+ async discover(rootDir) {
269
+ const rootPkg = readJson(join4(rootDir, "package.json"));
270
+ const modules = [
271
+ {
272
+ id: rootPkg?.name ?? "root",
273
+ name: rootPkg?.name ?? "Root Workspace (npm)",
274
+ path: rootDir,
275
+ relPath: "Root",
276
+ ecosystem: "npm",
277
+ isRoot: true,
278
+ version: typeof rootPkg?.version === "string" ? rootPkg.version : void 0
279
+ }
280
+ ];
281
+ const patterns = detectPackageManager(rootDir).name === "pnpm" ? readPnpmWorkspace(rootDir)?.packages : rootPkg?.workspaces;
282
+ if (!patterns || Array.isArray(patterns) && patterns.length === 0) return modules;
283
+ let workspaces;
284
+ try {
285
+ workspaces = await mapWorkspaces({ cwd: rootDir, pkg: { ...rootPkg, workspaces: patterns } });
286
+ } catch {
287
+ return modules;
288
+ }
289
+ for (const [name, path] of workspaces) {
290
+ const pkg = readJson(join4(path, "package.json"));
291
+ modules.push({
292
+ id: name,
293
+ name,
294
+ path,
295
+ relPath: relative(rootDir, path).replace(/\\/g, "/"),
296
+ ecosystem: "npm",
297
+ isRoot: false,
298
+ version: typeof pkg?.version === "string" ? pkg.version : void 0
299
+ });
300
+ }
301
+ modules.sort((a, b) => a.isRoot ? -1 : b.isRoot ? 1 : a.relPath.localeCompare(b.relPath));
302
+ return modules;
303
+ }
304
+ };
305
+
306
+ // src/adapters/npm/npm-registry.ts
307
+ import semver from "semver";
308
+
151
309
  // src/domain/origin.ts
152
310
  function buildWorkspaceIndex(modules) {
153
311
  const index = /* @__PURE__ */ new Map();
@@ -194,9 +352,24 @@ function npmScopeOf(name) {
194
352
  return slash > 1 ? name.slice(0, slash) : void 0;
195
353
  }
196
354
 
355
+ // src/adapters/npm/npm-commands.ts
356
+ function driverFor(dir) {
357
+ return detectPackageManager(dir).name === "pnpm" ? "pnpm" : "npm";
358
+ }
359
+ var commands = {
360
+ /** Install after Elevate wrote new ranges. pnpm is told explicitly not to freeze: `CI` would otherwise imply it. */
361
+ install: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--no-frozen-lockfile"] } : { command: "npm", args: ["install"] },
362
+ /** Restore the installed state after a rollback. */
363
+ restore: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--frozen-lockfile"] } : { command: "npm", args: ["install"] },
364
+ /** Default verification. */
365
+ verify: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--frozen-lockfile", "--lockfile-only"] } : { command: "npm", args: ["ls"] },
366
+ view: (m, name, field) => ({ command: m, args: ["view", name, field, "--json"] }),
367
+ config: (m) => ({ command: m, args: ["config", "list", "--json"] })
368
+ };
369
+
197
370
  // src/adapters/npm/npm-config.ts
198
371
  var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
199
- var PUBLIC_NPM_HOSTS = /* @__PURE__ */ new Set(["registry.npmjs.org", "registry.npmjs.com", "registry.yarnpkg.com"]);
372
+ var PUBLIC_NPM_HOSTS = /* @__PURE__ */ new Set(["registry.npmjs.org", "registry.npmjs.com", "registry.yarnpkg.com", "npm.jsr.io"]);
200
373
  var NpmConfigReader = class {
201
374
  cache = /* @__PURE__ */ new Map();
202
375
  /** Returns the registry configuration that applies in `cwd` (cached per directory). */
@@ -233,7 +406,8 @@ function parseNpmConfig(json) {
233
406
  }
234
407
  async function loadConfig(cwd) {
235
408
  try {
236
- const result = await runCommand("npm", ["config", "list", "--json"], { cwd, timeoutMs: 15e3 });
409
+ const { command, args } = commands.config(driverFor(cwd));
410
+ const result = await runCommand(command, args, { cwd, timeoutMs: 15e3 });
237
411
  if (result.exitCode !== 0) return parseNpmConfig({});
238
412
  return parseNpmConfig(JSON.parse(result.stdout));
239
413
  } catch {
@@ -277,10 +451,11 @@ var NpmRegistryAdapter = class {
277
451
  const list = Array.isArray(versions) ? versions : versions ? [versions] : [];
278
452
  return list.map(String).reverse();
279
453
  }
280
- /** Runs `npm view <name> <field> --json`; resolves null on any failure. */
454
+ /** Runs `npm view <name> <field> --json` (`pnpm view` in a pnpm repository); resolves null on any failure. */
281
455
  async view(name, field, cwd) {
282
456
  try {
283
- const result = await runCommand("npm", ["view", name, field, "--json"], {
457
+ const { command, args } = commands.view(driverFor(cwd), name, field);
458
+ const result = await runCommand(command, args, {
284
459
  cwd,
285
460
  timeoutMs: LOOKUP_TIMEOUT_MS
286
461
  });
@@ -293,8 +468,8 @@ var NpmRegistryAdapter = class {
293
468
  };
294
469
 
295
470
  // src/adapters/npm/npm-scanner.ts
296
- import { readFileSync as readFileSync3 } from "fs";
297
- import { join as join4 } from "path";
471
+ import { readFileSync as readFileSync4 } from "fs";
472
+ import { join as join5 } from "path";
298
473
  import semver3 from "semver";
299
474
 
300
475
  // src/domain/versions.ts
@@ -347,71 +522,11 @@ async function mapPooled(items, concurrency, fn) {
347
522
  return results;
348
523
  }
349
524
 
350
- // src/adapters/npm/npm-package-manager.ts
351
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
352
- import { dirname as dirname3, join as join3, resolve as resolve3 } from "path";
353
-
354
- // src/adapters/shared/checkout.ts
355
- import { existsSync } from "fs";
356
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
357
- function findCheckoutBoundary(startDir) {
358
- let dir = resolve2(startDir);
359
- for (; ; ) {
360
- if (existsSync(join2(dir, ".git"))) return dir;
361
- const parent = dirname2(dir);
362
- if (parent === dir) return void 0;
363
- dir = parent;
364
- }
365
- }
366
-
367
- // src/adapters/npm/npm-package-manager.ts
368
- var MARKER_FILES = [
369
- ["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
370
- ["pnpm", ["pnpm-lock.yaml", "pnpm-workspace.yaml"]],
371
- ["yarn", ["yarn.lock", ".yarnrc.yml"]],
372
- ["bun", ["bun.lock", "bun.lockb"]]
373
- ];
374
- var UnsupportedPackageManagerError = class extends Error {
375
- constructor(detected) {
376
- super(
377
- `This repository is managed by ${detected.name} (${detected.evidence}). Elevate supports npm for Node.js projects; using it here would create an npm lockfile next to ${detected.name}'s. Nothing was changed.`
378
- );
379
- this.name = "UnsupportedPackageManagerError";
380
- }
381
- };
382
- function detectPackageManager(dir) {
383
- const boundary = findCheckoutBoundary(dir);
384
- let current = resolve3(dir);
385
- for (; ; ) {
386
- const found = detectIn(current);
387
- if (found) return found;
388
- const parent = dirname3(current);
389
- if (boundary === void 0 || current === boundary || parent === current) break;
390
- current = parent;
391
- }
392
- return { name: "npm", evidence: "no other package manager detected" };
393
- }
394
- function detectIn(dir) {
395
- const declared = readPackageManagerField(dir);
396
- const name = declared?.match(/^(npm|pnpm|yarn|bun)@/)?.[1];
397
- if (declared && name) return { name, evidence: `"packageManager": "${declared}" in package.json` };
398
- for (const [manager, files] of MARKER_FILES) {
399
- const found = files.find((file) => existsSync2(join3(dir, file)));
400
- if (found) return { name: manager, evidence: found };
401
- }
402
- return void 0;
403
- }
404
- function assertNpmManaged(rootDir) {
405
- const detected = detectPackageManager(rootDir);
406
- if (detected.name !== "npm") throw new UnsupportedPackageManagerError(detected);
407
- }
408
- function readPackageManagerField(rootDir) {
409
- try {
410
- const field = JSON.parse(readFileSync2(join3(rootDir, "package.json"), "utf8"))?.packageManager;
411
- return typeof field === "string" ? field : void 0;
412
- } catch {
413
- return void 0;
414
- }
525
+ // src/adapters/npm/npm-protocols.ts
526
+ function splitRangeProtocol(range) {
527
+ if (range.startsWith("workspace:")) return { protocol: "workspace", rest: range.slice("workspace:".length) };
528
+ if (range.startsWith("catalog:")) return { protocol: "catalog", rest: range.slice("catalog:".length) };
529
+ return { protocol: void 0, rest: range };
415
530
  }
416
531
 
417
532
  // src/adapters/npm/npm-scanner.ts
@@ -425,10 +540,11 @@ var NpmDependencyAdapter = class {
425
540
  registry;
426
541
  config;
427
542
  async scan(module, context) {
428
- assertNpmManaged(context.rootDir);
543
+ const manager = await prepareSupportedPackageManager(context.rootDir);
544
+ const pnpm = manager === "pnpm";
429
545
  let pkg;
430
546
  try {
431
- pkg = JSON.parse(readFileSync3(join4(module.path, "package.json"), "utf8"));
547
+ pkg = JSON.parse(readFileSync4(join5(module.path, "package.json"), "utf8"));
432
548
  } catch {
433
549
  return { candidates: [], skipped: [] };
434
550
  }
@@ -442,13 +558,20 @@ var NpmDependencyAdapter = class {
442
558
  [...npmConfig.scopedRegistries].filter(([, url]) => !isPublicNpmRegistry(url))
443
559
  )
444
560
  };
561
+ const workspaceFile = pnpm ? readPnpmWorkspace(context.rootDir) : void 0;
445
562
  const outcomes = await mapPooled(declaredDependencies(pkg), CONCURRENCY, async (dep) => {
446
563
  const origin = classifyOrigin(dep.name, "npm", originContext);
447
564
  if (origin.kind === "workspace") {
448
565
  const target = context.workspace.get(dep.name);
449
- const candidate = target ? alignmentCandidate(dep, target, origin) : null;
566
+ if (pnpm && isUpgradableRange(dep.range) && splitRangeProtocol(dep.range).protocol === void 0) {
567
+ return { skipped: { identifier: dep.name, origin: "workspace", reason: "not-linked", detail: dep.range } };
568
+ }
569
+ const candidate = target ? alignmentCandidate(dep, target, origin, pnpm) : null;
450
570
  return candidate ? { candidate } : null;
451
571
  }
572
+ if (pnpm && splitRangeProtocol(dep.range).protocol === "catalog") {
573
+ return this.catalogOutcome(dep, origin, context, module.path, workspaceFile);
574
+ }
452
575
  return this.registryOutcome(dep, origin, context);
453
576
  });
454
577
  const candidates = [];
@@ -461,8 +584,34 @@ var NpmDependencyAdapter = class {
461
584
  candidates.sort((a, b) => (DIFF_ORDER[a.diff] ?? 3) - (DIFF_ORDER[b.diff] ?? 3));
462
585
  return { candidates, skipped };
463
586
  }
587
+ /** A `catalog:` dependency: the range comes from pnpm-workspace.yaml and is written back there. */
588
+ async catalogOutcome(dep, origin, context, modulePath, workspaceFile) {
589
+ const catalogName = splitRangeProtocol(dep.range).rest || "default";
590
+ const unresolved = (detail) => ({
591
+ skipped: { identifier: dep.name, origin: origin.kind, reason: "catalog-unresolved", detail }
592
+ });
593
+ if (!workspaceFile) return unresolved("pnpm-workspace.yaml is missing or not valid YAML");
594
+ if (catalogName === "default" && workspaceFile.ambiguousDefaultCatalog) {
595
+ return unresolved("catalog 'default' is defined twice (catalog and catalogs.default)");
596
+ }
597
+ const catalog = workspaceFile.catalogs.get(catalogName);
598
+ if (!catalog) return unresolved(`no catalog '${catalogName}'`);
599
+ const range = catalog.get(dep.name);
600
+ if (range === void 0) return unresolved(`no entry for '${dep.name}' in catalog '${catalogName}'`);
601
+ const declaration = {
602
+ file: workspaceFile.file,
603
+ kind: "catalog",
604
+ catalogName,
605
+ displayPath: "pnpm-workspace.yaml"
606
+ };
607
+ const sharedWith = usersOfCatalogEntry(context, modulePath, dep.name, catalogName);
608
+ return this.registryOutcome({ ...dep, range }, origin, context, {
609
+ declaration,
610
+ ...sharedWith.length > 0 ? { sharedWith } : {}
611
+ });
612
+ }
464
613
  /** Looks up a private or public dependency in its registry. */
465
- async registryOutcome(dep, origin, context) {
614
+ async registryOutcome(dep, origin, context, extra = {}) {
466
615
  if (!isUpgradableRange(dep.range)) return null;
467
616
  if (!isValidPackageName(dep.name)) {
468
617
  return { skipped: { identifier: dep.name, origin: origin.kind, reason: "invalid-name" } };
@@ -504,7 +653,8 @@ var NpmDependencyAdapter = class {
504
653
  origin: resolvedOrigin,
505
654
  action: "update",
506
655
  isPreRelease: tag !== void 0,
507
- preReleaseTag: tag
656
+ preReleaseTag: tag,
657
+ ...extra
508
658
  }
509
659
  };
510
660
  }
@@ -525,11 +675,14 @@ function isUpgradableRange(range) {
525
675
  if (trimmed === "" || trimmed === "*" || trimmed.toLowerCase() === "x") return false;
526
676
  return semver3.validRange(trimmed) !== null;
527
677
  }
528
- function alignmentCandidate(dep, target, origin) {
678
+ function alignmentCandidate(dep, target, origin, pnpm = false) {
679
+ const split = pnpm ? splitRangeProtocol(dep.range) : { protocol: void 0, rest: dep.range };
680
+ const range = split.protocol === "workspace" ? split.rest : dep.range;
681
+ const protocol = split.protocol === "workspace" ? "workspace:" : "";
529
682
  const local = target.version;
530
- if (!local || !semver3.valid(local) || !isUpgradableRange(dep.range)) return null;
531
- if (semver3.satisfies(local, dep.range, { includePrerelease: true })) return null;
532
- const currentClean = semver3.minVersion(dep.range)?.version ?? "0.0.0";
683
+ if (!local || !semver3.valid(local) || !isUpgradableRange(range)) return null;
684
+ if (semver3.satisfies(local, range, { includePrerelease: true })) return null;
685
+ const currentClean = semver3.minVersion(range)?.version ?? "0.0.0";
533
686
  const diff = diffVersions(currentClean, local);
534
687
  const tag = semver3.prerelease(local) ? extractPreReleaseTag(local) ?? "PRE" : void 0;
535
688
  return {
@@ -537,7 +690,7 @@ function alignmentCandidate(dep, target, origin) {
537
690
  currentRange: dep.range,
538
691
  currentClean,
539
692
  latest: local,
540
- newRange: `${rangePrefix(dep.range)}${local}`,
693
+ newRange: `${protocol}${rangePrefix(range)}${local}`,
541
694
  diff,
542
695
  scope: dep.scope,
543
696
  selected: diff !== "major",
@@ -548,10 +701,27 @@ function alignmentCandidate(dep, target, origin) {
548
701
  preReleaseTag: tag
549
702
  };
550
703
  }
704
+ function usersOfCatalogEntry(context, selfPath, name, catalogName) {
705
+ const users = [];
706
+ for (const module of context.workspace.values()) {
707
+ if (module.path === selfPath) continue;
708
+ let pkg;
709
+ try {
710
+ pkg = JSON.parse(readFileSync4(join5(module.path, "package.json"), "utf8"));
711
+ } catch {
712
+ continue;
713
+ }
714
+ const dep = declaredDependencies(pkg).find((d) => d.name === name);
715
+ if (!dep || splitRangeProtocol(dep.range).protocol !== "catalog") continue;
716
+ if ((splitRangeProtocol(dep.range).rest || "default") !== catalogName) continue;
717
+ users.push(module.id);
718
+ }
719
+ return users;
720
+ }
551
721
 
552
722
  // src/adapters/npm/npm-updater.ts
553
- import { readFileSync as readFileSync5, writeFileSync } from "fs";
554
- import { join as join6, relative as relative2 } from "path";
723
+ import { readFileSync as readFileSync7, writeFileSync } from "fs";
724
+ import { join as join8, relative as relative2 } from "path";
555
725
 
556
726
  // src/domain/ports.ts
557
727
  var InstallError = class extends Error {
@@ -564,13 +734,13 @@ var InstallError = class extends Error {
564
734
  };
565
735
 
566
736
  // src/adapters/npm/npm-lockfile.ts
567
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
568
- import { join as join5 } from "path";
737
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
738
+ import { join as join6 } from "path";
569
739
  function readLockfile(rootDir) {
570
- const file = join5(rootDir, "package-lock.json");
740
+ const file = join6(rootDir, "package-lock.json");
571
741
  if (!existsSync3(file)) return void 0;
572
742
  try {
573
- return JSON.parse(readFileSync4(file, "utf8"));
743
+ return JSON.parse(readFileSync5(file, "utf8"));
574
744
  } catch {
575
745
  return void 0;
576
746
  }
@@ -591,51 +761,168 @@ function findUnlinkedWorkspaceDependencies(lockfile, moduleRelPath, names) {
591
761
  return violations;
592
762
  }
593
763
 
764
+ // src/adapters/npm/pnpm-audit.ts
765
+ var AUDIT_TIMEOUT_MS = 6e4;
766
+ var LEVELS = ["info", "low", "moderate", "high", "critical"];
767
+ function unavailable(reason) {
768
+ return { message: `Vulnerability check unavailable: ${reason}.`, severity: "warn" };
769
+ }
770
+ function parsePnpmAudit(output) {
771
+ let json;
772
+ try {
773
+ json = JSON.parse(output);
774
+ } catch {
775
+ return unavailable("pnpm audit returned no result");
776
+ }
777
+ const counts = json?.metadata?.vulnerabilities;
778
+ if (counts && typeof counts === "object") {
779
+ const parts = [];
780
+ let total = 0;
781
+ for (const level of LEVELS) {
782
+ const n = Number(counts[level]) || 0;
783
+ total += n;
784
+ if (n > 0) parts.push(`${n} ${level}`);
785
+ }
786
+ if (total === 0) return { message: "No known vulnerabilities found.", severity: "clean" };
787
+ return {
788
+ message: `${total} ${total === 1 ? "vulnerability" : "vulnerabilities"} (${parts.join(", ")})`,
789
+ severity: "warn"
790
+ };
791
+ }
792
+ const errorMessage = json?.error?.message;
793
+ if (typeof errorMessage === "string" && errorMessage) return unavailable(errorMessage);
794
+ return unavailable("pnpm audit returned no result");
795
+ }
796
+
797
+ // src/adapters/npm/pnpm-lockfile.ts
798
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
799
+ import { join as join7 } from "path";
800
+ import { parse as parse2 } from "yaml";
801
+ var SECTIONS = ["dependencies", "devDependencies", "optionalDependencies"];
802
+ function readPnpmLockfile(rootDir) {
803
+ const file = join7(rootDir, "pnpm-lock.yaml");
804
+ if (!existsSync4(file)) return { kind: "missing" };
805
+ let data;
806
+ try {
807
+ data = parse2(readFileSync6(file, "utf8"));
808
+ } catch {
809
+ return { kind: "unreadable" };
810
+ }
811
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return { kind: "unreadable" };
812
+ const lockfileVersion = String(data.lockfileVersion ?? "");
813
+ if (!/^9(\.|$)/.test(lockfileVersion)) return { kind: "unsupported", lockfileVersion };
814
+ const importers = data.importers ?? {};
815
+ if (typeof importers !== "object" || Array.isArray(importers)) return { kind: "unreadable" };
816
+ return { kind: "ok", importers };
817
+ }
818
+ function findUnlinkedPnpmDependencies(lockfile, moduleRelPath, names) {
819
+ switch (lockfile.kind) {
820
+ case "missing":
821
+ return ["pnpm-lock.yaml is missing; workspace links cannot be verified"];
822
+ case "unreadable":
823
+ return ["pnpm-lock.yaml could not be read; workspace links cannot be verified"];
824
+ case "unsupported":
825
+ return [
826
+ `pnpm-lock.yaml has lockfileVersion '${lockfile.lockfileVersion}', which Elevate cannot read; workspace links cannot be verified`
827
+ ];
828
+ }
829
+ const importer = lockfile.importers[moduleRelPath === "" ? "." : moduleRelPath] ?? {};
830
+ const violations = [];
831
+ for (const name of names) {
832
+ let entry;
833
+ for (const section of SECTIONS) {
834
+ entry = importer[section]?.[name];
835
+ if (entry) break;
836
+ }
837
+ if (!entry) violations.push(`${name}: not present in pnpm-lock.yaml`);
838
+ else if (!String(entry.version).startsWith("link:")) {
839
+ violations.push(`${name}: resolved to ${entry.version} instead of linking the workspace package`);
840
+ }
841
+ }
842
+ return violations;
843
+ }
844
+
594
845
  // src/adapters/npm/npm-updater.ts
595
846
  var INSTALL_TIMEOUT_MS = 10 * 6e4;
847
+ function failureDetails(manager, result) {
848
+ return manager === "pnpm" ? tail(`${result.stdout}
849
+ ${result.stderr}`) : tail(result.stderr || result.stdout);
850
+ }
596
851
  var NpmUpdaterAdapter = class {
597
852
  affectedFiles(module, rootDir) {
853
+ if (detectPackageManager(rootDir).name === "pnpm") {
854
+ return [
855
+ join8(module.path, "package.json"),
856
+ join8(rootDir, "package.json"),
857
+ join8(rootDir, "pnpm-lock.yaml"),
858
+ join8(rootDir, "pnpm-workspace.yaml")
859
+ ];
860
+ }
598
861
  return [
599
- join6(module.path, "package.json"),
600
- join6(rootDir, "package.json"),
601
- join6(rootDir, "package-lock.json"),
602
- join6(rootDir, "npm-shrinkwrap.json")
862
+ join8(module.path, "package.json"),
863
+ join8(rootDir, "package.json"),
864
+ join8(rootDir, "package-lock.json"),
865
+ join8(rootDir, "npm-shrinkwrap.json")
603
866
  ];
604
867
  }
605
868
  async applyUpdates(module, rootDir, updates, onProgress) {
869
+ let manager;
606
870
  try {
607
- assertNpmManaged(rootDir);
871
+ manager = await prepareSupportedPackageManager(rootDir);
608
872
  } catch (err) {
609
- if (err instanceof UnsupportedPackageManagerError) throw new InstallError(err.message);
873
+ if (err instanceof UnsupportedPackageManagerError || err instanceof PackageManagerNotFoundError) {
874
+ throw new InstallError(err.message);
875
+ }
610
876
  throw err;
611
877
  }
612
- const pkgPath = join6(module.path, "package.json");
613
- let source;
614
- let pkg;
615
- try {
616
- source = readFileSync5(pkgPath, "utf8");
617
- pkg = JSON.parse(source);
618
- } catch {
619
- throw new InstallError(`Cannot read package.json in ${module.path}.`);
878
+ const catalogUpdates = updates.filter((u) => u.declaration?.kind === "catalog");
879
+ const manifestUpdates = updates.filter((u) => u.declaration?.kind !== "catalog");
880
+ const pkgPath = join8(module.path, "package.json");
881
+ let manifest;
882
+ if (manifestUpdates.length > 0 || catalogUpdates.length === 0) {
883
+ try {
884
+ const source = readFileSync7(pkgPath, "utf8");
885
+ manifest = { source, pkg: JSON.parse(source) };
886
+ } catch {
887
+ throw new InstallError(`Cannot read package.json in ${module.path}.`);
888
+ }
620
889
  }
621
- onProgress("Writing new versions to package.json\u2026");
622
- for (const item of updates) {
623
- const name = item.coordinate.artifact;
624
- for (const section of DEPENDENCY_SECTIONS) {
625
- if (typeof pkg[section]?.[name] === "string") pkg[section][name] = item.newRange;
890
+ if (manifest) {
891
+ onProgress("Writing new versions to package.json\u2026");
892
+ for (const item of manifestUpdates) {
893
+ const name = item.coordinate.artifact;
894
+ for (const section of DEPENDENCY_SECTIONS) {
895
+ if (typeof manifest.pkg[section]?.[name] === "string") manifest.pkg[section][name] = item.newRange;
896
+ }
626
897
  }
898
+ writeFileSync(pkgPath, serializeLike(manifest.source, manifest.pkg), "utf8");
899
+ }
900
+ if (catalogUpdates.length > 0) {
901
+ onProgress("Writing new versions to pnpm-workspace.yaml\u2026");
902
+ writeCatalogRanges(catalogUpdates);
627
903
  }
628
- writeFileSync(pkgPath, serializeLike(source, pkg), "utf8");
629
- onProgress("Running npm install\u2026");
630
- const install = await runCommand("npm", ["install"], { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
631
- if (install.exitCode !== 0) {
632
- const reason = install.timedOut ? "npm install timed out" : "npm install failed";
633
- throw new InstallError(reason, tail(install.stderr || install.stdout));
634
- }
635
- onProgress("Checking workspace links in package-lock.json\u2026");
636
- const integrityViolations = verifyAlignedLinks(module, rootDir, updates);
637
- const combined = `${install.stdout}
638
- ${install.stderr}`;
904
+ const install = commands.install(manager);
905
+ onProgress(`Running ${manager} install\u2026`);
906
+ const result = await runCommand(install.command, install.args, { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
907
+ if (result.exitCode !== 0) {
908
+ const reason = result.timedOut ? `${manager} install timed out` : `${manager} install failed`;
909
+ throw new InstallError(reason, failureDetails(manager, result));
910
+ }
911
+ const lockName = manager === "pnpm" ? "pnpm-lock.yaml" : "package-lock.json";
912
+ onProgress(`Checking workspace links in ${lockName}\u2026`);
913
+ const integrityViolations = verifyAlignedLinks(manager, module, rootDir, updates);
914
+ if (manager === "pnpm") {
915
+ const audit2 = integrityViolations.length > 0 ? void 0 : await runPnpmAudit(rootDir);
916
+ return {
917
+ updatedCount: updates.length,
918
+ auditMessage: audit2?.message ?? "",
919
+ auditSeverity: audit2?.severity ?? "clean",
920
+ fundingMessage: void 0,
921
+ integrityViolations
922
+ };
923
+ }
924
+ const combined = `${result.stdout}
925
+ ${result.stderr}`;
639
926
  const audit = parseAuditSummary(combined);
640
927
  const fundingMatch = combined.match(/(\d+ packages? (?:is|are) looking for funding)/i);
641
928
  return {
@@ -647,14 +934,42 @@ ${install.stderr}`;
647
934
  };
648
935
  }
649
936
  async resync(_module, rootDir, onProgress) {
650
- if (detectPackageManager(rootDir).name !== "npm") return;
651
- onProgress("Restoring node_modules (npm install)\u2026");
652
- const result = await runCommand("npm", ["install"], { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
937
+ const name = detectPackageManager(rootDir).name;
938
+ if (name !== "npm" && name !== "pnpm") return;
939
+ const restore = commands.restore(name);
940
+ onProgress(`Restoring node_modules (${name} install)\u2026`);
941
+ const result = await runCommand(restore.command, restore.args, { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
653
942
  if (result.exitCode !== 0) {
654
- throw new InstallError("npm install failed while restoring the previous state", tail(result.stderr));
943
+ throw new InstallError(
944
+ `${name} install failed while restoring the previous state`,
945
+ name === "pnpm" ? failureDetails(name, result) : tail(result.stderr)
946
+ );
655
947
  }
656
948
  }
657
949
  };
950
+ function writeCatalogRanges(updates) {
951
+ const sources = /* @__PURE__ */ new Map();
952
+ for (const item of updates) {
953
+ const declaration = item.declaration;
954
+ try {
955
+ let source = sources.get(declaration.file) ?? readFileSync7(declaration.file, "utf8");
956
+ source = setCatalogRange(source, declaration.catalogName ?? "default", item.coordinate.artifact, item.newRange);
957
+ sources.set(declaration.file, source);
958
+ } catch (err) {
959
+ throw new InstallError(err instanceof Error ? err.message : `Cannot update ${declaration.displayPath}.`);
960
+ }
961
+ }
962
+ for (const [file, source] of sources) writeFileSync(file, source, "utf8");
963
+ }
964
+ async function runPnpmAudit(rootDir) {
965
+ try {
966
+ const result = await runCommand("pnpm", ["audit", "--json"], { cwd: rootDir, timeoutMs: AUDIT_TIMEOUT_MS });
967
+ if (result.timedOut) return unavailable(`pnpm audit timed out after ${AUDIT_TIMEOUT_MS / 1e3} seconds`);
968
+ return parsePnpmAudit(result.stdout);
969
+ } catch {
970
+ return unavailable("pnpm audit returned no result");
971
+ }
972
+ }
658
973
  var VULNERABILITY_LINE = /^(?:found\s+)?(\d+)\s+(?:(?:info|low|moderate|high|critical)\s+severity\s+)?vulnerabilit(?:y|ies)\b.*$/i;
659
974
  function parseAuditSummary(output) {
660
975
  for (const line of output.split(/\r?\n/)) {
@@ -663,12 +978,13 @@ function parseAuditSummary(output) {
663
978
  }
664
979
  return { message: "No known vulnerabilities found.", severity: "clean" };
665
980
  }
666
- function verifyAlignedLinks(module, rootDir, updates) {
981
+ function verifyAlignedLinks(manager, module, rootDir, updates) {
667
982
  const aligned = updates.filter((u) => u.origin.kind === "workspace").map((u) => u.coordinate.identifier);
668
983
  if (aligned.length === 0) return [];
984
+ const moduleRelPath = relative2(rootDir, module.path).replace(/\\/g, "/");
985
+ if (manager === "pnpm") return findUnlinkedPnpmDependencies(readPnpmLockfile(rootDir), moduleRelPath, aligned);
669
986
  const lockfile = readLockfile(rootDir);
670
987
  if (!lockfile) return ["package-lock.json is missing; workspace links cannot be verified"];
671
- const moduleRelPath = relative2(rootDir, module.path).replace(/\\/g, "/");
672
988
  return findUnlinkedWorkspaceDependencies(lockfile, moduleRelPath, aligned);
673
989
  }
674
990
  function serializeLike(original, value) {
@@ -682,24 +998,28 @@ function serializeLike(original, value) {
682
998
  var VERIFY_TIMEOUT_MS = 10 * 6e4;
683
999
  var NpmVerificationAdapter = class {
684
1000
  async verify(_module, rootDir, options = {}) {
685
- const label = options.customLabel || options.customScript || "Dependency tree integrity (npm ls)";
1001
+ let manager = "npm";
686
1002
  if (!options.customScript) {
687
1003
  try {
688
- assertNpmManaged(rootDir);
1004
+ manager = await prepareSupportedPackageManager(rootDir);
689
1005
  } catch (err) {
690
- if (err instanceof UnsupportedPackageManagerError) return { status: "warn", details: err.message, label };
1006
+ if (err instanceof UnsupportedPackageManagerError || err instanceof PackageManagerNotFoundError) {
1007
+ return { status: "warn", details: err.message, label: options.customLabel || "Dependency verification" };
1008
+ }
691
1009
  throw err;
692
1010
  }
693
1011
  }
694
- const result = options.customScript ? await runShellScript(options.customScript, { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS }) : await runCommand("npm", ["ls"], { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS });
1012
+ const defaultLabel = manager === "pnpm" ? "Lockfile consistency (pnpm install --frozen-lockfile)" : "Dependency tree integrity (npm ls)";
1013
+ const label = options.customLabel || options.customScript || defaultLabel;
1014
+ const verify = commands.verify(manager);
1015
+ const result = options.customScript ? await runShellScript(options.customScript, { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS }) : await runCommand(verify.command, verify.args, { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS });
695
1016
  if (result.exitCode === 0) {
696
- return { status: "clean", details: "All dependencies are installed and consistent.", label };
1017
+ const details = manager === "pnpm" && !options.customScript ? "pnpm-lock.yaml is up to date with all package.json files." : "All dependencies are installed and consistent.";
1018
+ return { status: "clean", details, label };
697
1019
  }
698
- return {
699
- status: "warn",
700
- details: result.timedOut ? "Verification timed out." : tail(result.stderr || result.stdout),
701
- label
702
- };
1020
+ const output = manager === "pnpm" && !options.customScript ? tail(`${result.stdout}
1021
+ ${result.stderr}`) : tail(result.stderr || result.stdout);
1022
+ return { status: "warn", details: result.timedOut ? "Verification timed out." : output, label };
703
1023
  }
704
1024
  };
705
1025
 
@@ -707,6 +1027,12 @@ var NpmVerificationAdapter = class {
707
1027
  var NpmEcosystemStrategy = class {
708
1028
  ecosystem = "npm";
709
1029
  displayName = "Node / npm";
1030
+ packageManagerFor(rootDir) {
1031
+ return detectPackageManager(rootDir).name;
1032
+ }
1033
+ displayNameFor(rootDir) {
1034
+ return detectPackageManager(rootDir).name === "pnpm" ? "Node / pnpm" : "Node / npm";
1035
+ }
710
1036
  icon = "\u{1F4E6}";
711
1037
  manifestFile = "package.json";
712
1038
  discovery = new NpmModuleDiscoveryAdapter();
@@ -724,16 +1050,16 @@ var NpmEcosystemStrategy = class {
724
1050
  };
725
1051
 
726
1052
  // src/adapters/maven/maven-discovery.ts
727
- import { existsSync as existsSync6 } from "fs";
728
- import { dirname as dirname5, join as join9, relative as relative3, resolve as resolve6 } from "path";
1053
+ import { existsSync as existsSync7 } from "fs";
1054
+ import { dirname as dirname5, join as join11, relative as relative3, resolve as resolve6 } from "path";
729
1055
 
730
1056
  // src/adapters/maven/maven-project.ts
731
- import { existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
732
- import { join as join8, resolve as resolve5 } from "path";
1057
+ import { existsSync as existsSync6, readdirSync, statSync as statSync2 } from "fs";
1058
+ import { join as join10, resolve as resolve5 } from "path";
733
1059
 
734
1060
  // src/adapters/maven/maven-pom.ts
735
- import { existsSync as existsSync4, readFileSync as readFileSync6, statSync } from "fs";
736
- import { dirname as dirname4, join as join7, resolve as resolve4 } from "path";
1061
+ import { existsSync as existsSync5, readFileSync as readFileSync8, statSync } from "fs";
1062
+ import { dirname as dirname4, join as join9, resolve as resolve4 } from "path";
737
1063
 
738
1064
  // src/adapters/shared/xml.ts
739
1065
  import { SaxesParser } from "saxes";
@@ -868,7 +1194,7 @@ function localName(qualified) {
868
1194
 
869
1195
  // src/adapters/maven/maven-pom.ts
870
1196
  function readPom(file) {
871
- const source = readFileSync6(file, "utf8");
1197
+ const source = readFileSync8(file, "utf8");
872
1198
  return parsePom(source, file);
873
1199
  }
874
1200
  function parsePom(source, file) {
@@ -917,8 +1243,8 @@ function parentPomFile(pom) {
917
1243
  const relativePath = pom.parent.relativePath ?? "../pom.xml";
918
1244
  if (relativePath === "") return void 0;
919
1245
  let candidate = resolve4(pom.dir, relativePath);
920
- if (existsSync4(candidate) && statSync(candidate).isDirectory()) candidate = join7(candidate, "pom.xml");
921
- return existsSync4(candidate) ? candidate : void 0;
1246
+ if (existsSync5(candidate) && statSync(candidate).isDirectory()) candidate = join9(candidate, "pom.xml");
1247
+ return existsSync5(candidate) ? candidate : void 0;
922
1248
  }
923
1249
  function builtinProperty(name, module) {
924
1250
  switch (name) {
@@ -994,8 +1320,8 @@ var MavenProject = class _MavenProject {
994
1320
  poms.push(pom);
995
1321
  for (const entry of pom.modules) {
996
1322
  const target = resolve5(pom.dir, entry);
997
- const moduleFile = target.endsWith(".xml") ? target : join8(target, "pom.xml");
998
- if (!seen.has(moduleFile) && existsSync5(moduleFile)) {
1323
+ const moduleFile = target.endsWith(".xml") ? target : join10(target, "pom.xml");
1324
+ if (!seen.has(moduleFile) && existsSync6(moduleFile)) {
999
1325
  seen.add(moduleFile);
1000
1326
  queue.push(moduleFile);
1001
1327
  }
@@ -1065,7 +1391,7 @@ var MavenProject = class _MavenProject {
1065
1391
  this.aggregators.set(pom.file, aggregator.dir);
1066
1392
  for (const entry of pom.modules) {
1067
1393
  const target = resolve5(pom.dir, entry);
1068
- const file = target.endsWith(".xml") ? target : join8(target, "pom.xml");
1394
+ const file = target.endsWith(".xml") ? target : join10(target, "pom.xml");
1069
1395
  const modulePom = this.byFile.get(file);
1070
1396
  if (modulePom) queue.push(modulePom);
1071
1397
  }
@@ -1075,23 +1401,23 @@ var MavenProject = class _MavenProject {
1075
1401
  };
1076
1402
  function findPomFiles(rootDir) {
1077
1403
  const results = [];
1078
- const rootPom = join8(rootDir, "pom.xml");
1079
- if (existsSync5(rootPom)) results.push(resolve5(rootPom));
1404
+ const rootPom = join10(rootDir, "pom.xml");
1405
+ if (existsSync6(rootPom)) results.push(resolve5(rootPom));
1080
1406
  const walk = (dir, depth) => {
1081
1407
  if (depth > MAX_DEPTH) return;
1082
- let entries;
1408
+ let entries2;
1083
1409
  try {
1084
- entries = readdirSync(dir, { withFileTypes: true });
1410
+ entries2 = readdirSync(dir, { withFileTypes: true });
1085
1411
  } catch {
1086
1412
  return;
1087
1413
  }
1088
- const isProject = existsSync5(join8(dir, "pom.xml"));
1089
- for (const entry of entries) {
1414
+ const isProject = existsSync6(join10(dir, "pom.xml"));
1415
+ for (const entry of entries2) {
1090
1416
  if (!entry.isDirectory() || entry.name.startsWith(".") || SKIPPED_DIRS.has(entry.name)) continue;
1091
1417
  if (isProject && entry.name === "src") continue;
1092
- const subDir = join8(dir, entry.name);
1093
- const pom = join8(subDir, "pom.xml");
1094
- if (existsSync5(pom) && statSync2(pom).isFile()) results.push(resolve5(pom));
1418
+ const subDir = join10(dir, entry.name);
1419
+ const pom = join10(subDir, "pom.xml");
1420
+ if (existsSync6(pom) && statSync2(pom).isFile()) results.push(resolve5(pom));
1095
1421
  walk(subDir, depth + 1);
1096
1422
  }
1097
1423
  };
@@ -1107,7 +1433,7 @@ var MavenModuleDiscoveryAdapter = class {
1107
1433
  */
1108
1434
  findRoot(startDir, boundaryDir) {
1109
1435
  let dir = resolve6(startDir);
1110
- if (!existsSync6(join9(dir, "pom.xml"))) return void 0;
1436
+ if (!existsSync7(join11(dir, "pom.xml"))) return void 0;
1111
1437
  const boundary = boundaryDir ? resolve6(boundaryDir) : void 0;
1112
1438
  for (; ; ) {
1113
1439
  const above = pomAbove(dir, boundary);
@@ -1154,7 +1480,7 @@ function ancestorsWithin(dir, boundary) {
1154
1480
  return result;
1155
1481
  }
1156
1482
  function pomAbove(dir, boundary) {
1157
- const pom = tryReadPom(join9(dir, "pom.xml"));
1483
+ const pom = tryReadPom(join11(dir, "pom.xml"));
1158
1484
  if (!pom) return void 0;
1159
1485
  const ancestors = ancestorsWithin(dir, boundary);
1160
1486
  const found = [];
@@ -1166,9 +1492,9 @@ function pomAbove(dir, boundary) {
1166
1492
  found.push(dirname5(parentFile));
1167
1493
  }
1168
1494
  }
1169
- const ownFile = join9(dir, "pom.xml");
1495
+ const ownFile = join11(dir, "pom.xml");
1170
1496
  const aggregator = ancestors.find(
1171
- (ancestor) => tryReadPom(join9(ancestor, "pom.xml"))?.modules.some((entry) => {
1497
+ (ancestor) => tryReadPom(join11(ancestor, "pom.xml"))?.modules.some((entry) => {
1172
1498
  const target = resolve6(ancestor, entry);
1173
1499
  return target === dir || target === ownFile;
1174
1500
  })
@@ -1178,12 +1504,12 @@ function pomAbove(dir, boundary) {
1178
1504
  }
1179
1505
 
1180
1506
  // src/adapters/maven/maven-registry.ts
1181
- import { existsSync as existsSync9 } from "fs";
1182
- import { join as join12 } from "path";
1507
+ import { existsSync as existsSync10 } from "fs";
1508
+ import { join as join14 } from "path";
1183
1509
 
1184
1510
  // src/adapters/maven/maven-command.ts
1185
- import { existsSync as existsSync7 } from "fs";
1186
- import { dirname as dirname6, isAbsolute, join as join10, relative as relative4, resolve as resolve7, sep } from "path";
1511
+ import { existsSync as existsSync8 } from "fs";
1512
+ import { dirname as dirname6, isAbsolute, join as join12, relative as relative4, resolve as resolve7, sep } from "path";
1187
1513
  var MavenUnavailableError = class extends Error {
1188
1514
  constructor() {
1189
1515
  super("Maven is not available: no Maven Wrapper (mvnw) in the project and no `mvn` on the PATH.");
@@ -1206,8 +1532,8 @@ function findMavenWrapper(fromDir, rootDir) {
1206
1532
  let dir = resolve7(fromDir);
1207
1533
  const root = resolve7(rootDir);
1208
1534
  for (; ; ) {
1209
- const candidate = join10(dir, WRAPPER);
1210
- if (existsSync7(candidate)) return candidate;
1535
+ const candidate = join12(dir, WRAPPER);
1536
+ if (existsSync8(candidate)) return candidate;
1211
1537
  if (dir === root || relative4(root, dir).startsWith("..")) return void 0;
1212
1538
  const parent = dirname6(dir);
1213
1539
  if (parent === dir) return void 0;
@@ -1240,8 +1566,8 @@ function mavenErrors(output) {
1240
1566
 
1241
1567
  // src/adapters/maven/maven-resolution.ts
1242
1568
  import { randomUUID } from "crypto";
1243
- import { existsSync as existsSync8, mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1244
- import { join as join11 } from "path";
1569
+ import { existsSync as existsSync9, mkdirSync, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
1570
+ import { join as join13 } from "path";
1245
1571
  var DEFAULT_MAVEN_PLUGINS = { help: "3.5.2", versions: "2.22.0" };
1246
1572
  var HELP_PLUGIN_ID = "org.apache.maven.plugins:maven-help-plugin";
1247
1573
  var VERSIONS_PLUGIN_ID = "org.codehaus.mojo:versions-maven-plugin";
@@ -1273,9 +1599,9 @@ var ScratchDirectory = class {
1273
1599
  target;
1274
1600
  createdTarget;
1275
1601
  constructor(moduleDir) {
1276
- this.target = join11(moduleDir, "target");
1277
- this.createdTarget = !existsSync8(this.target);
1278
- this.path = join11(this.target, `.elevate-${randomUUID().slice(0, 8)}`);
1602
+ this.target = join13(moduleDir, "target");
1603
+ this.createdTarget = !existsSync9(this.target);
1604
+ this.path = join13(this.target, `.elevate-${randomUUID().slice(0, 8)}`);
1279
1605
  mkdirSync(this.path, { recursive: true });
1280
1606
  }
1281
1607
  dispose() {
@@ -1287,7 +1613,7 @@ var ScratchDirectory = class {
1287
1613
  }
1288
1614
  };
1289
1615
  async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins, recursive = false) {
1290
- const output = join11(scratch.path, "effective-pom.xml");
1616
+ const output = join13(scratch.path, "effective-pom.xml");
1291
1617
  const args = [
1292
1618
  "-f",
1293
1619
  relativeToCwd(moduleDir, pomFile),
@@ -1299,7 +1625,7 @@ async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins,
1299
1625
  rootDir,
1300
1626
  timeoutMs: MAVEN_TIMEOUT_MS
1301
1627
  });
1302
- if (result.exitCode !== 0 || !existsSync8(output)) {
1628
+ if (result.exitCode !== 0 || !existsSync9(output)) {
1303
1629
  const text = `${result.stdout}
1304
1630
  ${result.stderr}`;
1305
1631
  if (isPluginUnavailable(text, HELP_PLUGIN_ID)) {
@@ -1311,7 +1637,7 @@ ${result.stderr}`;
1311
1637
  throw new MavenCommandError(`Maven could not build the effective POM:
1312
1638
  ${mavenErrors(text)}`, text);
1313
1639
  }
1314
- return parseEffectivePoms(readFileSync7(output, "utf8"));
1640
+ return parseEffectivePoms(readFileSync9(output, "utf8"));
1315
1641
  }
1316
1642
  function parseEffectivePoms(source) {
1317
1643
  const root = parseXml(source);
@@ -1360,7 +1686,7 @@ function readEffectiveDependencies(container) {
1360
1686
  }
1361
1687
  function buildProbePom(artifacts, model) {
1362
1688
  const seen = /* @__PURE__ */ new Set();
1363
- const entries = [];
1689
+ const entries2 = [];
1364
1690
  for (const artifact of artifacts) {
1365
1691
  const key = `${artifact.groupId}:${artifact.artifactId}:${artifact.type ?? "jar"}:${artifact.classifier ?? ""}`;
1366
1692
  if (seen.has(key)) continue;
@@ -1372,7 +1698,7 @@ function buildProbePom(artifacts, model) {
1372
1698
  artifact.type && artifact.type !== "jar" ? `<type>${escapeXml(artifact.type)}</type>` : "",
1373
1699
  artifact.classifier ? `<classifier>${escapeXml(artifact.classifier)}</classifier>` : ""
1374
1700
  ].filter(Boolean);
1375
- entries.push(` <dependency>${fields.join("")}</dependency>`);
1701
+ entries2.push(` <dependency>${fields.join("")}</dependency>`);
1376
1702
  }
1377
1703
  const repositories = [model.repositories, model.pluginRepositories].filter((element) => element !== void 0).map((element) => serializeXml(element, " ")).join("\n");
1378
1704
  return [
@@ -1386,7 +1712,7 @@ function buildProbePom(artifacts, model) {
1386
1712
  repositories,
1387
1713
  " <dependencyManagement>",
1388
1714
  " <dependencies>",
1389
- ...entries,
1715
+ ...entries2,
1390
1716
  " </dependencies>",
1391
1717
  " </dependencyManagement>",
1392
1718
  "</project>",
@@ -1395,9 +1721,9 @@ function buildProbePom(artifacts, model) {
1395
1721
  }
1396
1722
  async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch, plugins) {
1397
1723
  if (artifacts.length === 0) return { newerVersions: /* @__PURE__ */ new Map(), failedLookups: /* @__PURE__ */ new Set() };
1398
- const probeDir = join11(scratch.path, "probe");
1724
+ const probeDir = join13(scratch.path, "probe");
1399
1725
  mkdirSync(probeDir, { recursive: true });
1400
- const probePom = join11(probeDir, "pom.xml");
1726
+ const probePom = join13(probeDir, "pom.xml");
1401
1727
  writeFileSync2(probePom, buildProbePom(artifacts, model), "utf8");
1402
1728
  const result = await runMaven(
1403
1729
  [
@@ -1412,8 +1738,8 @@ async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch,
1412
1738
  );
1413
1739
  const log = `${result.stdout}
1414
1740
  ${result.stderr}`;
1415
- const reportFile = join11(probeDir, "target", "dependency-updates-report.xml");
1416
- if (result.exitCode !== 0 || !existsSync8(reportFile)) {
1741
+ const reportFile = join13(probeDir, "target", "dependency-updates-report.xml");
1742
+ if (result.exitCode !== 0 || !existsSync9(reportFile)) {
1417
1743
  if (isPluginUnavailable(log, VERSIONS_PLUGIN_ID)) {
1418
1744
  throw new MavenPluginUnavailableError(
1419
1745
  { id: VERSIONS_PLUGIN_ID, version: plugins.versions, key: "versions", purpose: "look up newer versions" },
@@ -1424,7 +1750,7 @@ ${result.stderr}`;
1424
1750
  ${mavenErrors(log)}`, log);
1425
1751
  }
1426
1752
  return {
1427
- newerVersions: parseUpdatesReport(readFileSync7(reportFile, "utf8")),
1753
+ newerVersions: parseUpdatesReport(readFileSync9(reportFile, "utf8")),
1428
1754
  failedLookups: parseFailedLookups(log)
1429
1755
  };
1430
1756
  }
@@ -1509,14 +1835,14 @@ var MavenRegistryAdapter = class {
1509
1835
  }
1510
1836
  };
1511
1837
  async function projectRepositories(cwd, scratch, plugins) {
1512
- const rootPom = join12(cwd, "pom.xml");
1513
- if (!existsSync9(rootPom)) return NO_REPOSITORIES;
1838
+ const rootPom = join14(cwd, "pom.xml");
1839
+ if (!existsSync10(rootPom)) return NO_REPOSITORIES;
1514
1840
  const models = await readEffectivePoms(rootPom, cwd, cwd, scratch, plugins);
1515
1841
  return [...models.values()][0] ?? NO_REPOSITORIES;
1516
1842
  }
1517
1843
 
1518
1844
  // src/adapters/maven/maven-scanner.ts
1519
- import { join as join13 } from "path";
1845
+ import { join as join15 } from "path";
1520
1846
  import semver4 from "semver";
1521
1847
 
1522
1848
  // src/adapters/maven/maven-locator.ts
@@ -1661,14 +1987,14 @@ function groupModules(project, modules) {
1661
1987
  if (module.aggregatorDir) reactorSizes.set(module.aggregatorDir, (reactorSizes.get(module.aggregatorDir) ?? 0) + 1);
1662
1988
  }
1663
1989
  for (const module of modules) {
1664
- const pom = project.pomAt(join13(module.path, "pom.xml"));
1990
+ const pom = project.pomAt(join15(module.path, "pom.xml"));
1665
1991
  if (!pom) continue;
1666
1992
  const reactor = module.aggregatorDir;
1667
1993
  const batched = reactor !== void 0 && (reactorSizes.get(reactor) ?? 0) > 1;
1668
1994
  const key = batched ? `reactor:${reactor}` : `module:${module.path}`;
1669
1995
  const group = groups.get(key) ?? {
1670
1996
  dir: batched ? reactor : module.path,
1671
- aggregatorPom: batched ? join13(reactor, "pom.xml") : void 0,
1997
+ aggregatorPom: batched ? join15(reactor, "pom.xml") : void 0,
1672
1998
  members: []
1673
1999
  };
1674
2000
  group.members.push({ module, pom });
@@ -1836,11 +2162,11 @@ function markSharedDeclarations(candidates) {
1836
2162
  }
1837
2163
 
1838
2164
  // src/adapters/maven/maven-updater.ts
1839
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
1840
- import { join as join14, resolve as resolve8 } from "path";
2165
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
2166
+ import { join as join16, resolve as resolve8 } from "path";
1841
2167
  var MavenUpdaterAdapter = class {
1842
2168
  affectedFiles(module, _rootDir, updates) {
1843
- const files = new Set(updates.map((u) => resolve8(u.declaration?.file ?? join14(module.path, "pom.xml"))));
2169
+ const files = new Set(updates.map((u) => resolve8(u.declaration?.file ?? join16(module.path, "pom.xml"))));
1844
2170
  return [...files];
1845
2171
  }
1846
2172
  async applyUpdates(module, rootDir, updates, onProgress) {
@@ -1856,7 +2182,7 @@ var MavenUpdaterAdapter = class {
1856
2182
  const writes = [];
1857
2183
  for (const [file, fileUpdates] of byFile) {
1858
2184
  onProgress(`Preparing ${fileUpdates.length} change(s) in ${fileUpdates[0].declaration.displayPath}\u2026`);
1859
- const source = readFileSync8(file, "utf8");
2185
+ const source = readFileSync10(file, "utf8");
1860
2186
  const pom = parsePom(source, file);
1861
2187
  const chain = project.pomAt(file) ? project.chainOf(project.pomAt(file)).poms : [pom];
1862
2188
  const edits = collectEdits(pom, [pom, ...chain.slice(1)], fileUpdates);
@@ -1926,14 +2252,14 @@ function describe(declaration) {
1926
2252
  }
1927
2253
 
1928
2254
  // src/adapters/maven/maven-verifier.ts
1929
- import { dirname as dirname7, join as join15, relative as relative6, resolve as resolve9 } from "path";
2255
+ import { dirname as dirname7, join as join17, relative as relative6, resolve as resolve9 } from "path";
1930
2256
  var VERIFY_TIMEOUT_MS2 = 30 * 6e4;
1931
2257
  var MavenVerificationAdapter = class {
1932
2258
  async verify(module, rootDir, options = {}) {
1933
2259
  if (options.customScript) {
1934
2260
  const label2 = options.customLabel || options.customScript;
1935
2261
  const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs: VERIFY_TIMEOUT_MS2 });
1936
- return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: failureDetails(result), label: label2 };
2262
+ return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: failureDetails2(result), label: label2 };
1937
2263
  }
1938
2264
  const plan = planBuild(module, rootDir, options.changedFiles ?? []);
1939
2265
  const label = `Maven build (${["test-compile", ...plan.args].join(" ")})`;
@@ -1946,7 +2272,7 @@ var MavenVerificationAdapter = class {
1946
2272
  if (result.exitCode === 0) {
1947
2273
  return { status: "clean", details: "All affected modules compile, including tests.", label };
1948
2274
  }
1949
- const details = result.timedOut ? "Maven build timed out." : failureDetails(result);
2275
+ const details = result.timedOut ? "Maven build timed out." : failureDetails2(result);
1950
2276
  return { status: "warn", details, label };
1951
2277
  } catch (err) {
1952
2278
  if (err instanceof MavenUnavailableError) return { status: "warn", details: err.message, label };
@@ -1954,7 +2280,7 @@ var MavenVerificationAdapter = class {
1954
2280
  }
1955
2281
  }
1956
2282
  };
1957
- function failureDetails(result) {
2283
+ function failureDetails2(result) {
1958
2284
  const output = `${result.stdout}
1959
2285
  ${result.stderr}`.trim();
1960
2286
  return output ? mavenErrors(output) : `Exited with code ${result.exitCode}.`;
@@ -1963,7 +2289,7 @@ function planBuild(module, rootDir, changedFiles) {
1963
2289
  const aggregatorDir = module.aggregatorDir;
1964
2290
  if (!aggregatorDir) return { cwd: module.path, args: [] };
1965
2291
  const project = MavenProject.load(rootDir);
1966
- const files = changedFiles.length > 0 ? changedFiles : [join15(module.path, "pom.xml")];
2292
+ const files = changedFiles.length > 0 ? changedFiles : [join17(module.path, "pom.xml")];
1967
2293
  const selected = /* @__PURE__ */ new Set();
1968
2294
  for (const file of files) {
1969
2295
  const pom = project.pomAt(file);
@@ -2038,7 +2364,7 @@ function findRepositoryRoot(startDir) {
2038
2364
  const boundary = findCheckoutBoundary(start);
2039
2365
  let dir = start;
2040
2366
  for (; ; ) {
2041
- if (existsSync10(join16(dir, "elevate.config.json"))) return dir;
2367
+ if (existsSync11(join18(dir, "elevate.config.json"))) return dir;
2042
2368
  const parent = dirname8(dir);
2043
2369
  if (dir === boundary || parent === dir) break;
2044
2370
  dir = parent;
@@ -2104,15 +2430,15 @@ function resolveConfig(rootDir, file, source = "elevate.config.json") {
2104
2430
  }
2105
2431
  function loadConfig2(startDir = process.cwd()) {
2106
2432
  const rootDir = findRepositoryRoot(startDir);
2107
- const configPath = join16(rootDir, "elevate.config.json");
2433
+ const configPath = join18(rootDir, "elevate.config.json");
2108
2434
  let file = {};
2109
- if (existsSync10(configPath)) {
2435
+ if (existsSync11(configPath)) {
2110
2436
  const unreadable = (detail) => new ConfigError(
2111
2437
  `Cannot read elevate.config.json: ${detail} (${configPath}). Fix the file or remove it; ${NO_CONTINUE}`
2112
2438
  );
2113
2439
  let parsed;
2114
2440
  try {
2115
- parsed = JSON.parse(readFileSync9(configPath, "utf8").replace(/^/, ""));
2441
+ parsed = JSON.parse(readFileSync11(configPath, "utf8").replace(/^/, ""));
2116
2442
  } catch (err) {
2117
2443
  throw unreadable(err instanceof Error ? err.message : String(err));
2118
2444
  }
@@ -2161,7 +2487,9 @@ var de = {
2161
2487
  "lookup-failed": "Abfrage der Registry fehlgeschlagen",
2162
2488
  "managed-externally": "Version wird au\xDFerhalb dieses Repos verwaltet",
2163
2489
  "declaration-mismatch": "Ort der Versionsangabe ist nicht eindeutig",
2164
- "invalid-name": "ung\xFCltiger Paketname"
2490
+ "invalid-name": "ung\xFCltiger Paketname",
2491
+ "not-linked": "Workspace-Paket, das pnpm ohne das workspace:-Protokoll nicht verlinkt",
2492
+ "catalog-unresolved": "Catalog-Eintrag konnte nicht aufgel\xF6st werden"
2165
2493
  })[reason]
2166
2494
  },
2167
2495
  badges: {
@@ -2267,7 +2595,9 @@ var en = {
2267
2595
  "lookup-failed": "registry lookup failed",
2268
2596
  "managed-externally": "version is managed outside this repository",
2269
2597
  "declaration-mismatch": "version location is ambiguous",
2270
- "invalid-name": "invalid package name"
2598
+ "invalid-name": "invalid package name",
2599
+ "not-linked": "workspace package that pnpm does not link without the workspace: protocol",
2600
+ "catalog-unresolved": "catalog entry could not be resolved"
2271
2601
  })[reason]
2272
2602
  },
2273
2603
  badges: {
@@ -2461,7 +2791,12 @@ function candidateToJson(u) {
2461
2791
  scope: u.scope,
2462
2792
  isPreRelease: u.isPreRelease ?? false,
2463
2793
  preReleaseTag: u.preReleaseTag,
2464
- declaredIn: u.declaration ? { file: u.declaration.displayPath, kind: u.declaration.kind, property: u.declaration.propertyName } : void 0,
2794
+ declaredIn: u.declaration ? {
2795
+ file: u.declaration.displayPath,
2796
+ kind: u.declaration.kind,
2797
+ property: u.declaration.propertyName,
2798
+ catalog: u.declaration.catalogName
2799
+ } : void 0,
2465
2800
  sharedWith: u.sharedWith
2466
2801
  };
2467
2802
  }
@@ -2583,24 +2918,24 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
2583
2918
  import { useState as useState4, useCallback as useCallback4 } from "react";
2584
2919
 
2585
2920
  // src/application/update-workflow.ts
2586
- import { existsSync as existsSync12 } from "fs";
2921
+ import { existsSync as existsSync13 } from "fs";
2587
2922
  import { relative as relative7 } from "path";
2588
2923
 
2589
2924
  // src/adapters/shared/file-snapshot.ts
2590
- import { existsSync as existsSync11, readFileSync as readFileSync10, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
2925
+ import { existsSync as existsSync12, readFileSync as readFileSync12, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
2591
2926
  var FileSnapshot = class _FileSnapshot {
2592
- constructor(entries) {
2593
- this.entries = entries;
2927
+ constructor(entries2) {
2928
+ this.entries = entries2;
2594
2929
  }
2595
2930
  entries;
2596
2931
  /** Records the current content (or absence) of each file. */
2597
2932
  static capture(files) {
2598
- const entries = /* @__PURE__ */ new Map();
2933
+ const entries2 = /* @__PURE__ */ new Map();
2599
2934
  for (const file of files) {
2600
- if (entries.has(file)) continue;
2601
- entries.set(file, existsSync11(file) ? readFileSync10(file) : null);
2935
+ if (entries2.has(file)) continue;
2936
+ entries2.set(file, existsSync12(file) ? readFileSync12(file) : null);
2602
2937
  }
2603
- return new _FileSnapshot(entries);
2938
+ return new _FileSnapshot(entries2);
2604
2939
  }
2605
2940
  /** The files covered by this snapshot. */
2606
2941
  get files() {
@@ -2610,9 +2945,9 @@ var FileSnapshot = class _FileSnapshot {
2610
2945
  changedFiles() {
2611
2946
  return this.files.filter((file) => {
2612
2947
  const before = this.entries.get(file) ?? null;
2613
- const exists = existsSync11(file);
2948
+ const exists = existsSync12(file);
2614
2949
  if (before === null) return exists;
2615
- return !exists || !readFileSync10(file).equals(before);
2950
+ return !exists || !readFileSync12(file).equals(before);
2616
2951
  });
2617
2952
  }
2618
2953
  /** Writes every file back to its captured state; removes files that did not exist. */
@@ -2660,7 +2995,7 @@ Restoring the installed state failed as well: ${message(err)}`;
2660
2995
  let baseline;
2661
2996
  if (!options.skipVerification) {
2662
2997
  progress("Running verification on the current state\u2026");
2663
- baseline = await verify(snapshot.files.filter((file) => existsSync12(file)));
2998
+ baseline = await verify(snapshot.files.filter((file) => existsSync13(file)));
2664
2999
  }
2665
3000
  let applied;
2666
3001
  try {
@@ -2864,7 +3199,8 @@ var Header = ({
2864
3199
  currentLocale,
2865
3200
  strategy,
2866
3201
  nextStrategyName,
2867
- channel
3202
+ channel,
3203
+ rootDir
2868
3204
  }) => /* @__PURE__ */ jsxs2(
2869
3205
  Box2,
2870
3206
  {
@@ -2885,7 +3221,7 @@ var Header = ({
2885
3221
  /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.brandLight, bold: true, children: [
2886
3222
  strategy.icon,
2887
3223
  " ",
2888
- strategy.displayName
3224
+ rootDir && strategy.displayNameFor?.(rootDir) || strategy.displayName
2889
3225
  ] }),
2890
3226
  /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.muted, children: [
2891
3227
  " [",
@@ -3733,7 +4069,8 @@ var App = ({ config }) => {
3733
4069
  currentLocale: locale,
3734
4070
  strategy,
3735
4071
  nextStrategyName: `${nextStrategy.icon} ${nextStrategy.displayName}`,
3736
- channel: config.channel
4072
+ channel: config.channel,
4073
+ rootDir: config.rootDir
3737
4074
  }
3738
4075
  ),
3739
4076
  view === "workspace_modal" && /* @__PURE__ */ jsx14(
@@ -3931,12 +4268,14 @@ async function handleModulesCommand(options, config) {
3931
4268
  const ecosystem = options.ecosystem ?? "npm";
3932
4269
  const strategy = EcosystemFactory.getStrategy(ecosystem);
3933
4270
  const modules = await strategy.discovery.discover(config.rootDir);
4271
+ const displayName = strategy.displayNameFor?.(config.rootDir) ?? strategy.displayName;
4272
+ const packageManager = strategy.packageManagerFor?.(config.rootDir);
3934
4273
  if (options.json) {
3935
- console.log(JSON.stringify({ ecosystem, count: modules.length, modules }, null, 2));
4274
+ console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules }, null, 2));
3936
4275
  return 0;
3937
4276
  }
3938
4277
  console.log(`
3939
- \u{1FAB6} Discovered ${strategy.icon} ${strategy.displayName} Modules (${modules.length}):
4278
+ \u{1FAB6} Discovered ${strategy.icon} ${displayName} Modules (${modules.length}):
3940
4279
  `);
3941
4280
  for (const m of modules) {
3942
4281
  const isRootBadge = m.isRoot ? " [ROOT]" : "";
@@ -3960,6 +4299,10 @@ function describeSkip(skipped) {
3960
4299
  return `version location is ambiguous${detail}`;
3961
4300
  case "invalid-name":
3962
4301
  return "not a valid package name";
4302
+ case "not-linked":
4303
+ return `workspace package that pnpm does not link unless it is declared with the workspace: protocol${detail}`;
4304
+ case "catalog-unresolved":
4305
+ return `catalog entry could not be resolved${detail}`;
3963
4306
  }
3964
4307
  }
3965
4308
 
@@ -4366,9 +4709,9 @@ function parsePackageArgument(item) {
4366
4709
  }
4367
4710
 
4368
4711
  // src/cli/command-init.ts
4369
- import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2 } from "fs";
4712
+ import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2 } from "fs";
4370
4713
  import { homedir, platform } from "os";
4371
- import { join as join17, resolve as resolve11 } from "path";
4714
+ import { join as join19, resolve as resolve11 } from "path";
4372
4715
  function mcpServerEntry() {
4373
4716
  return {
4374
4717
  command: "npx",
@@ -4414,7 +4757,7 @@ automatic rollback.
4414
4757
  var home = homedir();
4415
4758
  var isWin = platform() === "win32";
4416
4759
  function appData() {
4417
- return process.env.APPDATA || join17(home, "AppData", "Roaming");
4760
+ return process.env.APPDATA || join19(home, "AppData", "Roaming");
4418
4761
  }
4419
4762
  function detectCursor(cwd) {
4420
4763
  const env = {
@@ -4426,25 +4769,25 @@ function detectCursor(cwd) {
4426
4769
  mcpConfigured: false,
4427
4770
  skillConfigured: false
4428
4771
  };
4429
- const workspaceCursorDir = join17(cwd, ".cursor");
4430
- const globalCursorDir = join17(home, ".cursor");
4431
- if (existsSync13(workspaceCursorDir) || existsSync13(globalCursorDir)) {
4772
+ const workspaceCursorDir = join19(cwd, ".cursor");
4773
+ const globalCursorDir = join19(home, ".cursor");
4774
+ if (existsSync14(workspaceCursorDir) || existsSync14(globalCursorDir)) {
4432
4775
  env.detected = true;
4433
- env.reason = existsSync13(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
4776
+ env.reason = existsSync14(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
4434
4777
  }
4435
- const mcpPath = join17(cwd, ".cursor", "mcp.json");
4436
- if (existsSync13(mcpPath)) {
4778
+ const mcpPath = join19(cwd, ".cursor", "mcp.json");
4779
+ if (existsSync14(mcpPath)) {
4437
4780
  try {
4438
- const content = JSON.parse(readFileSync11(mcpPath, "utf8"));
4781
+ const content = JSON.parse(readFileSync13(mcpPath, "utf8"));
4439
4782
  if (content?.mcpServers?.elevate) {
4440
4783
  env.mcpConfigured = true;
4441
4784
  }
4442
4785
  } catch {
4443
4786
  }
4444
4787
  }
4445
- const rulesPath = join17(cwd, ".cursorrules");
4446
- if (existsSync13(rulesPath)) {
4447
- const content = readFileSync11(rulesPath, "utf8");
4788
+ const rulesPath = join19(cwd, ".cursorrules");
4789
+ if (existsSync14(rulesPath)) {
4790
+ const content = readFileSync13(rulesPath, "utf8");
4448
4791
  if (content.includes("elevate") || content.includes("Elevate")) {
4449
4792
  env.skillConfigured = true;
4450
4793
  }
@@ -4461,12 +4804,12 @@ function detectClaudeDesktop() {
4461
4804
  mcpConfigured: false,
4462
4805
  skillConfigured: false
4463
4806
  };
4464
- const configPath = isWin ? join17(appData(), "Claude", "claude_desktop_config.json") : join17(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
4465
- if (existsSync13(configPath)) {
4807
+ const configPath = isWin ? join19(appData(), "Claude", "claude_desktop_config.json") : join19(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
4808
+ if (existsSync14(configPath)) {
4466
4809
  env.detected = true;
4467
4810
  env.reason = `Found ${configPath}`;
4468
4811
  try {
4469
- const content = JSON.parse(readFileSync11(configPath, "utf8"));
4812
+ const content = JSON.parse(readFileSync13(configPath, "utf8"));
4470
4813
  if (content?.mcpServers?.elevate) {
4471
4814
  env.mcpConfigured = true;
4472
4815
  }
@@ -4485,23 +4828,23 @@ function detectClaudeCode(cwd) {
4485
4828
  mcpConfigured: false,
4486
4829
  skillConfigured: false
4487
4830
  };
4488
- const mcpJsonPath = join17(cwd, ".mcp.json");
4489
- const claudeMdPath = join17(cwd, "CLAUDE.md");
4490
- if (existsSync13(mcpJsonPath) || existsSync13(claudeMdPath)) {
4831
+ const mcpJsonPath = join19(cwd, ".mcp.json");
4832
+ const claudeMdPath = join19(cwd, "CLAUDE.md");
4833
+ if (existsSync14(mcpJsonPath) || existsSync14(claudeMdPath)) {
4491
4834
  env.detected = true;
4492
- env.reason = existsSync13(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
4835
+ env.reason = existsSync14(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
4493
4836
  }
4494
- if (existsSync13(mcpJsonPath)) {
4837
+ if (existsSync14(mcpJsonPath)) {
4495
4838
  try {
4496
- const content = JSON.parse(readFileSync11(mcpJsonPath, "utf8"));
4839
+ const content = JSON.parse(readFileSync13(mcpJsonPath, "utf8"));
4497
4840
  if (content?.mcpServers?.elevate) {
4498
4841
  env.mcpConfigured = true;
4499
4842
  }
4500
4843
  } catch {
4501
4844
  }
4502
4845
  }
4503
- if (existsSync13(claudeMdPath)) {
4504
- const content = readFileSync11(claudeMdPath, "utf8");
4846
+ if (existsSync14(claudeMdPath)) {
4847
+ const content = readFileSync13(claudeMdPath, "utf8");
4505
4848
  if (content.includes("elevate") || content.includes("Elevate")) {
4506
4849
  env.skillConfigured = true;
4507
4850
  }
@@ -4518,20 +4861,20 @@ function detectAntigravity(cwd) {
4518
4861
  mcpConfigured: false,
4519
4862
  skillConfigured: false
4520
4863
  };
4521
- const globalGeminiDir = join17(home, ".gemini");
4522
- const workspaceAgentsDir = join17(cwd, ".agents");
4523
- if (existsSync13(globalGeminiDir) || existsSync13(workspaceAgentsDir)) {
4864
+ const globalGeminiDir = join19(home, ".gemini");
4865
+ const workspaceAgentsDir = join19(cwd, ".agents");
4866
+ if (existsSync14(globalGeminiDir) || existsSync14(workspaceAgentsDir)) {
4524
4867
  env.detected = true;
4525
- env.reason = existsSync13(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
4868
+ env.reason = existsSync14(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
4526
4869
  }
4527
4870
  const mcpPaths = [
4528
- join17(cwd, ".agents", "mcp_config.json"),
4529
- join17(home, ".gemini", "config", "mcp_config.json")
4871
+ join19(cwd, ".agents", "mcp_config.json"),
4872
+ join19(home, ".gemini", "config", "mcp_config.json")
4530
4873
  ];
4531
4874
  for (const p of mcpPaths) {
4532
- if (existsSync13(p)) {
4875
+ if (existsSync14(p)) {
4533
4876
  try {
4534
- const content = JSON.parse(readFileSync11(p, "utf8"));
4877
+ const content = JSON.parse(readFileSync13(p, "utf8"));
4535
4878
  if (content?.mcpServers?.elevate) {
4536
4879
  env.mcpConfigured = true;
4537
4880
  break;
@@ -4552,24 +4895,24 @@ function detectVSCode(cwd) {
4552
4895
  mcpConfigured: false,
4553
4896
  skillConfigured: false
4554
4897
  };
4555
- const vscodeDir = join17(cwd, ".vscode");
4556
- if (existsSync13(vscodeDir)) {
4898
+ const vscodeDir = join19(cwd, ".vscode");
4899
+ if (existsSync14(vscodeDir)) {
4557
4900
  env.detected = true;
4558
4901
  env.reason = `Found .vscode/ in workspace`;
4559
4902
  }
4560
- const mcpPath = join17(cwd, ".vscode", "mcp.json");
4561
- if (existsSync13(mcpPath)) {
4903
+ const mcpPath = join19(cwd, ".vscode", "mcp.json");
4904
+ if (existsSync14(mcpPath)) {
4562
4905
  try {
4563
- const content = JSON.parse(readFileSync11(mcpPath, "utf8"));
4906
+ const content = JSON.parse(readFileSync13(mcpPath, "utf8"));
4564
4907
  if (content?.servers?.elevate) {
4565
4908
  env.mcpConfigured = true;
4566
4909
  }
4567
4910
  } catch {
4568
4911
  }
4569
4912
  }
4570
- const copilotInstructions = join17(cwd, ".github", "copilot-instructions.md");
4571
- if (existsSync13(copilotInstructions)) {
4572
- const content = readFileSync11(copilotInstructions, "utf8");
4913
+ const copilotInstructions = join19(cwd, ".github", "copilot-instructions.md");
4914
+ if (existsSync14(copilotInstructions)) {
4915
+ const content = readFileSync13(copilotInstructions, "utf8");
4573
4916
  if (content.includes("elevate") || content.includes("Elevate")) {
4574
4917
  env.skillConfigured = true;
4575
4918
  }
@@ -4586,14 +4929,14 @@ function detectWindsurf() {
4586
4929
  mcpConfigured: false,
4587
4930
  skillConfigured: false
4588
4931
  };
4589
- const configPath = isWin ? join17(appData(), "Codeium", "Windsurf", "mcp_config.json") : join17(home, ".codeium", "windsurf", "mcp_config.json");
4590
- const altPath = join17(home, ".codeium", "windsurf", "mcp_config.json");
4591
- if (existsSync13(configPath) || existsSync13(altPath)) {
4932
+ const configPath = isWin ? join19(appData(), "Codeium", "Windsurf", "mcp_config.json") : join19(home, ".codeium", "windsurf", "mcp_config.json");
4933
+ const altPath = join19(home, ".codeium", "windsurf", "mcp_config.json");
4934
+ if (existsSync14(configPath) || existsSync14(altPath)) {
4592
4935
  env.detected = true;
4593
4936
  env.reason = `Found Windsurf config`;
4594
- const path = existsSync13(configPath) ? configPath : altPath;
4937
+ const path = existsSync14(configPath) ? configPath : altPath;
4595
4938
  try {
4596
- const content = JSON.parse(readFileSync11(path, "utf8"));
4939
+ const content = JSON.parse(readFileSync13(path, "utf8"));
4597
4940
  if (content?.mcpServers?.elevate) {
4598
4941
  env.mcpConfigured = true;
4599
4942
  }
@@ -4604,9 +4947,9 @@ function detectWindsurf() {
4604
4947
  }
4605
4948
  function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
4606
4949
  let existing = {};
4607
- if (existsSync13(filePath)) {
4950
+ if (existsSync14(filePath)) {
4608
4951
  try {
4609
- existing = JSON.parse(readFileSync11(filePath, "utf8"));
4952
+ existing = JSON.parse(readFileSync13(filePath, "utf8"));
4610
4953
  } catch {
4611
4954
  existing = {};
4612
4955
  }
@@ -4618,15 +4961,15 @@ function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
4618
4961
  servers[serverKey] = mcpServerEntry();
4619
4962
  existing[topLevelKey] = servers;
4620
4963
  const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
4621
- if (dir && !existsSync13(dir)) {
4964
+ if (dir && !existsSync14(dir)) {
4622
4965
  mkdirSync2(dir, { recursive: true });
4623
4966
  }
4624
4967
  writeFileSync5(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
4625
4968
  return { written: true, message: `${filePath}: elevate MCP server registered` };
4626
4969
  }
4627
4970
  function appendSkillToFile(filePath, content, marker) {
4628
- if (existsSync13(filePath)) {
4629
- const existing = readFileSync11(filePath, "utf8");
4971
+ if (existsSync14(filePath)) {
4972
+ const existing = readFileSync13(filePath, "utf8");
4630
4973
  if (existing.includes(marker)) {
4631
4974
  return { written: false, message: `${filePath}: skill already present` };
4632
4975
  }
@@ -4634,48 +4977,48 @@ function appendSkillToFile(filePath, content, marker) {
4634
4977
  return { written: true, message: `${filePath}: skill appended` };
4635
4978
  }
4636
4979
  const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
4637
- if (dir && !existsSync13(dir)) {
4980
+ if (dir && !existsSync14(dir)) {
4638
4981
  mkdirSync2(dir, { recursive: true });
4639
4982
  }
4640
4983
  writeFileSync5(filePath, content, "utf8");
4641
4984
  return { written: true, message: `${filePath}: skill created` };
4642
4985
  }
4643
4986
  function configureCursor(cwd, result) {
4644
- const mcpPath = join17(cwd, ".cursor", "mcp.json");
4987
+ const mcpPath = join19(cwd, ".cursor", "mcp.json");
4645
4988
  const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
4646
4989
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Cursor MCP: ${mcp.message}`);
4647
- const rulesPath = join17(cwd, ".cursorrules");
4990
+ const rulesPath = join19(cwd, ".cursorrules");
4648
4991
  const skill = appendSkillToFile(rulesPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
4649
4992
  result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Cursor Rules: ${skill.message}`);
4650
4993
  }
4651
4994
  function configureClaudeDesktop(result) {
4652
- const configPath = isWin ? join17(appData(), "Claude", "claude_desktop_config.json") : join17(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
4995
+ const configPath = isWin ? join19(appData(), "Claude", "claude_desktop_config.json") : join19(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
4653
4996
  const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
4654
4997
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Desktop MCP: ${mcp.message}`);
4655
4998
  }
4656
4999
  function configureClaudeCode(cwd, result) {
4657
- const mcpPath = join17(cwd, ".mcp.json");
5000
+ const mcpPath = join19(cwd, ".mcp.json");
4658
5001
  const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
4659
5002
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code MCP: ${mcp.message}`);
4660
- const claudeMdPath = join17(cwd, "CLAUDE.md");
5003
+ const claudeMdPath = join19(cwd, "CLAUDE.md");
4661
5004
  const skill = appendSkillToFile(claudeMdPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
4662
5005
  result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code Skill: ${skill.message}`);
4663
5006
  }
4664
5007
  function configureAntigravity(cwd, result) {
4665
- const mcpPath = join17(cwd, ".agents", "mcp_config.json");
5008
+ const mcpPath = join19(cwd, ".agents", "mcp_config.json");
4666
5009
  const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
4667
5010
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Antigravity MCP: ${mcp.message}`);
4668
5011
  }
4669
5012
  function configureVSCode(cwd, result) {
4670
- const mcpPath = join17(cwd, ".vscode", "mcp.json");
5013
+ const mcpPath = join19(cwd, ".vscode", "mcp.json");
4671
5014
  const mcp = mergeJsonMcpServer(mcpPath, "elevate", "servers");
4672
5015
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} VS Code MCP: ${mcp.message}`);
4673
- const instructionsPath = join17(cwd, ".github", "copilot-instructions.md");
5016
+ const instructionsPath = join19(cwd, ".github", "copilot-instructions.md");
4674
5017
  const skill = appendSkillToFile(instructionsPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
4675
5018
  result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Copilot Instructions: ${skill.message}`);
4676
5019
  }
4677
5020
  function configureWindsurf(result) {
4678
- const configPath = isWin ? join17(appData(), "Codeium", "Windsurf", "mcp_config.json") : join17(home, ".codeium", "windsurf", "mcp_config.json");
5021
+ const configPath = isWin ? join19(appData(), "Codeium", "Windsurf", "mcp_config.json") : join19(home, ".codeium", "windsurf", "mcp_config.json");
4679
5022
  const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
4680
5023
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Windsurf MCP: ${mcp.message}`);
4681
5024
  }
@@ -4708,7 +5051,7 @@ async function handleInitCommand(options) {
4708
5051
  console.log(` Creating portable .mcp.json for broad compatibility.
4709
5052
  `);
4710
5053
  }
4711
- const mcpPath = join17(cwd, ".mcp.json");
5054
+ const mcpPath = join19(cwd, ".mcp.json");
4712
5055
  const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
4713
5056
  result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Generic MCP: ${mcp.message}`);
4714
5057
  } else {
@@ -4917,8 +5260,10 @@ async function startMcpServer(config) {
4917
5260
  try {
4918
5261
  if (name === "elevate_discover_modules") {
4919
5262
  const modules = await strategy.discovery.discover(config.rootDir);
5263
+ const packageManager = strategy.packageManagerFor?.(config.rootDir);
4920
5264
  return reply({
4921
5265
  ecosystem,
5266
+ ...packageManager ? { packageManager } : {},
4922
5267
  count: modules.length,
4923
5268
  modules: modules.map((m) => ({
4924
5269
  id: m.id,