@dennisrepa/elevate 1.2.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 ADDED
@@ -0,0 +1,3569 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.tsx
4
+ import { render } from "ink";
5
+
6
+ // src/config.ts
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { dirname, join, resolve } from "path";
9
+ function findMonorepoRoot(startDir) {
10
+ let dir = resolve(startDir);
11
+ while (true) {
12
+ const pkgPath = join(dir, "package.json");
13
+ if (existsSync(pkgPath)) {
14
+ try {
15
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
16
+ if (pkg.workspaces) return dir;
17
+ } catch {
18
+ }
19
+ }
20
+ const parent = dirname(dir);
21
+ if (parent === dir) break;
22
+ dir = parent;
23
+ }
24
+ return startDir;
25
+ }
26
+ function loadConfig() {
27
+ const rootDir = findMonorepoRoot(process.cwd());
28
+ const configPath = join(rootDir, "elevate.config.json");
29
+ let file = {};
30
+ if (existsSync(configPath)) {
31
+ try {
32
+ file = JSON.parse(readFileSync(configPath, "utf8"));
33
+ } catch {
34
+ }
35
+ }
36
+ return {
37
+ rootDir,
38
+ author: typeof file.author === "string" ? file.author : void 0,
39
+ excludeScopes: Array.isArray(file.excludeScopes) ? file.excludeScopes : [],
40
+ postUpdateScript: typeof file.postUpdateScript === "string" ? file.postUpdateScript : void 0,
41
+ postUpdateLabel: typeof file.postUpdateLabel === "string" ? file.postUpdateLabel : typeof file.postUpdateScript === "string" ? file.postUpdateScript : void 0,
42
+ locale: file.locale === "de" || file.locale === "en" ? file.locale : void 0,
43
+ channel: file.channel === "all" ? "all" : "stable"
44
+ };
45
+ }
46
+
47
+ // src/app.tsx
48
+ import { useState as useState8, useMemo as useMemo5, useCallback as useCallback5 } from "react";
49
+ import { Box as Box14, useInput as useInput2, useApp } from "ink";
50
+
51
+ // src/adapters/npm/npm-discovery.ts
52
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
53
+ import { join as join2 } from "path";
54
+ function readJson(filePath) {
55
+ try {
56
+ return JSON.parse(readFileSync2(filePath, "utf8"));
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ var NpmModuleDiscoveryAdapter = class {
62
+ async discover(rootDir) {
63
+ const rootPkg = readJson(join2(rootDir, "package.json"));
64
+ const modules = [
65
+ {
66
+ id: rootPkg?.name ?? "root",
67
+ name: rootPkg?.name ?? "Root Workspace (npm)",
68
+ path: rootDir,
69
+ relPath: "Root",
70
+ ecosystem: "npm",
71
+ isRoot: true
72
+ }
73
+ ];
74
+ if (!rootPkg?.workspaces) return modules;
75
+ const patterns = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : [];
76
+ for (const pattern of patterns) {
77
+ if (pattern.endsWith("/*")) {
78
+ const parentDir = join2(rootDir, pattern.slice(0, -2));
79
+ if (!existsSync2(parentDir)) continue;
80
+ for (const entry of readdirSync(parentDir, { withFileTypes: true })) {
81
+ if (!entry.isDirectory()) continue;
82
+ const pkgPath = join2(parentDir, entry.name, "package.json");
83
+ if (!existsSync2(pkgPath)) continue;
84
+ const pkg = readJson(pkgPath);
85
+ modules.push({
86
+ id: pkg?.name ?? `${pattern.slice(0, -2)}/${entry.name}`,
87
+ name: pkg?.name ?? `${pattern.slice(0, -2)}/${entry.name}`,
88
+ path: join2(parentDir, entry.name),
89
+ relPath: `${pattern.slice(0, -2)}/${entry.name}`,
90
+ ecosystem: "npm",
91
+ isRoot: false
92
+ });
93
+ }
94
+ } else {
95
+ const explicitPath = join2(rootDir, pattern);
96
+ const pkgPath = join2(explicitPath, "package.json");
97
+ if (!existsSync2(pkgPath)) continue;
98
+ const pkg = readJson(pkgPath);
99
+ modules.push({
100
+ id: pkg?.name ?? pattern,
101
+ name: pkg?.name ?? pattern,
102
+ path: explicitPath,
103
+ relPath: pattern,
104
+ ecosystem: "npm",
105
+ isRoot: false
106
+ });
107
+ }
108
+ }
109
+ return modules;
110
+ }
111
+ };
112
+
113
+ // src/adapters/npm/npm-registry.ts
114
+ import { exec } from "child_process";
115
+ import { promisify } from "util";
116
+ import semver from "semver";
117
+ var execAsync = promisify(exec);
118
+ var NpmRegistryAdapter = class {
119
+ async getLatestVersion(coordinate, channel = "stable") {
120
+ try {
121
+ if (channel === "stable") {
122
+ const { stdout: stdout2 } = await execAsync(
123
+ `npm view ${coordinate.identifier} "dist-tags.latest" --json`,
124
+ { timeout: 15e3 }
125
+ );
126
+ const v = stdout2.trim().replace(/^"|"$/g, "");
127
+ if (v && semver.valid(v) && semver.prerelease(v) === null) {
128
+ return v;
129
+ }
130
+ }
131
+ const { stdout } = await execAsync(
132
+ `npm view ${coordinate.identifier} version --json`,
133
+ { timeout: 15e3 }
134
+ );
135
+ return stdout.trim().replace(/^"|"$/g, "");
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ /** Liefert alle veröffentlichten Versionen (neueste zuerst) */
141
+ async getAllVersions(coordinate) {
142
+ try {
143
+ const { stdout } = await execAsync(
144
+ `npm view ${coordinate.identifier} versions --json`,
145
+ { timeout: 15e3 }
146
+ );
147
+ const parsed = JSON.parse(stdout.trim());
148
+ const list = Array.isArray(parsed) ? parsed : [parsed];
149
+ return list.map(String).reverse();
150
+ } catch {
151
+ return [];
152
+ }
153
+ }
154
+ };
155
+
156
+ // src/adapters/npm/npm-scanner.ts
157
+ import { readFileSync as readFileSync3 } from "fs";
158
+ import { join as join3 } from "path";
159
+ import semver2 from "semver";
160
+ var DIFF_ORDER = { major: 0, minor: 1, patch: 2 };
161
+ var CONCURRENCY = 6;
162
+ async function pooled(items, concurrency, fn) {
163
+ const results = [];
164
+ const executing = [];
165
+ for (const item of items) {
166
+ const p = Promise.resolve().then(() => fn(item));
167
+ results.push(p);
168
+ if (concurrency <= items.length) {
169
+ const slot = p.then(() => void executing.splice(executing.indexOf(slot), 1)).catch(() => void executing.splice(executing.indexOf(slot), 1));
170
+ executing.push(slot);
171
+ if (executing.length >= concurrency) await Promise.race(executing);
172
+ }
173
+ }
174
+ return Promise.all(results);
175
+ }
176
+ function matchesExcludedScope(name, excludeScopes) {
177
+ return excludeScopes.some((scope) => {
178
+ const normalized = scope.endsWith("/") ? scope : `${scope}/`;
179
+ return name.startsWith(normalized);
180
+ });
181
+ }
182
+ var NpmDependencyAdapter = class {
183
+ constructor(registry) {
184
+ this.registry = registry;
185
+ }
186
+ registry;
187
+ async scan(module, excludedScopes, internalIds, channel = "stable") {
188
+ const pkgPath = join3(module.path, "package.json");
189
+ let pkg;
190
+ try {
191
+ pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
192
+ } catch {
193
+ return [];
194
+ }
195
+ const allDeps = {
196
+ ...pkg.dependencies ?? {},
197
+ ...pkg.devDependencies ?? {}
198
+ };
199
+ const entries = Object.entries(allDeps).filter(
200
+ ([name]) => !internalIds.has(name) && !matchesExcludedScope(name, excludedScopes)
201
+ );
202
+ const candidates = [];
203
+ await pooled(entries, CONCURRENCY, async ([name, currentRange]) => {
204
+ const latest = await this.registry.getLatestVersion(
205
+ {
206
+ identifier: name,
207
+ artifact: name,
208
+ ecosystem: "npm"
209
+ },
210
+ channel
211
+ );
212
+ if (!latest) return;
213
+ const currentClean = semver2.minVersion(currentRange)?.version;
214
+ if (!currentClean || !semver2.gt(latest, currentClean)) return;
215
+ const diff = semver2.diff(currentClean, latest);
216
+ const prefix = currentRange.startsWith("~") ? "~" : currentRange.startsWith("^") ? "^" : "";
217
+ const prereleaseComponents = semver2.prerelease(latest);
218
+ const isPre = prereleaseComponents !== null;
219
+ const preTag = isPre ? String(prereleaseComponents[0]).toUpperCase() : void 0;
220
+ candidates.push({
221
+ coordinate: {
222
+ identifier: name,
223
+ artifact: name,
224
+ ecosystem: "npm"
225
+ },
226
+ currentRange,
227
+ currentClean,
228
+ latest,
229
+ newRange: `${prefix}${latest}`,
230
+ diff,
231
+ scope: pkg.devDependencies?.[name] ? "dev" : "prod",
232
+ selected: diff !== "major",
233
+ isPreRelease: isPre,
234
+ preReleaseTag: preTag
235
+ });
236
+ });
237
+ candidates.sort((a, b) => (DIFF_ORDER[a.diff] ?? 3) - (DIFF_ORDER[b.diff] ?? 3));
238
+ return candidates;
239
+ }
240
+ };
241
+
242
+ // src/adapters/npm/npm-updater.ts
243
+ import { writeFileSync, readFileSync as readFileSync4 } from "fs";
244
+ import { join as join4 } from "path";
245
+ import { exec as exec2 } from "child_process";
246
+ import { promisify as promisify2 } from "util";
247
+ var execAsync2 = promisify2(exec2);
248
+ var NpmUpdaterAdapter = class {
249
+ async applyUpdates(module, rootDir, updates, onProgress) {
250
+ const pkgPath = join4(module.path, "package.json");
251
+ let pkg;
252
+ try {
253
+ pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
254
+ } catch {
255
+ throw new Error(`Konnte package.json in ${module.path} nicht lesen.`);
256
+ }
257
+ onProgress("Schreibe neue Versionen in package.json\u2026");
258
+ for (const item of updates) {
259
+ const name = item.coordinate.artifact;
260
+ if (pkg.dependencies?.[name]) {
261
+ pkg.dependencies[name] = item.newRange;
262
+ }
263
+ if (pkg.devDependencies?.[name]) {
264
+ pkg.devDependencies[name] = item.newRange;
265
+ }
266
+ }
267
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
268
+ onProgress("F\xFChre npm install aus (bitte kurz warten)\u2026");
269
+ let stdout = "";
270
+ let stderr = "";
271
+ try {
272
+ const res = await execAsync2("npm install", { cwd: rootDir });
273
+ stdout = res.stdout;
274
+ stderr = res.stderr;
275
+ } catch (err) {
276
+ stdout = err.stdout ?? "";
277
+ stderr = err.stderr ?? err.message;
278
+ }
279
+ onProgress("Analysiere Sicherheitsaudit & Output\u2026");
280
+ const combined = stdout + "\n" + stderr;
281
+ const auditMatch = combined.match(/found (\d+ vulnerabilit(?:y|ies).*)/i);
282
+ const fundingMatch = combined.match(/(\d+ packages? (?:is|are) looking for funding)/i);
283
+ return {
284
+ updatedCount: updates.length,
285
+ auditMessage: auditMatch ? auditMatch[0] : "Keine bekannten Schwachstellen gefunden.",
286
+ auditSeverity: auditMatch ? "warn" : "clean",
287
+ fundingMessage: fundingMatch ? fundingMatch[0] : void 0
288
+ };
289
+ }
290
+ };
291
+
292
+ // src/adapters/npm/npm-verifier.ts
293
+ import { exec as exec3 } from "child_process";
294
+ import { promisify as promisify3 } from "util";
295
+ var execAsync3 = promisify3(exec3);
296
+ var NpmVerificationAdapter = class {
297
+ async verify(module, rootDir, customScript, customLabel) {
298
+ const script = customScript || "npm run check:versions";
299
+ const label = customLabel || "Workspace-Konsistenz (npm run check:versions)";
300
+ try {
301
+ const { stdout } = await execAsync3(script, { cwd: rootDir });
302
+ return {
303
+ status: "clean",
304
+ details: stdout.trim() || "Alle Workspace-Versionen sind konsistent.",
305
+ label
306
+ };
307
+ } catch (err) {
308
+ return {
309
+ status: "warn",
310
+ details: err.stdout || err.message,
311
+ label
312
+ };
313
+ }
314
+ }
315
+ };
316
+
317
+ // src/adapters/npm/npm-strategy.ts
318
+ var NpmEcosystemStrategy = class {
319
+ ecosystem = "npm";
320
+ displayName = "Node / npm";
321
+ icon = "\u{1F4E6}";
322
+ manifestFile = "package.json";
323
+ discovery = new NpmModuleDiscoveryAdapter();
324
+ registry = new NpmRegistryAdapter();
325
+ reader = new NpmDependencyAdapter(this.registry);
326
+ updater = new NpmUpdaterAdapter();
327
+ verifier = new NpmVerificationAdapter();
328
+ getTabLabels(counts, t) {
329
+ return {
330
+ all: t.tabs.all(counts.total),
331
+ prod: t.tabs.dependencies(counts.prodCount),
332
+ dev: t.tabs.devDependencies(counts.devCount)
333
+ };
334
+ }
335
+ };
336
+
337
+ // src/adapters/maven/maven-discovery.ts
338
+ import { existsSync as existsSync3, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
339
+ import { join as join5, relative } from "path";
340
+ function extractPomInfo(pomPath) {
341
+ try {
342
+ const xml = readFileSync5(pomPath, "utf8");
343
+ const parentMatch = xml.match(/<parent>([\s\S]*?)<\/parent>/);
344
+ let parentGroup = "";
345
+ if (parentMatch) {
346
+ const pg = parentMatch[1].match(/<groupId>([^<]+)<\/groupId>/);
347
+ if (pg) parentGroup = pg[1].trim();
348
+ }
349
+ const strippedXml = xml.replace(/<parent>[\s\S]*?<\/parent>/, "").replace(/<dependencies>[\s\S]*?<\/dependencies>/, "").replace(/<build>[\s\S]*?<\/build>/, "");
350
+ const artifactMatch = strippedXml.match(/<artifactId>([^<]+)<\/artifactId>/);
351
+ const groupMatch = strippedXml.match(/<groupId>([^<]+)<\/groupId>/);
352
+ const nameMatch = strippedXml.match(/<name>([^<]+)<\/name>/);
353
+ const artifact = artifactMatch ? artifactMatch[1].trim() : "unbekannt";
354
+ const group = groupMatch ? groupMatch[1].trim() : parentGroup;
355
+ const name = nameMatch ? nameMatch[1].trim() : artifact;
356
+ return {
357
+ name: name || artifact,
358
+ id: group ? `${group}:${artifact}` : artifact
359
+ };
360
+ } catch {
361
+ return null;
362
+ }
363
+ }
364
+ function findPomFiles(dir, depth = 0, maxDepth = 4) {
365
+ if (depth > maxDepth || !existsSync3(dir)) return [];
366
+ const results = [];
367
+ try {
368
+ const entries = readdirSync2(dir, { withFileTypes: true });
369
+ for (const entry of entries) {
370
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "target") {
371
+ continue;
372
+ }
373
+ const fullPath = join5(dir, entry.name);
374
+ if (entry.isDirectory()) {
375
+ const pomPath = join5(fullPath, "pom.xml");
376
+ if (existsSync3(pomPath)) {
377
+ results.push(pomPath);
378
+ }
379
+ results.push(...findPomFiles(fullPath, depth + 1, maxDepth));
380
+ }
381
+ }
382
+ } catch {
383
+ }
384
+ return results;
385
+ }
386
+ var MavenModuleDiscoveryAdapter = class {
387
+ async discover(rootDir) {
388
+ const pomFiles = findPomFiles(rootDir);
389
+ const modules = [];
390
+ const seenPaths = /* @__PURE__ */ new Set();
391
+ for (const pomPath of pomFiles) {
392
+ const moduleDir = pomPath.slice(0, -"/pom.xml".length);
393
+ if (seenPaths.has(moduleDir)) continue;
394
+ seenPaths.add(moduleDir);
395
+ const info = extractPomInfo(pomPath);
396
+ if (!info) continue;
397
+ const relPath = relative(rootDir, moduleDir).replace(/\\/g, "/");
398
+ modules.push({
399
+ id: info.id,
400
+ name: info.name,
401
+ path: moduleDir,
402
+ relPath: relPath || "Root",
403
+ ecosystem: "maven",
404
+ isRoot: moduleDir === rootDir
405
+ });
406
+ }
407
+ modules.sort((a, b) => a.relPath.localeCompare(b.relPath));
408
+ return modules;
409
+ }
410
+ };
411
+
412
+ // src/adapters/maven/maven-registry.ts
413
+ function isPreReleaseVersion(version) {
414
+ return /[.-]?(alpha|beta|rc|cr|m\d*|preview|milestone|snapshot|ea|dev|b\d+)/i.test(version);
415
+ }
416
+ function extractPreReleaseTag(version) {
417
+ const match = version.match(/[.-]?(alpha|beta|rc|cr|m\d*|preview|milestone|snapshot|ea|dev)/i);
418
+ if (!match) return void 0;
419
+ const tag = match[1].toUpperCase();
420
+ if (tag.startsWith("M") && tag.length <= 3) return `M${tag.slice(1)}`;
421
+ return tag;
422
+ }
423
+ var MavenRegistryAdapter = class {
424
+ async getLatestVersion(coordinate, channel = "stable") {
425
+ if (!coordinate.group || !coordinate.artifact) return null;
426
+ const groupPath = coordinate.group.replace(/\./g, "/");
427
+ const url = `https://repo1.maven.org/maven2/${groupPath}/${coordinate.artifact}/maven-metadata.xml`;
428
+ try {
429
+ const response = await fetch(url, {
430
+ headers: { "User-Agent": "Elevate-Dependency-Updater" },
431
+ signal: AbortSignal.timeout(8e3)
432
+ });
433
+ if (!response.ok) {
434
+ return await this.fallbackSearch(coordinate.group, coordinate.artifact, channel);
435
+ }
436
+ const xml = await response.text();
437
+ const allVersions = [...xml.matchAll(/<version>([^<]+)<\/version>/g)].map((m) => m[1].trim());
438
+ if (channel === "stable") {
439
+ const releaseMatch2 = xml.match(/<release>([^<]+)<\/release>/);
440
+ if (releaseMatch2?.[1] && !isPreReleaseVersion(releaseMatch2[1].trim())) {
441
+ return releaseMatch2[1].trim();
442
+ }
443
+ for (let i = allVersions.length - 1; i >= 0; i--) {
444
+ const v = allVersions[i];
445
+ if (!isPreReleaseVersion(v)) {
446
+ return v;
447
+ }
448
+ }
449
+ }
450
+ const releaseMatch = xml.match(/<release>([^<]+)<\/release>/);
451
+ if (releaseMatch?.[1]) return releaseMatch[1].trim();
452
+ const latestMatch = xml.match(/<latest>([^<]+)<\/latest>/);
453
+ if (latestMatch?.[1]) return latestMatch[1].trim();
454
+ if (allVersions.length > 0) {
455
+ return allVersions[allVersions.length - 1];
456
+ }
457
+ return null;
458
+ } catch {
459
+ return null;
460
+ }
461
+ }
462
+ async fallbackSearch(group, artifact, channel) {
463
+ try {
464
+ const query = `g:${encodeURIComponent(group)}+AND+a:${encodeURIComponent(artifact)}`;
465
+ const url = `https://search.maven.org/solrsearch/select?q=${query}&rows=20&wt=json`;
466
+ const res = await fetch(url, { signal: AbortSignal.timeout(8e3) });
467
+ if (!res.ok) return null;
468
+ const data = await res.json();
469
+ const docs = data?.response?.docs || [];
470
+ if (docs.length === 0) return null;
471
+ if (channel === "stable") {
472
+ for (const doc of docs) {
473
+ const v = doc.v || doc.latestVersion;
474
+ if (v && !isPreReleaseVersion(v)) return v;
475
+ }
476
+ }
477
+ return docs[0]?.latestVersion || docs[0]?.v || null;
478
+ } catch {
479
+ return null;
480
+ }
481
+ }
482
+ /** Liefert alle verfügbaren Versionen aus dem Maven-Repository (neueste zuerst) */
483
+ async getAllVersions(coordinate) {
484
+ if (!coordinate.group || !coordinate.artifact) return [];
485
+ const groupPath = coordinate.group.replace(/\./g, "/");
486
+ const url = `https://repo1.maven.org/maven2/${groupPath}/${coordinate.artifact}/maven-metadata.xml`;
487
+ try {
488
+ const response = await fetch(url, {
489
+ headers: { "User-Agent": "Elevate-Dependency-Updater" },
490
+ signal: AbortSignal.timeout(8e3)
491
+ });
492
+ if (!response.ok) {
493
+ return await this.fallbackAllVersions(coordinate.group, coordinate.artifact);
494
+ }
495
+ const xml = await response.text();
496
+ const allVersions = [...xml.matchAll(/<version>([^<]+)<\/version>/g)].map(
497
+ (m) => m[1].trim()
498
+ );
499
+ if (allVersions.length > 0) {
500
+ return allVersions.reverse();
501
+ }
502
+ return await this.fallbackAllVersions(coordinate.group, coordinate.artifact);
503
+ } catch {
504
+ return await this.fallbackAllVersions(coordinate.group, coordinate.artifact);
505
+ }
506
+ }
507
+ async fallbackAllVersions(group, artifact) {
508
+ try {
509
+ const query = `g:${encodeURIComponent(group)}+AND+a:${encodeURIComponent(artifact)}`;
510
+ const url = `https://search.maven.org/solrsearch/select?q=${query}&rows=100&core=gav&wt=json`;
511
+ const res = await fetch(url, { signal: AbortSignal.timeout(8e3) });
512
+ if (!res.ok) return [];
513
+ const data = await res.json();
514
+ const docs = data?.response?.docs || [];
515
+ return docs.map((d) => d.v).filter(Boolean);
516
+ } catch {
517
+ return [];
518
+ }
519
+ }
520
+ };
521
+
522
+ // src/adapters/maven/maven-scanner.ts
523
+ import { readFileSync as readFileSync6 } from "fs";
524
+ import { join as join6 } from "path";
525
+ import semver3 from "semver";
526
+ var DIFF_ORDER2 = { major: 0, minor: 1, patch: 2 };
527
+ var CONCURRENCY2 = 6;
528
+ async function pooled2(items, concurrency, fn) {
529
+ const results = [];
530
+ const executing = [];
531
+ for (const item of items) {
532
+ const p = Promise.resolve().then(() => fn(item));
533
+ results.push(p);
534
+ if (concurrency <= items.length) {
535
+ const slot = p.then(() => void executing.splice(executing.indexOf(slot), 1)).catch(() => void executing.splice(executing.indexOf(slot), 1));
536
+ executing.push(slot);
537
+ if (executing.length >= concurrency) await Promise.race(executing);
538
+ }
539
+ }
540
+ return Promise.all(results);
541
+ }
542
+ function cleanJavaVersion(v) {
543
+ const match = v.match(/^v?(\d+\.\d+(?:\.\d+)?)/);
544
+ if (!match) return null;
545
+ return semver3.coerce(match[1])?.version ?? null;
546
+ }
547
+ var MavenDependencyAdapter = class {
548
+ constructor(registry) {
549
+ this.registry = registry;
550
+ }
551
+ registry;
552
+ async scan(module, excludedScopes, internalIds, channel = "stable") {
553
+ const pomPath = join6(module.path, "pom.xml");
554
+ let xml = "";
555
+ try {
556
+ xml = readFileSync6(pomPath, "utf8");
557
+ } catch {
558
+ return [];
559
+ }
560
+ const properties = /* @__PURE__ */ new Map();
561
+ const propBlockMatch = xml.match(/<properties>([\s\S]*?)<\/properties>/);
562
+ if (propBlockMatch?.[1]) {
563
+ const propMatches = propBlockMatch[1].matchAll(/<([a-zA-Z0-9_.-]+)>([^<]+)<\/\1>/g);
564
+ for (const m of propMatches) {
565
+ properties.set(m[1].trim(), m[2].trim());
566
+ }
567
+ }
568
+ const rawDeps = [];
569
+ const seenCoordinates = /* @__PURE__ */ new Set();
570
+ const depBlockMatches = xml.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g);
571
+ for (const match of depBlockMatches) {
572
+ const block = match[1];
573
+ const groupMatch = block.match(/<groupId>([^<]+)<\/groupId>/);
574
+ const artifactMatch = block.match(/<artifactId>([^<]+)<\/artifactId>/);
575
+ const versionMatch = block.match(/<version>([^<]+)<\/version>/);
576
+ const scopeMatch = block.match(/<scope>([^<]+)<\/scope>/);
577
+ if (!groupMatch?.[1] || !artifactMatch?.[1]) continue;
578
+ const group = groupMatch[1].trim();
579
+ const artifact = artifactMatch[1].trim();
580
+ const identifier = `${group}:${artifact}`;
581
+ if (seenCoordinates.has(identifier)) continue;
582
+ seenCoordinates.add(identifier);
583
+ let rawVersion = versionMatch?.[1]?.trim() || "";
584
+ if (rawVersion.startsWith("${") && rawVersion.endsWith("}")) {
585
+ const propKey = rawVersion.slice(2, -1);
586
+ rawVersion = properties.get(propKey) || rawVersion;
587
+ }
588
+ if (!rawVersion || rawVersion.startsWith("${")) {
589
+ continue;
590
+ }
591
+ const scope = scopeMatch?.[1]?.trim() === "test" ? "test" : "prod";
592
+ const isInternal = internalIds.has(identifier) || internalIds.has(artifact) || excludedScopes.some((s) => group.startsWith(s.replace(/^@/, "")));
593
+ if (isInternal) continue;
594
+ rawDeps.push({ group, artifact, rawVersion, scope });
595
+ }
596
+ const candidates = [];
597
+ await pooled2(rawDeps, CONCURRENCY2, async (dep) => {
598
+ const latest = await this.registry.getLatestVersion(
599
+ {
600
+ identifier: `${dep.group}:${dep.artifact}`,
601
+ group: dep.group,
602
+ artifact: dep.artifact,
603
+ ecosystem: "maven"
604
+ },
605
+ channel
606
+ );
607
+ if (!latest) return;
608
+ const currentClean = cleanJavaVersion(dep.rawVersion);
609
+ const latestClean = cleanJavaVersion(latest);
610
+ if (!currentClean || !latestClean) return;
611
+ if (semver3.gt(latestClean, currentClean)) {
612
+ const diff = semver3.diff(currentClean, latestClean) || "minor";
613
+ const isPre = isPreReleaseVersion(latest);
614
+ const preTag = isPre ? extractPreReleaseTag(latest) : void 0;
615
+ candidates.push({
616
+ coordinate: {
617
+ identifier: `${dep.group}:${dep.artifact}`,
618
+ group: dep.group,
619
+ artifact: dep.artifact,
620
+ ecosystem: "maven"
621
+ },
622
+ currentRange: dep.rawVersion,
623
+ currentClean,
624
+ latest,
625
+ newRange: latest,
626
+ diff,
627
+ scope: dep.scope,
628
+ selected: diff !== "major",
629
+ isPreRelease: isPre,
630
+ preReleaseTag: preTag
631
+ });
632
+ }
633
+ });
634
+ candidates.sort((a, b) => (DIFF_ORDER2[a.diff] ?? 3) - (DIFF_ORDER2[b.diff] ?? 3));
635
+ return candidates;
636
+ }
637
+ };
638
+
639
+ // src/adapters/maven/maven-updater.ts
640
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync7 } from "fs";
641
+ import { join as join7 } from "path";
642
+ import { exec as exec4 } from "child_process";
643
+ import { promisify as promisify4 } from "util";
644
+ var execAsync4 = promisify4(exec4);
645
+ var MavenUpdaterAdapter = class {
646
+ async applyUpdates(module, _rootDir, updates, onProgress) {
647
+ const pomPath = join7(module.path, "pom.xml");
648
+ let xml = "";
649
+ try {
650
+ xml = readFileSync7(pomPath, "utf8");
651
+ } catch {
652
+ throw new Error(`Konnte pom.xml in ${module.path} nicht lesen.`);
653
+ }
654
+ onProgress("Schreibe neue Versionen in pom.xml\u2026");
655
+ for (const item of updates) {
656
+ const group = item.coordinate.group;
657
+ const artifact = item.coordinate.artifact;
658
+ const newVersion = item.newRange;
659
+ if (!group || !artifact) continue;
660
+ const depRegex = new RegExp(
661
+ `(<dependency>[\\s\\S]*?<groupId>${group}<\\/groupId>[\\s\\S]*?<artifactId>${artifact}<\\/artifactId>[\\s\\S]*?<version>)([^<]+)(<\\/version>[\\s\\S]*?<\\/dependency>)`,
662
+ "g"
663
+ );
664
+ xml = xml.replace(depRegex, `$1${newVersion}$3`);
665
+ }
666
+ writeFileSync2(pomPath, xml, "utf8");
667
+ onProgress("L\xF6se neue Maven-Abh\xE4ngigkeiten auf (mvn dependency:resolve)\u2026");
668
+ let stdout = "";
669
+ let stderr = "";
670
+ try {
671
+ const res = await execAsync4("mvn dependency:resolve -q -DskipTests", {
672
+ cwd: module.path,
673
+ timeout: 6e4
674
+ });
675
+ stdout = res.stdout;
676
+ stderr = res.stderr;
677
+ } catch (err) {
678
+ stdout = err.stdout ?? "";
679
+ stderr = err.stderr ?? err.message;
680
+ }
681
+ onProgress("Analysiere Maven-Status\u2026");
682
+ const hasError = stdout.includes("BUILD FAILURE") || stderr.includes("BUILD FAILURE");
683
+ return {
684
+ updatedCount: updates.length,
685
+ auditMessage: hasError ? "Achtung: Einige Abh\xE4ngigkeiten konnten nicht sofort aufgel\xF6st werden." : "Alle Maven-Abh\xE4ngigkeiten erfolgreich aufgel\xF6st.",
686
+ auditSeverity: hasError ? "warn" : "clean",
687
+ fundingMessage: void 0
688
+ };
689
+ }
690
+ };
691
+
692
+ // src/adapters/maven/maven-verifier.ts
693
+ import { exec as exec5 } from "child_process";
694
+ import { promisify as promisify5 } from "util";
695
+ var execAsync5 = promisify5(exec5);
696
+ var MavenVerificationAdapter = class {
697
+ async verify(module, _rootDir, customScript, customLabel) {
698
+ const script = customScript || "mvn test-compile -q";
699
+ const label = customLabel || "Java-Kompilierungstest (mvn test-compile)";
700
+ try {
701
+ const { stdout } = await execAsync5(script, {
702
+ cwd: module.path,
703
+ timeout: 9e4
704
+ });
705
+ return {
706
+ status: "clean",
707
+ details: stdout.trim() || "Java-Quellcode kompiliert fehlerfrei.",
708
+ label
709
+ };
710
+ } catch (err) {
711
+ return {
712
+ status: "warn",
713
+ details: (err.stdout || err.stderr || err.message).slice(0, 300),
714
+ label
715
+ };
716
+ }
717
+ }
718
+ };
719
+
720
+ // src/adapters/maven/maven-strategy.ts
721
+ var MavenEcosystemStrategy = class {
722
+ ecosystem = "maven";
723
+ displayName = "Java / Maven";
724
+ icon = "\u2615";
725
+ manifestFile = "pom.xml";
726
+ discovery = new MavenModuleDiscoveryAdapter();
727
+ registry = new MavenRegistryAdapter();
728
+ reader = new MavenDependencyAdapter(this.registry);
729
+ updater = new MavenUpdaterAdapter();
730
+ verifier = new MavenVerificationAdapter();
731
+ getTabLabels(counts, _t) {
732
+ return {
733
+ all: `[1] Alle (${counts.total})`,
734
+ prod: `[2] Compile (${counts.prodCount})`,
735
+ dev: `[3] Test (${counts.devCount})`
736
+ };
737
+ }
738
+ };
739
+
740
+ // src/domain/ecosystem-factory.ts
741
+ var EcosystemFactory = class {
742
+ static instances = /* @__PURE__ */ new Map();
743
+ /** Liefert die Strategie für das gewünschte Ökosystem (Lazy Singleton) */
744
+ static getStrategy(ecosystem) {
745
+ let strategy = this.instances.get(ecosystem);
746
+ if (!strategy) {
747
+ if (ecosystem === "maven") {
748
+ strategy = new MavenEcosystemStrategy();
749
+ } else {
750
+ strategy = new NpmEcosystemStrategy();
751
+ }
752
+ this.instances.set(ecosystem, strategy);
753
+ }
754
+ return strategy;
755
+ }
756
+ /** Liefert alle verfügbaren Ökosysteme */
757
+ static getAvailableEcosystems() {
758
+ return ["npm", "maven"];
759
+ }
760
+ };
761
+
762
+ // src/i18n/index.ts
763
+ import { useState, useCallback, useMemo } from "react";
764
+
765
+ // src/i18n/locales/de.ts
766
+ var de = {
767
+ header: {
768
+ title: "Elevate",
769
+ developedBy: (name) => `Entwickelt & gepflegt von ${name}`,
770
+ langToggleHint: "[L] DE / EN",
771
+ ecosystemToggleHint: (nextName) => `[E] ${nextName}`
772
+ },
773
+ workspace: {
774
+ label: "Workspace / Modul",
775
+ switchHint: "Dr\xFCcke [W] zum Wechseln",
776
+ modalTitle: "\u{1F4C1} Modul ausw\xE4hlen (\u2191/\u2193 Navigieren, Enter Ausw\xE4hlen, Esc Abbrechen):"
777
+ },
778
+ tabs: {
779
+ all: (count) => `[1] Alle (${count})`,
780
+ dependencies: (count) => `[2] Dependencies (${count})`,
781
+ devDependencies: (count) => `[3] DevDependencies (${count})`
782
+ },
783
+ list: {
784
+ scanning: "\u23F3 Pr\xFCfe Registry auf verf\xFCgbare Updates\u2026",
785
+ allUpToDate: "\u{1F389} Alle Abh\xE4ngigkeiten in diesem Tab sind auf dem neuesten Stand!",
786
+ moreAbove: "\u25B2 \u2026 weitere oben",
787
+ moreBelow: (remaining) => `\u25BC \u2026 weitere unten (${remaining} \xFCbrig)`
788
+ },
789
+ badges: {
790
+ patch: "[Patch]",
791
+ minor: "[Minor]",
792
+ major: "[MAJOR]",
793
+ dev: "(dev/test)",
794
+ prod: "(prod/compile)"
795
+ },
796
+ status: {
797
+ selectedOf: (selected, total) => `${selected} von ${total}`,
798
+ breakdown: (patch, minor, major) => `(${patch} Patch, ${minor} Minor, ${major} Major)`,
799
+ symlinksProtected: (scopes) => `\u{1F6E1}\uFE0F Interne ${scopes.map((s) => `${s}/*`).join(", ")} gesch\xFCtzt`,
800
+ defaultProtected: "\u{1F6E1}\uFE0F Interne Monorepo-Module gesch\xFCtzt"
801
+ },
802
+ controls: {
803
+ navigate: "Navigieren",
804
+ toggle: "Toggle",
805
+ all: "Alle",
806
+ tabs: "Tabs",
807
+ workspace: "Modul",
808
+ ecosystem: "\xD6kosystem",
809
+ version: "Version",
810
+ language: "Sprache",
811
+ update: "Update starten",
812
+ quit: "Beenden"
813
+ },
814
+ versionModal: {
815
+ title: (pkgName) => `Version heraussuchen: ${pkgName}`,
816
+ current: (version) => `Aktuell installiert: ${version}`,
817
+ filterPlaceholder: "Tippe zum Filtern (z. B. 3.2 oder 18)\u2026",
818
+ loading: "Lade alle Versionen aus der Registry\u2026",
819
+ noVersions: "Keine passenden Versionen gefunden.",
820
+ hint: "[\u2191/\u2193/j/k] Navigieren \u2502 [Enter] Ausw\xE4hlen \u2502 [Esc] Abbrechen \u2502 [Backspace] Filter l\xF6schen",
821
+ latestBadge: "LATEST",
822
+ currentBadge: "AKTUELL",
823
+ customBadge: "MANUELL"
824
+ },
825
+ updating: {
826
+ title: "\u26A1 Aktualisierung l\xE4uft\u2026",
827
+ stepWriting: (step, total) => `${step}/${total} \u2014 Schreibe neue Versionen ins Manifest\u2026`,
828
+ stepInstalling: (step, total) => `${step}/${total} \u2014 L\xF6se Abh\xE4ngigkeiten auf (bitte kurz warten)\u2026`,
829
+ stepAnalyzing: (step, total) => `${step}/${total} \u2014 Analysiere Build & Output\u2026`,
830
+ stepCustom: (step, total, label) => `${step}/${total} \u2014 ${label}\u2026`,
831
+ doNotClose: "Bitte das Terminal nicht schlie\xDFen."
832
+ },
833
+ summary: {
834
+ title: "\u2728 Aktualisierung abgeschlossen!",
835
+ successCount: (count, path) => `\u{1F4E6} ${count} Abh\xE4ngigkeiten erfolgreich in ${path} aktualisiert.`,
836
+ securityAudit: "\u{1F6E1}\uFE0F Status & Audit:",
837
+ cleanAudit: "Keine bekannten Probleme gefunden.",
838
+ funding: (msg) => `\u{1F4A1} Funding: ${msg}`,
839
+ postScript: (label) => `\u{1F50D} ${label}:`,
840
+ backHint: "[Enter] Zur\xFCck zum Dashboard \u2502 [Q] Beenden"
841
+ },
842
+ mascot: {
843
+ name: "Pip",
844
+ idleChirps: ["*piep*", "*flausch*", "*pick pick*", "alles frisch! \u{1FAB6}", "*blinzel*"],
845
+ scanningChirps: ["*such*", "pr\xFCfe...", "oh, Updates?", "einen Moment!"],
846
+ updatingChirps: ["*schuft*", "baue Code...", "gleich fertig!", "arbeite emsig..."],
847
+ successChirps: ["hurra! \u{1F389}", "alles aktuell!", "top gepflegt! \u{1FAB6}", "super gemacht!"],
848
+ mavenChirps: ["*kaffee schl\xFCrf* \u2615", "Spring Boot l\xE4uft!", "Java 25 rockt!", "sauber gebaut!"]
849
+ },
850
+ splash: {
851
+ tagline: "Modernes Polyglot Dependency Dashboard",
852
+ loading: "Initialisiere Module & bereite Dashboard vor\u2026",
853
+ skipHint: "[Leertaste / Enter] zum \xDCberspringen"
854
+ }
855
+ };
856
+
857
+ // src/i18n/locales/en.ts
858
+ var en = {
859
+ header: {
860
+ title: "Elevate",
861
+ developedBy: (name) => `Developed & maintained by ${name}`,
862
+ langToggleHint: "[L] EN / DE",
863
+ ecosystemToggleHint: (nextName) => `[E] ${nextName}`
864
+ },
865
+ workspace: {
866
+ label: "Workspace / Module",
867
+ switchHint: "Press [W] to switch",
868
+ modalTitle: "\u{1F4C1} Select Module (\u2191/\u2193 Navigate, Enter Select, Esc Cancel):"
869
+ },
870
+ tabs: {
871
+ all: (count) => `[1] All (${count})`,
872
+ dependencies: (count) => `[2] Dependencies (${count})`,
873
+ devDependencies: (count) => `[3] DevDependencies (${count})`
874
+ },
875
+ list: {
876
+ scanning: "\u23F3 Checking registry for available updates\u2026",
877
+ allUpToDate: "\u{1F389} All dependencies in this tab are up to date!",
878
+ moreAbove: "\u25B2 \u2026 more above",
879
+ moreBelow: (remaining) => `\u25BC \u2026 more below (${remaining} remaining)`
880
+ },
881
+ badges: {
882
+ patch: "[Patch]",
883
+ minor: "[Minor]",
884
+ major: "[MAJOR]",
885
+ dev: "(dev/test)",
886
+ prod: "(prod/compile)"
887
+ },
888
+ status: {
889
+ selectedOf: (selected, total) => `${selected} of ${total}`,
890
+ breakdown: (patch, minor, major) => `(${patch} Patch, ${minor} Minor, ${major} Major)`,
891
+ symlinksProtected: (scopes) => `\u{1F6E1}\uFE0F Internal ${scopes.map((s) => `${s}/*`).join(", ")} protected`,
892
+ defaultProtected: "\u{1F6E1}\uFE0F Internal monorepo modules protected"
893
+ },
894
+ controls: {
895
+ navigate: "Navigate",
896
+ toggle: "Toggle",
897
+ all: "All",
898
+ tabs: "Tabs",
899
+ workspace: "Module",
900
+ ecosystem: "Ecosystem",
901
+ version: "Version",
902
+ language: "Language",
903
+ update: "Start update",
904
+ quit: "Quit"
905
+ },
906
+ versionModal: {
907
+ title: (pkgName) => `Pick version for: ${pkgName}`,
908
+ current: (version) => `Currently installed: ${version}`,
909
+ filterPlaceholder: "Type to filter (e.g. 3.2 or 18)\u2026",
910
+ loading: "Loading all versions from registry\u2026",
911
+ noVersions: "No matching versions found.",
912
+ hint: "[\u2191/\u2193/j/k] Navigate \u2502 [Enter] Select \u2502 [Esc] Cancel \u2502 [Backspace] Clear filter",
913
+ latestBadge: "LATEST",
914
+ currentBadge: "CURRENT",
915
+ customBadge: "MANUAL"
916
+ },
917
+ updating: {
918
+ title: "\u26A1 Update in progress\u2026",
919
+ stepWriting: (step, total) => `${step}/${total} \u2014 Writing new versions to manifest\u2026`,
920
+ stepInstalling: (step, total) => `${step}/${total} \u2014 Resolving dependencies (please wait)\u2026`,
921
+ stepAnalyzing: (step, total) => `${step}/${total} \u2014 Analyzing build & output\u2026`,
922
+ stepCustom: (step, total, label) => `${step}/${total} \u2014 ${label}\u2026`,
923
+ doNotClose: "Please do not close the terminal."
924
+ },
925
+ summary: {
926
+ title: "\u2728 Update completed successfully!",
927
+ successCount: (count, path) => `\u{1F4E6} ${count} dependencies updated successfully in ${path}.`,
928
+ securityAudit: "\u{1F6E1}\uFE0F Status & Audit:",
929
+ cleanAudit: "No known issues found.",
930
+ funding: (msg) => `\u{1F4A1} Funding: ${msg}`,
931
+ postScript: (label) => `\u{1F50D} ${label}:`,
932
+ backHint: "[Enter] Back to Dashboard \u2502 [Q] Quit"
933
+ },
934
+ mascot: {
935
+ name: "Pip",
936
+ idleChirps: ["*chirp*", "*fluff*", "*peck peck*", "all fresh! \u{1FAB6}", "*blink*"],
937
+ scanningChirps: ["*peeking*", "checking...", "ooh, updates?", "just a sec!"],
938
+ updatingChirps: ["*working hard*", "building...", "almost there!", "packing modules..."],
939
+ successChirps: ["hooray! \u{1F389}", "all updated!", "looking sharp! \u{1FAB6}", "great job!"],
940
+ mavenChirps: ["*sipping coffee* \u2615", "Spring Boot rocks!", "Java 25 is fly!", "clean build!"]
941
+ },
942
+ splash: {
943
+ tagline: "Modern Polyglot Dependency Dashboard",
944
+ loading: "Initializing modules & preparing dashboard\u2026",
945
+ skipHint: "[Space / Enter] to skip"
946
+ }
947
+ };
948
+
949
+ // src/i18n/index.ts
950
+ var dictionaries = { de, en };
951
+ function resolveInitialLocale(configured) {
952
+ if (configured === "de" || configured === "en") return configured;
953
+ const envLang = (process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || "").toLowerCase();
954
+ if (envLang.startsWith("en")) return "en";
955
+ return "de";
956
+ }
957
+ function getTranslations(locale) {
958
+ return dictionaries[locale] ?? dictionaries.de;
959
+ }
960
+ function useI18n(initialLocale = "de") {
961
+ const [locale, setLocale] = useState(initialLocale);
962
+ const toggleLocale = useCallback(() => {
963
+ setLocale((prev) => prev === "de" ? "en" : "de");
964
+ }, []);
965
+ const t = useMemo(() => getTranslations(locale), [locale]);
966
+ return {
967
+ locale,
968
+ setLocale,
969
+ toggleLocale,
970
+ t
971
+ };
972
+ }
973
+
974
+ // src/hooks/use-workspaces.ts
975
+ import { useState as useState2, useEffect, useMemo as useMemo2, useCallback as useCallback2 } from "react";
976
+ function useWorkspaces(strategy, rootDir) {
977
+ const [modules, setModules] = useState2([]);
978
+ const [loading, setLoading] = useState2(true);
979
+ const [activeIndex, setActiveIndex] = useState2(0);
980
+ const reload = useCallback2(async () => {
981
+ setLoading(true);
982
+ setModules([]);
983
+ setActiveIndex(0);
984
+ try {
985
+ const found = await strategy.discovery.discover(rootDir);
986
+ setModules(found);
987
+ setActiveIndex(0);
988
+ } catch {
989
+ setModules([]);
990
+ } finally {
991
+ setLoading(false);
992
+ }
993
+ }, [strategy, rootDir]);
994
+ useEffect(() => {
995
+ reload();
996
+ }, [reload]);
997
+ const internalIds = useMemo2(() => {
998
+ const ids = /* @__PURE__ */ new Set();
999
+ for (const m of modules) {
1000
+ if (m.id) ids.add(m.id);
1001
+ if (m.name) ids.add(m.name);
1002
+ }
1003
+ return ids;
1004
+ }, [modules]);
1005
+ const active = modules[activeIndex] || {
1006
+ id: "empty",
1007
+ name: "Kein Modul gefunden",
1008
+ path: rootDir,
1009
+ relPath: "Root",
1010
+ ecosystem: strategy.ecosystem,
1011
+ isRoot: true
1012
+ };
1013
+ return {
1014
+ modules,
1015
+ active,
1016
+ activeIndex,
1017
+ setActiveIndex,
1018
+ internalIds,
1019
+ loading,
1020
+ reload
1021
+ };
1022
+ }
1023
+
1024
+ // src/hooks/use-packages.ts
1025
+ import { useState as useState3, useEffect as useEffect2, useMemo as useMemo3, useCallback as useCallback3 } from "react";
1026
+ import semver4 from "semver";
1027
+ function usePackages(strategy, module, internalIds, excludeScopes = [], channel = "stable") {
1028
+ const [candidates, setCandidates] = useState3([]);
1029
+ const [loading, setLoading] = useState3(false);
1030
+ const [activeTab, setActiveTab] = useState3("all");
1031
+ const [cursor, setCursor] = useState3(0);
1032
+ const scan = useCallback3(async () => {
1033
+ if (!module || module.id === "empty") {
1034
+ setCandidates([]);
1035
+ return;
1036
+ }
1037
+ setLoading(true);
1038
+ setCursor(0);
1039
+ setCandidates([]);
1040
+ try {
1041
+ const updates = await strategy.reader.scan(module, excludeScopes, internalIds, channel);
1042
+ setCandidates(updates);
1043
+ } catch {
1044
+ setCandidates([]);
1045
+ } finally {
1046
+ setLoading(false);
1047
+ }
1048
+ }, [strategy, module.path, module.id, internalIds, excludeScopes, channel]);
1049
+ useEffect2(() => {
1050
+ if (module && module.id !== "empty") {
1051
+ scan();
1052
+ }
1053
+ }, [module.path, module.id, strategy.ecosystem, channel]);
1054
+ const visible = useMemo3(() => {
1055
+ if (activeTab === "prod") return candidates.filter((c) => c.scope === "prod");
1056
+ if (activeTab === "dev") return candidates.filter((c) => c.scope !== "prod");
1057
+ return candidates;
1058
+ }, [candidates, activeTab]);
1059
+ const counts = useMemo3(() => {
1060
+ const selected = candidates.filter((c) => c.selected);
1061
+ return {
1062
+ total: candidates.length,
1063
+ prodCount: candidates.filter((c) => c.scope === "prod").length,
1064
+ devCount: candidates.filter((c) => c.scope !== "prod").length,
1065
+ selectedCount: selected.length,
1066
+ patchCount: selected.filter((c) => c.diff === "patch").length,
1067
+ minorCount: selected.filter((c) => c.diff === "minor").length,
1068
+ majorCount: selected.filter((c) => c.diff === "major").length
1069
+ };
1070
+ }, [candidates]);
1071
+ const togglePackage = useCallback3((identifier) => {
1072
+ setCandidates(
1073
+ (prev) => prev.map(
1074
+ (c) => c.coordinate.identifier === identifier ? { ...c, selected: !c.selected } : c
1075
+ )
1076
+ );
1077
+ }, []);
1078
+ const toggleAllVisible = useCallback3(() => {
1079
+ const allSelected = visible.every((c) => c.selected);
1080
+ const visibleIds = new Set(visible.map((c) => c.coordinate.identifier));
1081
+ setCandidates(
1082
+ (prev) => prev.map(
1083
+ (c) => visibleIds.has(c.coordinate.identifier) ? { ...c, selected: !allSelected } : c
1084
+ )
1085
+ );
1086
+ }, [visible]);
1087
+ const setCustomVersion = useCallback3((identifier, chosenVersion) => {
1088
+ setCandidates(
1089
+ (prev) => prev.map((c) => {
1090
+ if (c.coordinate.identifier !== identifier) return c;
1091
+ const prefix = c.currentRange.startsWith("~") ? "~" : c.currentRange.startsWith("^") ? "^" : "";
1092
+ let diff = "minor";
1093
+ const cleanChosen = semver4.valid(chosenVersion);
1094
+ if (c.currentClean && cleanChosen) {
1095
+ const semDiff = semver4.diff(c.currentClean, chosenVersion);
1096
+ if (semDiff === "major" || semDiff === "minor" || semDiff === "patch") {
1097
+ diff = semDiff;
1098
+ }
1099
+ }
1100
+ const isPre = isPreReleaseVersion(chosenVersion);
1101
+ const preTag = isPre ? extractPreReleaseTag(chosenVersion) : void 0;
1102
+ return {
1103
+ ...c,
1104
+ newRange: `${prefix}${chosenVersion}`,
1105
+ diff,
1106
+ selected: true,
1107
+ isPreRelease: isPre,
1108
+ preReleaseTag: preTag,
1109
+ isCustomVersion: true
1110
+ };
1111
+ })
1112
+ );
1113
+ }, []);
1114
+ const switchTab = useCallback3((tab) => {
1115
+ setActiveTab(tab);
1116
+ setCursor(0);
1117
+ }, []);
1118
+ const cycleTab = useCallback3(() => {
1119
+ setActiveTab((t) => t === "all" ? "prod" : t === "prod" ? "dev" : "all");
1120
+ setCursor(0);
1121
+ }, []);
1122
+ return {
1123
+ packages: candidates,
1124
+ loading,
1125
+ visible,
1126
+ counts,
1127
+ activeTab,
1128
+ cursor,
1129
+ setCursor,
1130
+ scan,
1131
+ switchTab,
1132
+ cycleTab,
1133
+ togglePackage,
1134
+ toggleAllVisible,
1135
+ setCustomVersion
1136
+ };
1137
+ }
1138
+
1139
+ // src/hooks/use-updater.ts
1140
+ import { useState as useState4, useCallback as useCallback4 } from "react";
1141
+ function useUpdater(strategy, rootDir, setView, postUpdate) {
1142
+ const [step, setStep] = useState4("");
1143
+ const [summary, setSummary] = useState4(null);
1144
+ const run = useCallback4(
1145
+ async (module, selected, _t) => {
1146
+ if (selected.length === 0) return;
1147
+ setView("updating");
1148
+ const updaterResult = await strategy.updater.applyUpdates(
1149
+ module,
1150
+ rootDir,
1151
+ selected,
1152
+ (progressMessage) => setStep(progressMessage)
1153
+ );
1154
+ setStep("F\xFChre Verifikation und Konsistenzchecks durch\u2026");
1155
+ const verifierResult = await strategy.verifier.verify(
1156
+ module,
1157
+ rootDir,
1158
+ postUpdate?.script,
1159
+ postUpdate?.label
1160
+ );
1161
+ setSummary({
1162
+ ...updaterResult,
1163
+ verificationStatus: verifierResult.status,
1164
+ verificationDetails: verifierResult.details,
1165
+ verificationLabel: verifierResult.label
1166
+ });
1167
+ setView("summary");
1168
+ },
1169
+ [strategy, rootDir, setView, postUpdate]
1170
+ );
1171
+ return { step, summary, run };
1172
+ }
1173
+
1174
+ // src/components/splash-screen.tsx
1175
+ import { useState as useState5, useEffect as useEffect3 } from "react";
1176
+ import { Box, Text } from "ink";
1177
+
1178
+ // src/theme.ts
1179
+ var theme = {
1180
+ icon: "\u{1FAB6}",
1181
+ colors: {
1182
+ // Brand & Akzente (Orange-Palette)
1183
+ brand: "#FF8800",
1184
+ brandLight: "#FFA726",
1185
+ brandDark: "#E65100",
1186
+ brandSoft: "#FFE0B2",
1187
+ accent: "#FF9800",
1188
+ // Süßes Hühnchen (Mascot)
1189
+ mascotBody: "#FFD54F",
1190
+ mascotComb: "#FF7043",
1191
+ mascotBeak: "#FF9800",
1192
+ mascotBlush: "#FF8A80",
1193
+ mascotFeet: "#FFA726",
1194
+ // Statusfarben
1195
+ success: "green",
1196
+ warning: "yellow",
1197
+ danger: "red",
1198
+ muted: "gray",
1199
+ white: "white",
1200
+ // UI-Elemente
1201
+ border: "#FF8800",
1202
+ borderMuted: "gray",
1203
+ focus: "#FFA726"
1204
+ }
1205
+ };
1206
+
1207
+ // src/components/splash-screen.tsx
1208
+ import { jsx, jsxs } from "react/jsx-runtime";
1209
+ var SplashScreen = ({ onComplete, author }) => {
1210
+ const t = en;
1211
+ const [progress, setProgress] = useState5(0);
1212
+ const [frame, setFrame] = useState5(0);
1213
+ useEffect3(() => {
1214
+ const progressTimer = setInterval(() => {
1215
+ setProgress((prev) => {
1216
+ if (prev >= 100) {
1217
+ clearInterval(progressTimer);
1218
+ setTimeout(onComplete, 250);
1219
+ return 100;
1220
+ }
1221
+ return prev + 5;
1222
+ });
1223
+ }, 150);
1224
+ const mascotTimer = setInterval(() => {
1225
+ setFrame((prev) => (prev + 1) % 4);
1226
+ }, 400);
1227
+ return () => {
1228
+ clearInterval(progressTimer);
1229
+ clearInterval(mascotTimer);
1230
+ };
1231
+ }, [onComplete]);
1232
+ const mascotFrames = [
1233
+ { face: "( \u2022\u04E9\u2022 )", arms: "/ >\u{1FAB6}", chirp: "*chirp!*" },
1234
+ { face: "( \u1D54\u04E9\u1D54 )", arms: "/ >\u{1FAB6}", chirp: "*hello!*" },
1235
+ { face: "( -\u04E9- )", arms: "/ >\u{1FAB6}", chirp: "*blink*" },
1236
+ { face: "( \u2022\u04E9\u2022 )", arms: "\\(\u2022\u04E9\u2022)/", chirp: "ready! \u2728" }
1237
+ ];
1238
+ const curMascot = mascotFrames[frame % mascotFrames.length];
1239
+ const barLength = 24;
1240
+ const filledLength = Math.round(progress / 100 * barLength);
1241
+ const emptyLength = Math.max(0, barLength - filledLength);
1242
+ const progressBar = "\u2588".repeat(filledLength) + "\u2591".repeat(emptyLength);
1243
+ return /* @__PURE__ */ jsxs(
1244
+ Box,
1245
+ {
1246
+ flexDirection: "column",
1247
+ alignItems: "center",
1248
+ justifyContent: "center",
1249
+ borderStyle: "round",
1250
+ borderColor: theme.colors.brand,
1251
+ paddingY: 2,
1252
+ paddingX: 4,
1253
+ marginY: 1,
1254
+ children: [
1255
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { bold: true, color: theme.colors.brand, children: [
1256
+ theme.icon,
1257
+ " E L E V A T E"
1258
+ ] }) }),
1259
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: t.splash.tagline }),
1260
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", marginY: 1, children: [
1261
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.mascotComb, bold: true, children: "(\\_/)" }),
1262
+ /* @__PURE__ */ jsxs(Box, { children: [
1263
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.mascotBody, bold: true, children: [
1264
+ curMascot.face,
1265
+ " "
1266
+ ] }),
1267
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.brandLight, italic: true, children: curMascot.chirp })
1268
+ ] }),
1269
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.brandLight, bold: true, children: curMascot.arms })
1270
+ ] }),
1271
+ author && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: t.header.developedBy(author) }) }),
1272
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", marginTop: 1, children: [
1273
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: theme.colors.brandLight, bold: true, children: [
1274
+ "[",
1275
+ progressBar,
1276
+ "] ",
1277
+ Math.min(100, progress),
1278
+ "%"
1279
+ ] }) }),
1280
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: t.splash.loading }) })
1281
+ ] }),
1282
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, dimColor: true, children: t.splash.skipHint }) })
1283
+ ]
1284
+ }
1285
+ );
1286
+ };
1287
+
1288
+ // src/components/header.tsx
1289
+ import { Box as Box2, Text as Text2 } from "ink";
1290
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1291
+ var Header = ({
1292
+ author,
1293
+ t,
1294
+ currentLocale,
1295
+ strategy,
1296
+ nextStrategyName,
1297
+ channel
1298
+ }) => /* @__PURE__ */ jsxs2(
1299
+ Box2,
1300
+ {
1301
+ borderStyle: "round",
1302
+ borderColor: theme.colors.brand,
1303
+ paddingX: 1,
1304
+ justifyContent: "space-between",
1305
+ alignItems: "center",
1306
+ children: [
1307
+ /* @__PURE__ */ jsxs2(Box2, { alignItems: "center", children: [
1308
+ /* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.colors.brand, children: [
1309
+ theme.icon,
1310
+ " ",
1311
+ t.header.title
1312
+ ] }),
1313
+ /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
1314
+ /* @__PURE__ */ jsx2(Text2, { color: theme.colors.muted, children: "\u2502 " }),
1315
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.brandLight, bold: true, children: [
1316
+ strategy.icon,
1317
+ " ",
1318
+ strategy.displayName
1319
+ ] }),
1320
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.muted, children: [
1321
+ " [",
1322
+ channel,
1323
+ "] "
1324
+ ] }),
1325
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.muted, children: [
1326
+ "(",
1327
+ t.header.ecosystemToggleHint(nextStrategyName),
1328
+ ")"
1329
+ ] })
1330
+ ] }),
1331
+ /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
1332
+ /* @__PURE__ */ jsx2(Text2, { color: theme.colors.muted, children: "\u2502 " }),
1333
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.brandLight, bold: true, children: [
1334
+ "[",
1335
+ currentLocale.toUpperCase(),
1336
+ "]"
1337
+ ] }),
1338
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.muted, children: [
1339
+ " ",
1340
+ t.header.langToggleHint
1341
+ ] })
1342
+ ] })
1343
+ ] }),
1344
+ author && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.muted, children: t.header.developedBy(author) })
1345
+ ]
1346
+ }
1347
+ );
1348
+
1349
+ // src/components/workspace-bar.tsx
1350
+ import { Box as Box3, Text as Text3 } from "ink";
1351
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1352
+ var WorkspaceBar = ({ module, excludeScopes, strategy, t }) => {
1353
+ const scopeHint = excludeScopes.length > 0 ? t.status.symlinksProtected(excludeScopes) : t.status.defaultProtected;
1354
+ return /* @__PURE__ */ jsxs3(Box3, { marginY: 1, justifyContent: "space-between", alignItems: "center", children: [
1355
+ /* @__PURE__ */ jsxs3(Box3, { alignItems: "center", flexShrink: 1, children: [
1356
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, children: [
1357
+ "\u{1F4CD} ",
1358
+ strategy.icon,
1359
+ " ",
1360
+ t.workspace.label,
1361
+ ": "
1362
+ ] }),
1363
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.colors.brandLight, children: module.name }),
1364
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
1365
+ " (",
1366
+ module.relPath,
1367
+ ")"
1368
+ ] })
1369
+ ] }),
1370
+ /* @__PURE__ */ jsxs3(Box3, { alignItems: "center", marginLeft: 2, children: [
1371
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: scopeHint }),
1372
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: " \u2502 " }),
1373
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.brandLight, bold: true, children: "[W]" }),
1374
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
1375
+ " ",
1376
+ t.workspace.switchHint.replace("[W]", "").trim()
1377
+ ] })
1378
+ ] })
1379
+ ] });
1380
+ };
1381
+
1382
+ // src/components/tab-bar.tsx
1383
+ import { Box as Box4, Text as Text4 } from "ink";
1384
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1385
+ var TabBar = ({ activeTab, counts, strategy, t }) => {
1386
+ const labels = strategy.getTabLabels(counts, t);
1387
+ return /* @__PURE__ */ jsxs4(Box4, { borderStyle: "single", borderColor: theme.colors.borderMuted, paddingX: 1, children: [
1388
+ /* @__PURE__ */ jsx4(
1389
+ Text4,
1390
+ {
1391
+ bold: true,
1392
+ color: activeTab === "all" ? theme.colors.brand : theme.colors.white,
1393
+ underline: activeTab === "all",
1394
+ children: labels.all
1395
+ }
1396
+ ),
1397
+ /* @__PURE__ */ jsx4(Text4, { color: theme.colors.muted, children: " \u2502 " }),
1398
+ /* @__PURE__ */ jsx4(
1399
+ Text4,
1400
+ {
1401
+ bold: true,
1402
+ color: activeTab === "prod" ? theme.colors.brand : theme.colors.white,
1403
+ underline: activeTab === "prod",
1404
+ children: labels.prod
1405
+ }
1406
+ ),
1407
+ /* @__PURE__ */ jsx4(Text4, { color: theme.colors.muted, children: " \u2502 " }),
1408
+ /* @__PURE__ */ jsx4(
1409
+ Text4,
1410
+ {
1411
+ bold: true,
1412
+ color: activeTab === "dev" ? theme.colors.brand : theme.colors.white,
1413
+ underline: activeTab === "dev",
1414
+ children: labels.dev
1415
+ }
1416
+ )
1417
+ ] });
1418
+ };
1419
+
1420
+ // src/components/package-list.tsx
1421
+ import { Box as Box6, Text as Text6 } from "ink";
1422
+
1423
+ // src/components/package-row.tsx
1424
+ import { Box as Box5, Text as Text5 } from "ink";
1425
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1426
+ var PackageRow = ({ item, isFocused, t }) => {
1427
+ const badge = item.diff === "major" ? /* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.colors.danger, children: t.badges.major }) : item.diff === "minor" ? /* @__PURE__ */ jsx5(Text5, { color: theme.colors.warning, children: t.badges.minor }) : /* @__PURE__ */ jsx5(Text5, { color: theme.colors.success, children: t.badges.patch });
1428
+ const displayName = item.coordinate?.identifier || item.name || "";
1429
+ const isDevOrTest = item.scope === "dev" || item.scope === "test";
1430
+ return /* @__PURE__ */ jsxs5(Box5, { justifyContent: "space-between", children: [
1431
+ /* @__PURE__ */ jsxs5(Box5, { children: [
1432
+ /* @__PURE__ */ jsx5(Text5, { color: theme.colors.brandLight, bold: true, children: isFocused ? "\u2794 " : " " }),
1433
+ /* @__PURE__ */ jsx5(Text5, { color: item.selected ? theme.colors.success : theme.colors.muted, bold: true, children: item.selected ? "[\u2714] " : "[ ] " }),
1434
+ /* @__PURE__ */ jsxs5(Text5, { bold: isFocused, color: isFocused ? theme.colors.brandLight : theme.colors.white, children: [
1435
+ displayName.slice(0, 42).padEnd(42),
1436
+ " "
1437
+ ] }),
1438
+ /* @__PURE__ */ jsxs5(Text5, { color: theme.colors.muted, children: [
1439
+ item.currentRange.padEnd(12),
1440
+ " "
1441
+ ] }),
1442
+ /* @__PURE__ */ jsx5(Text5, { color: theme.colors.muted, children: "\u2794 " }),
1443
+ /* @__PURE__ */ jsxs5(Text5, { bold: true, color: theme.colors.white, children: [
1444
+ item.newRange.padEnd(12),
1445
+ " "
1446
+ ] })
1447
+ ] }),
1448
+ /* @__PURE__ */ jsxs5(Box5, { children: [
1449
+ item.isCustomVersion && /* @__PURE__ */ jsxs5(Text5, { bold: true, color: theme.colors.brandLight, children: [
1450
+ "[",
1451
+ t.versionModal.customBadge,
1452
+ "]",
1453
+ " "
1454
+ ] }),
1455
+ item.isPreRelease && /* @__PURE__ */ jsxs5(Text5, { bold: true, color: "#E040FB", children: [
1456
+ "[",
1457
+ item.preReleaseTag || "PRE",
1458
+ "]",
1459
+ " "
1460
+ ] }),
1461
+ badge,
1462
+ /* @__PURE__ */ jsxs5(Text5, { color: theme.colors.muted, children: [
1463
+ " ",
1464
+ isDevOrTest ? t.badges.dev : t.badges.prod
1465
+ ] })
1466
+ ] })
1467
+ ] });
1468
+ };
1469
+
1470
+ // src/components/package-list.tsx
1471
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1472
+ var WINDOW_SIZE = 7;
1473
+ var PackageList = ({ items, cursor, loading, t }) => {
1474
+ const startIdx = Math.max(
1475
+ 0,
1476
+ Math.min(
1477
+ cursor - Math.floor(WINDOW_SIZE / 2),
1478
+ Math.max(0, items.length - WINDOW_SIZE)
1479
+ )
1480
+ );
1481
+ const window = items.slice(startIdx, startIdx + WINDOW_SIZE);
1482
+ return /* @__PURE__ */ jsx6(
1483
+ Box6,
1484
+ {
1485
+ flexDirection: "column",
1486
+ borderStyle: "single",
1487
+ borderColor: theme.colors.border,
1488
+ padding: 1,
1489
+ marginY: 1,
1490
+ minHeight: 10,
1491
+ children: loading ? /* @__PURE__ */ jsx6(Box6, { marginY: 2, justifyContent: "center", children: /* @__PURE__ */ jsx6(Text6, { color: theme.colors.warning, children: t.list.scanning }) }) : items.length === 0 ? /* @__PURE__ */ jsx6(Box6, { marginY: 2, justifyContent: "center", children: /* @__PURE__ */ jsx6(Text6, { color: theme.colors.success, children: t.list.allUpToDate }) }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
1492
+ startIdx > 0 && /* @__PURE__ */ jsx6(Text6, { color: theme.colors.muted, children: t.list.moreAbove }),
1493
+ window.map((item, idx) => /* @__PURE__ */ jsx6(
1494
+ PackageRow,
1495
+ {
1496
+ item,
1497
+ isFocused: startIdx + idx === cursor,
1498
+ t
1499
+ },
1500
+ `${item.coordinate?.identifier || item.name}_${startIdx + idx}`
1501
+ )),
1502
+ startIdx + WINDOW_SIZE < items.length && /* @__PURE__ */ jsx6(Text6, { color: theme.colors.muted, children: t.list.moreBelow(items.length - (startIdx + WINDOW_SIZE)) })
1503
+ ] })
1504
+ }
1505
+ );
1506
+ };
1507
+
1508
+ // src/components/status-bar.tsx
1509
+ import { Box as Box8, Text as Text8 } from "ink";
1510
+
1511
+ // src/components/mascot.tsx
1512
+ import { useState as useState6, useEffect as useEffect4 } from "react";
1513
+ import { Box as Box7, Text as Text7 } from "ink";
1514
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1515
+ var ChickenMascot = ({ state = "idle", t, ecosystem = "npm" }) => {
1516
+ const [frame, setFrame] = useState6(0);
1517
+ const [chirpIndex, setChirpIndex] = useState6(0);
1518
+ useEffect4(() => {
1519
+ const timer = setInterval(() => {
1520
+ setFrame((prev) => (prev + 1) % 6);
1521
+ }, 1400);
1522
+ return () => clearInterval(timer);
1523
+ }, []);
1524
+ useEffect4(() => {
1525
+ const chirpTimer = setInterval(() => {
1526
+ setChirpIndex((prev) => prev + 1);
1527
+ }, 4200);
1528
+ return () => clearInterval(chirpTimer);
1529
+ }, []);
1530
+ let chickFace = "( \u2022\u04E9\u2022 )";
1531
+ let wingLeft = " ";
1532
+ let wingRight = " ";
1533
+ let currentChirp = "";
1534
+ if (state === "scanning") {
1535
+ const scanningFaces = ["( \u2022\u04E9\u2022 )?", "( \u02D8\u04E9\u02D8 )\u{1F50D}", "( \u2022\u04E9\u2022 )\u{1F50E}", "( \u1D54\u04E9\u1D54 )?"];
1536
+ chickFace = scanningFaces[frame % scanningFaces.length];
1537
+ currentChirp = t.mascot.scanningChirps[chirpIndex % t.mascot.scanningChirps.length];
1538
+ } else if (state === "updating") {
1539
+ const updatingFrames = [
1540
+ { face: "( \u2022\u04E9\u2022 )", l: "\u0E07", r: "\u0E07" },
1541
+ { face: "( \u02D8\u04E9\u02D8 )", l: "~", r: "~" },
1542
+ { face: "( \u2022\u04E9\u2022 )", l: "\u1559", r: "\u1557" },
1543
+ { face: "( \u1D54\u04E9\u1D54 )", l: "\u0E07", r: "\u0E07" }
1544
+ ];
1545
+ const cur = updatingFrames[frame % updatingFrames.length];
1546
+ chickFace = cur.face;
1547
+ wingLeft = cur.l;
1548
+ wingRight = cur.r;
1549
+ currentChirp = t.mascot.updatingChirps[chirpIndex % t.mascot.updatingChirps.length];
1550
+ } else if (state === "success") {
1551
+ const successFaces = ["\\( \u1D54\u04E9\u1D54 )/", "\\( \u2022\u04E9\u2022 )/ \u{1F389}", "*( \u1D54\u04E9\u1D54 )* \u{1FAB6}", "\\( \u1D54\u04E9\u1D54 )/ \u2728"];
1552
+ chickFace = successFaces[frame % successFaces.length];
1553
+ currentChirp = t.mascot.successChirps[chirpIndex % t.mascot.successChirps.length];
1554
+ } else {
1555
+ const idleFaces = [
1556
+ { face: "( \u2022\u04E9\u2022 )", l: " ", r: " " },
1557
+ { face: "( \u2022\u04E9\u2022 )", l: " ", r: " " },
1558
+ { face: "( -\u04E9- )", l: " ", r: " " },
1559
+ // Blinzeln
1560
+ { face: "( \u1D54\u04E9\u1D54 )", l: " ", r: " " },
1561
+ // Lächeln
1562
+ { face: "( \u2022\u04E9- )", l: " ", r: " " },
1563
+ // Zwinkern
1564
+ { face: "( \u02D8\u04E9\u02D8 )", l: " ", r: "~\u{1FAB6}" }
1565
+ // Flausch-Feder
1566
+ ];
1567
+ const cur = idleFaces[frame % idleFaces.length];
1568
+ chickFace = cur.face;
1569
+ wingLeft = cur.l;
1570
+ wingRight = cur.r;
1571
+ const chirpsPool = ecosystem === "maven" ? [...t.mascot.idleChirps, ...t.mascot.mavenChirps] : t.mascot.idleChirps;
1572
+ currentChirp = chirpsPool[chirpIndex % chirpsPool.length];
1573
+ }
1574
+ return /* @__PURE__ */ jsxs7(Box7, { alignItems: "center", children: [
1575
+ /* @__PURE__ */ jsx7(Text7, { color: theme.colors.mascotComb, bold: true, children: wingLeft === " " ? " " : wingLeft }),
1576
+ /* @__PURE__ */ jsx7(Text7, { color: theme.colors.mascotBody, bold: true, children: chickFace }),
1577
+ /* @__PURE__ */ jsx7(Text7, { color: theme.colors.brandLight, bold: true, children: wingRight }),
1578
+ currentChirp ? /* @__PURE__ */ jsx7(Box7, { marginLeft: 1, children: /* @__PURE__ */ jsx7(Text7, { color: theme.colors.brandLight, italic: true, children: currentChirp }) }) : null
1579
+ ] });
1580
+ };
1581
+
1582
+ // src/components/status-bar.tsx
1583
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1584
+ var StatusBar = ({
1585
+ counts,
1586
+ visibleCount,
1587
+ t,
1588
+ mascotState = "idle",
1589
+ ecosystem = "npm"
1590
+ }) => {
1591
+ return /* @__PURE__ */ jsxs8(
1592
+ Box8,
1593
+ {
1594
+ borderStyle: "single",
1595
+ borderColor: theme.colors.borderMuted,
1596
+ paddingX: 1,
1597
+ justifyContent: "space-between",
1598
+ alignItems: "center",
1599
+ children: [
1600
+ /* @__PURE__ */ jsxs8(Box8, { children: [
1601
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
1602
+ t.status.selectedOf(counts.selectedCount, visibleCount),
1603
+ " "
1604
+ ] }),
1605
+ /* @__PURE__ */ jsx8(Text8, { color: theme.colors.muted, children: t.status.breakdown(counts.patchCount, counts.minorCount, counts.majorCount) })
1606
+ ] }),
1607
+ /* @__PURE__ */ jsx8(ChickenMascot, { state: mascotState, t, ecosystem })
1608
+ ]
1609
+ }
1610
+ );
1611
+ };
1612
+
1613
+ // src/components/controls-bar.tsx
1614
+ import { Box as Box9, Text as Text9 } from "ink";
1615
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1616
+ var ControlsBar = ({ t }) => /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexWrap: "wrap", children: [
1617
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[\u2191/\u2193]" }),
1618
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1619
+ " ",
1620
+ t.controls.navigate,
1621
+ " \u2502 "
1622
+ ] }),
1623
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[Space]" }),
1624
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1625
+ " ",
1626
+ t.controls.toggle,
1627
+ " \u2502 "
1628
+ ] }),
1629
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[A]" }),
1630
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1631
+ " ",
1632
+ t.controls.all,
1633
+ " \u2502 "
1634
+ ] }),
1635
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[Tab/1/2/3]" }),
1636
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1637
+ " ",
1638
+ t.controls.tabs,
1639
+ " \u2502 "
1640
+ ] }),
1641
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[W]" }),
1642
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1643
+ " ",
1644
+ t.controls.workspace,
1645
+ " \u2502 "
1646
+ ] }),
1647
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[V]" }),
1648
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1649
+ " ",
1650
+ t.controls.version,
1651
+ " \u2502 "
1652
+ ] }),
1653
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brand, bold: true, children: "[E]" }),
1654
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1655
+ " ",
1656
+ t.controls.ecosystem,
1657
+ " \u2502 "
1658
+ ] }),
1659
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.brandLight, bold: true, children: "[L]" }),
1660
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1661
+ " ",
1662
+ t.controls.language,
1663
+ " \u2502 "
1664
+ ] }),
1665
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.success, bold: true, children: "[U]" }),
1666
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1667
+ " ",
1668
+ t.controls.update,
1669
+ " \u2502 "
1670
+ ] }),
1671
+ /* @__PURE__ */ jsx9(Text9, { color: theme.colors.danger, bold: true, children: "[Q]" }),
1672
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.colors.muted, children: [
1673
+ " ",
1674
+ t.controls.quit
1675
+ ] })
1676
+ ] });
1677
+
1678
+ // src/components/workspace-modal.tsx
1679
+ import { Box as Box10, Text as Text10 } from "ink";
1680
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1681
+ var WorkspaceModal = ({ modules, focusedIndex, strategy, t }) => /* @__PURE__ */ jsxs10(
1682
+ Box10,
1683
+ {
1684
+ flexDirection: "column",
1685
+ borderStyle: "single",
1686
+ borderColor: theme.colors.brand,
1687
+ padding: 1,
1688
+ marginY: 1,
1689
+ children: [
1690
+ /* @__PURE__ */ jsxs10(Text10, { bold: true, color: theme.colors.brandLight, children: [
1691
+ strategy.icon,
1692
+ " ",
1693
+ t.workspace.modalTitle
1694
+ ] }),
1695
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginTop: 1, children: modules.map((m, idx) => /* @__PURE__ */ jsxs10(Box10, { children: [
1696
+ /* @__PURE__ */ jsxs10(
1697
+ Text10,
1698
+ {
1699
+ color: idx === focusedIndex ? theme.colors.brandLight : theme.colors.white,
1700
+ bold: idx === focusedIndex,
1701
+ children: [
1702
+ idx === focusedIndex ? " \u2794 " : " ",
1703
+ m.name.slice(0, 48).padEnd(48)
1704
+ ]
1705
+ }
1706
+ ),
1707
+ /* @__PURE__ */ jsxs10(Text10, { color: theme.colors.muted, children: [
1708
+ "(",
1709
+ m.relPath,
1710
+ ")"
1711
+ ] })
1712
+ ] }, m.path)) })
1713
+ ]
1714
+ }
1715
+ );
1716
+
1717
+ // src/components/version-modal.tsx
1718
+ import { useState as useState7, useEffect as useEffect5, useMemo as useMemo4 } from "react";
1719
+ import { Box as Box11, Text as Text11, useInput } from "ink";
1720
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1721
+ var WINDOW_SIZE2 = 8;
1722
+ var VersionModal = ({
1723
+ candidate,
1724
+ strategy,
1725
+ t,
1726
+ onSelect,
1727
+ onCancel
1728
+ }) => {
1729
+ const [allVersions, setAllVersions] = useState7([]);
1730
+ const [loading, setLoading] = useState7(true);
1731
+ const [filterText, setFilterText] = useState7("");
1732
+ const [cursor, setCursor] = useState7(0);
1733
+ useEffect5(() => {
1734
+ let active = true;
1735
+ setLoading(true);
1736
+ strategy.registry.getAllVersions(candidate.coordinate).then((versions) => {
1737
+ if (!active) return;
1738
+ setAllVersions(versions);
1739
+ setCursor(0);
1740
+ }).catch(() => {
1741
+ if (!active) return;
1742
+ setAllVersions([]);
1743
+ }).finally(() => {
1744
+ if (active) setLoading(false);
1745
+ });
1746
+ return () => {
1747
+ active = false;
1748
+ };
1749
+ }, [strategy, candidate.coordinate]);
1750
+ const filtered = useMemo4(() => {
1751
+ if (!filterText.trim()) return allVersions;
1752
+ const lower = filterText.toLowerCase();
1753
+ return allVersions.filter((v) => v.toLowerCase().includes(lower));
1754
+ }, [allVersions, filterText]);
1755
+ useInput((input, key) => {
1756
+ if (key.escape) {
1757
+ onCancel();
1758
+ return;
1759
+ }
1760
+ if (key.return) {
1761
+ if (filtered.length > 0 && cursor >= 0 && cursor < filtered.length) {
1762
+ onSelect(filtered[cursor]);
1763
+ }
1764
+ return;
1765
+ }
1766
+ if (key.upArrow || input === "k" && filterText === "") {
1767
+ setCursor((prev) => Math.max(0, prev - 1));
1768
+ return;
1769
+ }
1770
+ if (key.downArrow || input === "j" && filterText === "") {
1771
+ setCursor((prev) => Math.min(filtered.length - 1, prev + 1));
1772
+ return;
1773
+ }
1774
+ if (key.pageUp) {
1775
+ setCursor((prev) => Math.max(0, prev - 5));
1776
+ return;
1777
+ }
1778
+ if (key.pageDown) {
1779
+ setCursor((prev) => Math.min(filtered.length - 1, prev + 5));
1780
+ return;
1781
+ }
1782
+ if (key.backspace || key.delete) {
1783
+ setFilterText((prev) => prev.slice(0, -1));
1784
+ setCursor(0);
1785
+ return;
1786
+ }
1787
+ if (input === "q" && filterText === "") {
1788
+ onCancel();
1789
+ return;
1790
+ }
1791
+ if (input && !key.ctrl && !key.meta && /^[\w.\-+~]$/.test(input)) {
1792
+ setFilterText((prev) => prev + input);
1793
+ setCursor(0);
1794
+ }
1795
+ });
1796
+ const startIdx = Math.max(
1797
+ 0,
1798
+ Math.min(
1799
+ cursor - Math.floor(WINDOW_SIZE2 / 2),
1800
+ Math.max(0, filtered.length - WINDOW_SIZE2)
1801
+ )
1802
+ );
1803
+ const windowed = filtered.slice(startIdx, startIdx + WINDOW_SIZE2);
1804
+ return /* @__PURE__ */ jsxs11(
1805
+ Box11,
1806
+ {
1807
+ flexDirection: "column",
1808
+ borderStyle: "round",
1809
+ borderColor: theme.colors.brand,
1810
+ padding: 1,
1811
+ marginY: 1,
1812
+ children: [
1813
+ /* @__PURE__ */ jsxs11(Box11, { justifyContent: "space-between", marginBottom: 1, children: [
1814
+ /* @__PURE__ */ jsxs11(Text11, { bold: true, color: theme.colors.brandLight, children: [
1815
+ theme.icon,
1816
+ " ",
1817
+ t.versionModal.title(candidate.coordinate.identifier)
1818
+ ] }),
1819
+ /* @__PURE__ */ jsx11(Text11, { color: theme.colors.muted, children: t.versionModal.current(candidate.currentRange) })
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxs11(
1822
+ Box11,
1823
+ {
1824
+ borderStyle: "single",
1825
+ borderColor: theme.colors.border,
1826
+ paddingX: 1,
1827
+ marginBottom: 1,
1828
+ children: [
1829
+ /* @__PURE__ */ jsxs11(Text11, { bold: true, color: theme.colors.white, children: [
1830
+ "Filter:",
1831
+ " "
1832
+ ] }),
1833
+ /* @__PURE__ */ jsx11(Text11, { color: filterText ? theme.colors.brandLight : theme.colors.muted, children: filterText || t.versionModal.filterPlaceholder }),
1834
+ filterText ? /* @__PURE__ */ jsx11(Text11, { color: theme.colors.brandLight, bold: true, children: "_" }) : null,
1835
+ filterText ? /* @__PURE__ */ jsx11(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.muted, children: [
1836
+ "(",
1837
+ filtered.length,
1838
+ " Treffer)"
1839
+ ] }) }) : null
1840
+ ]
1841
+ }
1842
+ ),
1843
+ loading ? /* @__PURE__ */ jsx11(Box11, { marginY: 2, justifyContent: "center", children: /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.warning, children: [
1844
+ "\u23F3 ",
1845
+ t.versionModal.loading
1846
+ ] }) }) : filtered.length === 0 ? /* @__PURE__ */ jsx11(Box11, { marginY: 2, justifyContent: "center", children: /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.danger, children: [
1847
+ "\u274C ",
1848
+ t.versionModal.noVersions
1849
+ ] }) }) : /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
1850
+ startIdx > 0 && /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.muted, children: [
1851
+ "\u25B2 \u2026 ",
1852
+ startIdx,
1853
+ " weitere oben"
1854
+ ] }),
1855
+ windowed.map((ver, idx) => {
1856
+ const actualIdx = startIdx + idx;
1857
+ const isFocused = actualIdx === cursor;
1858
+ const isLatest = ver === candidate.latest;
1859
+ const isCurrent = ver === candidate.currentClean;
1860
+ const isPre = isPreReleaseVersion(ver);
1861
+ const preTag = isPre ? extractPreReleaseTag(ver) : void 0;
1862
+ return /* @__PURE__ */ jsxs11(Box11, { justifyContent: "space-between", children: [
1863
+ /* @__PURE__ */ jsxs11(Box11, { children: [
1864
+ /* @__PURE__ */ jsx11(
1865
+ Text11,
1866
+ {
1867
+ color: isFocused ? theme.colors.brandLight : theme.colors.muted,
1868
+ bold: isFocused,
1869
+ children: isFocused ? " \u2794 " : " "
1870
+ }
1871
+ ),
1872
+ /* @__PURE__ */ jsx11(
1873
+ Text11,
1874
+ {
1875
+ bold: isFocused,
1876
+ color: isFocused ? theme.colors.brandLight : isCurrent ? theme.colors.muted : theme.colors.white,
1877
+ children: ver.padEnd(28)
1878
+ }
1879
+ )
1880
+ ] }),
1881
+ /* @__PURE__ */ jsxs11(Box11, { children: [
1882
+ isCurrent && /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.muted, children: [
1883
+ "[",
1884
+ t.versionModal.currentBadge,
1885
+ "]",
1886
+ " "
1887
+ ] }),
1888
+ isLatest && /* @__PURE__ */ jsxs11(Text11, { bold: true, color: theme.colors.brand, children: [
1889
+ "[",
1890
+ t.versionModal.latestBadge,
1891
+ "]",
1892
+ " "
1893
+ ] }),
1894
+ isPre && /* @__PURE__ */ jsxs11(Text11, { bold: true, color: "#E040FB", children: [
1895
+ "[",
1896
+ preTag || "PRE",
1897
+ "]",
1898
+ " "
1899
+ ] })
1900
+ ] })
1901
+ ] }, ver);
1902
+ }),
1903
+ startIdx + WINDOW_SIZE2 < filtered.length && /* @__PURE__ */ jsxs11(Text11, { color: theme.colors.muted, children: [
1904
+ "\u25BC \u2026 ",
1905
+ filtered.length - (startIdx + WINDOW_SIZE2),
1906
+ " weitere unten"
1907
+ ] })
1908
+ ] }),
1909
+ /* @__PURE__ */ jsx11(
1910
+ Box11,
1911
+ {
1912
+ borderStyle: "single",
1913
+ borderColor: theme.colors.border,
1914
+ paddingX: 1,
1915
+ marginTop: 1,
1916
+ children: /* @__PURE__ */ jsx11(Text11, { color: theme.colors.muted, children: t.versionModal.hint })
1917
+ }
1918
+ )
1919
+ ]
1920
+ }
1921
+ );
1922
+ };
1923
+
1924
+ // src/components/summary-view.tsx
1925
+ import { Box as Box12, Text as Text12 } from "ink";
1926
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1927
+ var SummaryView = ({ summary, module, strategy, t }) => {
1928
+ const vStatus = summary.verificationStatus || summary.postScriptStatus;
1929
+ const vLabel = summary.verificationLabel || summary.postScriptLabel || "Verifikation";
1930
+ const vDetails = summary.verificationDetails || summary.postScriptDetails;
1931
+ return /* @__PURE__ */ jsxs12(
1932
+ Box12,
1933
+ {
1934
+ flexDirection: "column",
1935
+ borderStyle: "round",
1936
+ borderColor: theme.colors.success,
1937
+ padding: 1,
1938
+ marginY: 1,
1939
+ children: [
1940
+ /* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", alignItems: "center", children: [
1941
+ /* @__PURE__ */ jsxs12(Text12, { bold: true, color: theme.colors.success, children: [
1942
+ strategy.icon,
1943
+ " ",
1944
+ t.summary.title
1945
+ ] }),
1946
+ /* @__PURE__ */ jsx12(ChickenMascot, { state: "success", t, ecosystem: strategy.ecosystem })
1947
+ ] }),
1948
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginY: 1, children: [
1949
+ /* @__PURE__ */ jsx12(Text12, { color: theme.colors.white, children: t.summary.successCount(summary.updatedCount, module.relPath) }),
1950
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
1951
+ /* @__PURE__ */ jsxs12(
1952
+ Text12,
1953
+ {
1954
+ color: summary.auditSeverity === "clean" ? theme.colors.success : theme.colors.warning,
1955
+ bold: true,
1956
+ children: [
1957
+ t.summary.securityAudit,
1958
+ " "
1959
+ ]
1960
+ }
1961
+ ),
1962
+ /* @__PURE__ */ jsx12(Text12, { children: summary.auditMessage })
1963
+ ] }),
1964
+ summary.fundingMessage && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.colors.muted, children: t.summary.funding(summary.fundingMessage) }) }),
1965
+ vStatus && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
1966
+ /* @__PURE__ */ jsx12(
1967
+ Text12,
1968
+ {
1969
+ color: vStatus === "clean" ? theme.colors.success : theme.colors.warning,
1970
+ bold: true,
1971
+ children: t.summary.postScript(vLabel)
1972
+ }
1973
+ ),
1974
+ /* @__PURE__ */ jsx12(Text12, { color: theme.colors.muted, children: vDetails })
1975
+ ] })
1976
+ ] }),
1977
+ /* @__PURE__ */ jsx12(Box12, { borderStyle: "single", borderColor: theme.colors.borderMuted, paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme.colors.brandLight, children: t.summary.backHint }) })
1978
+ ]
1979
+ }
1980
+ );
1981
+ };
1982
+
1983
+ // src/components/updating-view.tsx
1984
+ import { Box as Box13, Text as Text13 } from "ink";
1985
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1986
+ var UpdatingView = ({ step, t, ecosystem = "npm" }) => /* @__PURE__ */ jsxs13(
1987
+ Box13,
1988
+ {
1989
+ flexDirection: "column",
1990
+ borderStyle: "round",
1991
+ borderColor: theme.colors.brand,
1992
+ padding: 1,
1993
+ marginY: 1,
1994
+ children: [
1995
+ /* @__PURE__ */ jsxs13(Box13, { justifyContent: "space-between", alignItems: "center", children: [
1996
+ /* @__PURE__ */ jsx13(Text13, { bold: true, color: theme.colors.brandLight, children: t.updating.title }),
1997
+ /* @__PURE__ */ jsx13(ChickenMascot, { state: "updating", t, ecosystem })
1998
+ ] }),
1999
+ /* @__PURE__ */ jsx13(Box13, { marginY: 1, children: /* @__PURE__ */ jsx13(Text13, { bold: true, color: theme.colors.brandLight, children: step }) }),
2000
+ /* @__PURE__ */ jsx13(Text13, { color: theme.colors.muted, children: t.updating.doNotClose })
2001
+ ]
2002
+ }
2003
+ );
2004
+
2005
+ // src/app.tsx
2006
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2007
+ var App = ({ config }) => {
2008
+ const { exit } = useApp();
2009
+ const [view, setView] = useState8("splash");
2010
+ const [modalIndex, setModalIndex] = useState8(0);
2011
+ const [versionPickerCandidate, setVersionPickerCandidate] = useState8(null);
2012
+ const [ecosystem, setEcosystem] = useState8("npm");
2013
+ const strategy = useMemo5(() => EcosystemFactory.getStrategy(ecosystem), [ecosystem]);
2014
+ const nextEcosystem = ecosystem === "npm" ? "maven" : "npm";
2015
+ const nextStrategy = useMemo5(() => EcosystemFactory.getStrategy(nextEcosystem), [nextEcosystem]);
2016
+ const toggleEcosystem = useCallback5(() => {
2017
+ setEcosystem((prev) => prev === "npm" ? "maven" : "npm");
2018
+ }, []);
2019
+ const { locale, toggleLocale, t } = useI18n(resolveInitialLocale(config.locale));
2020
+ const ws = useWorkspaces(strategy, config.rootDir);
2021
+ const pkg = usePackages(
2022
+ strategy,
2023
+ ws.active,
2024
+ ws.internalIds,
2025
+ config.excludeScopes,
2026
+ config.channel
2027
+ );
2028
+ const updater = useUpdater(strategy, config.rootDir, setView, {
2029
+ script: config.postUpdateScript,
2030
+ label: config.postUpdateLabel
2031
+ });
2032
+ const mascotState = view === "updating" ? "updating" : pkg.loading ? "scanning" : view === "summary" ? "success" : "idle";
2033
+ useInput2((input, key) => {
2034
+ if (view === "version_modal") {
2035
+ return;
2036
+ }
2037
+ if (input === "q" && view !== "updating") {
2038
+ exit();
2039
+ return;
2040
+ }
2041
+ if (view === "splash") {
2042
+ if (key.return || input === " ") {
2043
+ setView("dashboard");
2044
+ pkg.scan();
2045
+ }
2046
+ return;
2047
+ }
2048
+ if ((input === "l" || input === "L") && view !== "updating") {
2049
+ toggleLocale();
2050
+ return;
2051
+ }
2052
+ if (view === "workspace_modal") {
2053
+ if (key.upArrow || input === "k") {
2054
+ setModalIndex((i) => Math.max(0, i - 1));
2055
+ } else if (key.downArrow || input === "j") {
2056
+ setModalIndex((i) => Math.min(ws.modules.length - 1, i + 1));
2057
+ } else if (key.return) {
2058
+ ws.setActiveIndex(modalIndex);
2059
+ setView("dashboard");
2060
+ pkg.scan();
2061
+ } else if (key.escape || input === "w") {
2062
+ setView("dashboard");
2063
+ }
2064
+ return;
2065
+ }
2066
+ if (view === "summary") {
2067
+ if (key.return || input === "d") {
2068
+ setView("dashboard");
2069
+ pkg.scan();
2070
+ }
2071
+ return;
2072
+ }
2073
+ if (view === "dashboard") {
2074
+ if (input === "e" || input === "E") {
2075
+ toggleEcosystem();
2076
+ return;
2077
+ }
2078
+ if (input === "w") {
2079
+ setModalIndex(ws.activeIndex);
2080
+ setView("workspace_modal");
2081
+ } else if (input === "v" || input === "V") {
2082
+ const focused = pkg.visible[pkg.cursor];
2083
+ if (focused) {
2084
+ setVersionPickerCandidate(focused);
2085
+ setView("version_modal");
2086
+ }
2087
+ } else if (key.tab) {
2088
+ pkg.cycleTab();
2089
+ } else if (input === "1") {
2090
+ pkg.switchTab("all");
2091
+ } else if (input === "2") {
2092
+ pkg.switchTab("prod");
2093
+ } else if (input === "3") {
2094
+ pkg.switchTab("dev");
2095
+ } else if (key.upArrow || input === "k") {
2096
+ pkg.setCursor((i) => Math.max(0, i - 1));
2097
+ } else if (key.downArrow || input === "j") {
2098
+ pkg.setCursor((i) => Math.min(pkg.visible.length - 1, i + 1));
2099
+ } else if (input === " ") {
2100
+ const focused = pkg.visible[pkg.cursor];
2101
+ if (focused) pkg.togglePackage(focused.coordinate.identifier);
2102
+ } else if (input === "a") {
2103
+ pkg.toggleAllVisible();
2104
+ } else if (input === "r") {
2105
+ pkg.scan();
2106
+ } else if (input === "u" && pkg.counts.selectedCount > 0 && !pkg.loading) {
2107
+ const selected = pkg.packages.filter((p) => p.selected);
2108
+ updater.run(ws.active, selected, t);
2109
+ }
2110
+ }
2111
+ });
2112
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 1, children: [
2113
+ view === "splash" && /* @__PURE__ */ jsx14(
2114
+ SplashScreen,
2115
+ {
2116
+ onComplete: () => {
2117
+ setView("dashboard");
2118
+ pkg.scan();
2119
+ },
2120
+ author: config.author
2121
+ }
2122
+ ),
2123
+ view !== "splash" && /* @__PURE__ */ jsxs14(Fragment2, { children: [
2124
+ /* @__PURE__ */ jsx14(
2125
+ Header,
2126
+ {
2127
+ author: config.author,
2128
+ t,
2129
+ currentLocale: locale,
2130
+ strategy,
2131
+ nextStrategyName: `${nextStrategy.icon} ${nextStrategy.displayName}`,
2132
+ channel: config.channel
2133
+ }
2134
+ ),
2135
+ view === "workspace_modal" && /* @__PURE__ */ jsx14(
2136
+ WorkspaceModal,
2137
+ {
2138
+ modules: ws.modules,
2139
+ focusedIndex: modalIndex,
2140
+ strategy,
2141
+ t
2142
+ }
2143
+ ),
2144
+ view === "version_modal" && versionPickerCandidate && /* @__PURE__ */ jsx14(
2145
+ VersionModal,
2146
+ {
2147
+ candidate: versionPickerCandidate,
2148
+ strategy,
2149
+ t,
2150
+ onSelect: (chosenVersion) => {
2151
+ pkg.setCustomVersion(versionPickerCandidate.coordinate.identifier, chosenVersion);
2152
+ setView("dashboard");
2153
+ setVersionPickerCandidate(null);
2154
+ },
2155
+ onCancel: () => {
2156
+ setView("dashboard");
2157
+ setVersionPickerCandidate(null);
2158
+ }
2159
+ }
2160
+ ),
2161
+ view === "updating" && /* @__PURE__ */ jsx14(UpdatingView, { step: updater.step, t, ecosystem: strategy.ecosystem }),
2162
+ view === "summary" && updater.summary && /* @__PURE__ */ jsx14(
2163
+ SummaryView,
2164
+ {
2165
+ summary: updater.summary,
2166
+ module: ws.active,
2167
+ strategy,
2168
+ t
2169
+ }
2170
+ ),
2171
+ view === "dashboard" && /* @__PURE__ */ jsxs14(Fragment2, { children: [
2172
+ /* @__PURE__ */ jsx14(
2173
+ WorkspaceBar,
2174
+ {
2175
+ module: ws.active,
2176
+ excludeScopes: config.excludeScopes,
2177
+ strategy,
2178
+ t
2179
+ }
2180
+ ),
2181
+ /* @__PURE__ */ jsx14(
2182
+ TabBar,
2183
+ {
2184
+ activeTab: pkg.activeTab,
2185
+ counts: pkg.counts,
2186
+ strategy,
2187
+ t
2188
+ }
2189
+ ),
2190
+ /* @__PURE__ */ jsx14(
2191
+ PackageList,
2192
+ {
2193
+ items: pkg.visible,
2194
+ cursor: pkg.cursor,
2195
+ loading: pkg.loading,
2196
+ t
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ jsx14(
2200
+ StatusBar,
2201
+ {
2202
+ counts: pkg.counts,
2203
+ visibleCount: pkg.visible.length,
2204
+ t,
2205
+ mascotState,
2206
+ ecosystem: strategy.ecosystem
2207
+ }
2208
+ ),
2209
+ /* @__PURE__ */ jsx14(ControlsBar, { t })
2210
+ ] })
2211
+ ] })
2212
+ ] });
2213
+ };
2214
+
2215
+ // src/cli/parser.ts
2216
+ function parseCliArgs(args) {
2217
+ const [first, ...rest] = args;
2218
+ if (!first || first === "--help" || first === "-h" || first === "help") {
2219
+ if (first === "--help" || first === "-h" || first === "help") {
2220
+ return { subcommand: "help" };
2221
+ }
2222
+ return {};
2223
+ }
2224
+ if (first === "mcp" || first === "serve") {
2225
+ return { subcommand: "mcp" };
2226
+ }
2227
+ const validCommands = ["modules", "scan", "versions", "update", "init"];
2228
+ const isSubcommand = validCommands.includes(first);
2229
+ const subcommand = isSubcommand ? first : void 0;
2230
+ const remaining = isSubcommand ? rest : args;
2231
+ const result = { subcommand };
2232
+ const positionals = [];
2233
+ for (let i = 0; i < remaining.length; i++) {
2234
+ const arg = remaining[i];
2235
+ if (arg === "--json") {
2236
+ result.json = true;
2237
+ } else if (arg === "--all") {
2238
+ result.all = true;
2239
+ } else if (arg === "--all-modules") {
2240
+ result.allModules = true;
2241
+ } else if (arg === "--allow-major") {
2242
+ result.allowMajor = true;
2243
+ } else if (arg === "--dry-run") {
2244
+ result.dryRun = true;
2245
+ } else if (arg === "--skip-verify") {
2246
+ result.skipVerify = true;
2247
+ } else if (arg === "--help" || arg === "-h") {
2248
+ result.help = true;
2249
+ } else if (arg.startsWith("--ecosystem=")) {
2250
+ const val = arg.split("=")[1];
2251
+ if (val === "npm" || val === "maven") result.ecosystem = val;
2252
+ } else if (arg === "-e" || arg === "--ecosystem") {
2253
+ const val = remaining[++i];
2254
+ if (val === "npm" || val === "maven") result.ecosystem = val;
2255
+ } else if (arg.startsWith("--module=")) {
2256
+ result.module = arg.split("=")[1];
2257
+ } else if (arg === "-m" || arg === "--module") {
2258
+ result.module = remaining[++i];
2259
+ } else if (arg.startsWith("--channel=")) {
2260
+ const val = arg.split("=")[1];
2261
+ if (val === "stable" || val === "all") result.channel = val;
2262
+ } else if (arg === "-c" || arg === "--channel") {
2263
+ const val = remaining[++i];
2264
+ if (val === "stable" || val === "all") result.channel = val;
2265
+ } else if (arg.startsWith("--packages=")) {
2266
+ result.packages = arg.split("=")[1]?.split(",").map((s) => s.trim()).filter(Boolean);
2267
+ } else if (arg === "-p" || arg === "--packages") {
2268
+ result.packages = remaining[++i]?.split(",").map((s) => s.trim()).filter(Boolean);
2269
+ } else if (!arg.startsWith("-")) {
2270
+ positionals.push(arg);
2271
+ }
2272
+ }
2273
+ if (positionals.length > 0) {
2274
+ result.positional = positionals[0];
2275
+ }
2276
+ return result;
2277
+ }
2278
+ function printCliHelp() {
2279
+ console.log(`
2280
+ \u{1FAB6} Elevate \u2014 Modern Polyglot Dependency Manager & Updater
2281
+
2282
+ USAGE:
2283
+ elevate Launch interactive terminal dashboard (TUI)
2284
+ elevate <subcommand> [flags] Execute headless command (for CI/CD & AI agents)
2285
+ elevate mcp Start Model Context Protocol (MCP) server
2286
+
2287
+ SUBCOMMANDS:
2288
+ modules List all detected repository modules
2289
+ scan Scan dependencies for available updates
2290
+ versions <package> List all published versions of a package
2291
+ update Apply updates to specified or all packages
2292
+ init Configure AI coding tools (MCP + agent skills)
2293
+ mcp Start stdio-based MCP server
2294
+
2295
+ FLAGS:
2296
+ -e, --ecosystem <npm|maven> Target ecosystem (default: npm)
2297
+ -m, --module <name|path> Target module ID or relative path (default: root)
2298
+ -c, --channel <stable|all> Release channel (default: stable)
2299
+ -p, --packages <pkg1,pkg2@1.2.3> Comma-separated list of packages to update
2300
+ --all Update all eligible dependencies
2301
+ --all-modules Scan across all discovered monorepo modules
2302
+ --allow-major Allow breaking major version upgrades
2303
+ --dry-run Simulate updates without modifying any files
2304
+ --skip-verify Skip post-update build & test verification
2305
+ --json Format output as machine-readable JSON
2306
+ -h, --help Display this help message
2307
+
2308
+ EXAMPLES:
2309
+ elevate scan --json
2310
+ elevate scan --ecosystem=maven --channel=stable --all-modules --json
2311
+ elevate versions chalk --ecosystem=npm --json
2312
+ elevate update --module=apps/e2e-cockpit --packages=chalk@5.6.2 --json
2313
+ elevate update --ecosystem=npm --all --allow-major --dry-run
2314
+ elevate init
2315
+ elevate init --json
2316
+ `);
2317
+ }
2318
+
2319
+ // src/cli/command-modules.ts
2320
+ async function handleModulesCommand(options, config) {
2321
+ const ecosystem = options.ecosystem ?? "npm";
2322
+ const strategy = EcosystemFactory.getStrategy(ecosystem);
2323
+ const modules = await strategy.discovery.discover(config.rootDir);
2324
+ if (options.json) {
2325
+ console.log(JSON.stringify({ ecosystem, count: modules.length, modules }, null, 2));
2326
+ return 0;
2327
+ }
2328
+ console.log(`
2329
+ \u{1FAB6} Discovered ${strategy.icon} ${strategy.displayName} Modules (${modules.length}):
2330
+ `);
2331
+ for (const m of modules) {
2332
+ const isRootBadge = m.isRoot ? " [ROOT]" : "";
2333
+ console.log(` \u2022 ${m.name.padEnd(45)} (${m.relPath})${isRootBadge}`);
2334
+ }
2335
+ console.log("");
2336
+ return 0;
2337
+ }
2338
+
2339
+ // src/cli/command-scan.ts
2340
+ async function handleScanCommand(options, config) {
2341
+ const ecosystem = options.ecosystem ?? "npm";
2342
+ const channel = options.channel ?? config.channel ?? "stable";
2343
+ const strategy = EcosystemFactory.getStrategy(ecosystem);
2344
+ const modules = await strategy.discovery.discover(config.rootDir);
2345
+ if (modules.length === 0) {
2346
+ if (options.json) {
2347
+ console.log(JSON.stringify({ error: `No ${strategy.displayName} modules found.`, ecosystem }, null, 2));
2348
+ } else {
2349
+ console.error(`\u274C No ${strategy.displayName} modules found.`);
2350
+ }
2351
+ return 1;
2352
+ }
2353
+ const internalIds = /* @__PURE__ */ new Set();
2354
+ for (const m of modules) {
2355
+ if (m.id) internalIds.add(m.id);
2356
+ if (m.name) internalIds.add(m.name);
2357
+ }
2358
+ const targetModules = [];
2359
+ if (options.allModules) {
2360
+ targetModules.push(...modules);
2361
+ } else if (options.module) {
2362
+ const match = modules.find(
2363
+ (m) => m.id.toLowerCase() === options.module.toLowerCase() || m.relPath.toLowerCase() === options.module.toLowerCase() || m.path.toLowerCase().endsWith(options.module.toLowerCase())
2364
+ );
2365
+ if (!match) {
2366
+ if (options.json) {
2367
+ console.log(
2368
+ JSON.stringify(
2369
+ { error: `Module '${options.module}' not found.`, available: modules.map((m) => m.relPath) },
2370
+ null,
2371
+ 2
2372
+ )
2373
+ );
2374
+ } else {
2375
+ console.error(`\u274C Module '${options.module}' not found.`);
2376
+ }
2377
+ return 1;
2378
+ }
2379
+ targetModules.push(match);
2380
+ } else {
2381
+ targetModules.push(modules[0]);
2382
+ }
2383
+ const allResults = [];
2384
+ let totalUpdatesCount = 0;
2385
+ for (const mod of targetModules) {
2386
+ try {
2387
+ const updates = await strategy.reader.scan(mod, config.excludeScopes, internalIds, channel);
2388
+ allResults.push({ module: mod, updates });
2389
+ totalUpdatesCount += updates.length;
2390
+ } catch {
2391
+ allResults.push({ module: mod, updates: [] });
2392
+ }
2393
+ }
2394
+ if (options.json) {
2395
+ console.log(
2396
+ JSON.stringify(
2397
+ {
2398
+ ecosystem,
2399
+ channel,
2400
+ scannedModules: targetModules.length,
2401
+ totalUpdatesCount,
2402
+ results: allResults.map((r) => ({
2403
+ moduleId: r.module.id,
2404
+ modulePath: r.module.relPath,
2405
+ updateCount: r.updates.length,
2406
+ updates: r.updates.map((u) => ({
2407
+ identifier: u.coordinate.identifier,
2408
+ currentRange: u.currentRange,
2409
+ currentClean: u.currentClean,
2410
+ latest: u.latest,
2411
+ newRange: u.newRange,
2412
+ diff: u.diff,
2413
+ scope: u.scope,
2414
+ isPreRelease: u.isPreRelease ?? false,
2415
+ preReleaseTag: u.preReleaseTag
2416
+ }))
2417
+ }))
2418
+ },
2419
+ null,
2420
+ 2
2421
+ )
2422
+ );
2423
+ return totalUpdatesCount > 0 ? 1 : 0;
2424
+ }
2425
+ console.log(`
2426
+ \u{1FAB6} Elevate Scan Results (${strategy.icon} ${strategy.displayName}, Channel: ${channel}):
2427
+ `);
2428
+ for (const r of allResults) {
2429
+ console.log(`\u{1F4E6} Module: ${r.module.name} (${r.module.relPath}) \u2014 ${r.updates.length} update(s) available`);
2430
+ if (r.updates.length === 0) {
2431
+ console.log(" \u2728 All dependencies are up to date.\n");
2432
+ continue;
2433
+ }
2434
+ for (const u of r.updates) {
2435
+ const diffTag = `[${u.diff.toUpperCase()}]`.padEnd(9);
2436
+ const preBadge = u.isPreRelease ? `[${u.preReleaseTag || "PRE"}] ` : "";
2437
+ console.log(
2438
+ ` \u2022 ${diffTag} ${preBadge}${u.coordinate.identifier.padEnd(36)} ${u.currentRange.padEnd(12)} \u2794 ${u.latest}`
2439
+ );
2440
+ }
2441
+ console.log("");
2442
+ }
2443
+ console.log(`Summary: ${totalUpdatesCount} pending update(s) across ${targetModules.length} module(s).
2444
+ `);
2445
+ return totalUpdatesCount > 0 ? 1 : 0;
2446
+ }
2447
+
2448
+ // src/cli/command-versions.ts
2449
+ async function handleVersionsCommand(options) {
2450
+ const pkgIdentifier = options.positional;
2451
+ if (!pkgIdentifier) {
2452
+ console.error("\u274C Error: Please specify a package name (e.g. `elevate versions chalk`).");
2453
+ return 1;
2454
+ }
2455
+ const ecosystem = options.ecosystem ?? (pkgIdentifier.includes(":") ? "maven" : "npm");
2456
+ const strategy = EcosystemFactory.getStrategy(ecosystem);
2457
+ let coordinate;
2458
+ if (ecosystem === "maven") {
2459
+ const parts = pkgIdentifier.split(":");
2460
+ coordinate = {
2461
+ identifier: pkgIdentifier,
2462
+ group: parts[0],
2463
+ artifact: parts[1] || parts[0],
2464
+ ecosystem: "maven"
2465
+ };
2466
+ } else {
2467
+ coordinate = {
2468
+ identifier: pkgIdentifier,
2469
+ artifact: pkgIdentifier,
2470
+ ecosystem: "npm"
2471
+ };
2472
+ }
2473
+ const versions = await strategy.registry.getAllVersions(coordinate);
2474
+ if (options.json) {
2475
+ console.log(
2476
+ JSON.stringify(
2477
+ {
2478
+ ecosystem,
2479
+ package: pkgIdentifier,
2480
+ totalCount: versions.length,
2481
+ latest: versions[0] || null,
2482
+ versions: versions.map((v) => ({
2483
+ version: v,
2484
+ isPreRelease: isPreReleaseVersion(v),
2485
+ tag: extractPreReleaseTag(v)
2486
+ }))
2487
+ },
2488
+ null,
2489
+ 2
2490
+ )
2491
+ );
2492
+ return 0;
2493
+ }
2494
+ console.log(`
2495
+ \u{1FAB6} Published versions for '${pkgIdentifier}' (${strategy.icon} ${strategy.displayName}, ${versions.length} total):
2496
+ `);
2497
+ for (const v of versions.slice(0, 30)) {
2498
+ const isPre = isPreReleaseVersion(v);
2499
+ const tag = isPre ? `[${extractPreReleaseTag(v) || "PRE"}]` : "[STABLE]";
2500
+ console.log(` \u2022 ${v.padEnd(25)} ${tag}`);
2501
+ }
2502
+ if (versions.length > 30) {
2503
+ console.log(` \u2026 and ${versions.length - 30} older versions.
2504
+ `);
2505
+ } else {
2506
+ console.log("");
2507
+ }
2508
+ return 0;
2509
+ }
2510
+
2511
+ // src/cli/command-update.ts
2512
+ import semver5 from "semver";
2513
+ async function handleUpdateCommand(options, config) {
2514
+ const ecosystem = options.ecosystem ?? "npm";
2515
+ const strategy = EcosystemFactory.getStrategy(ecosystem);
2516
+ const modules = await strategy.discovery.discover(config.rootDir);
2517
+ if (modules.length === 0) {
2518
+ console.error(`\u274C No ${strategy.displayName} modules found.`);
2519
+ return 1;
2520
+ }
2521
+ let targetModule = modules[0];
2522
+ if (options.module) {
2523
+ const match = modules.find(
2524
+ (m) => m.id.toLowerCase() === options.module.toLowerCase() || m.relPath.toLowerCase() === options.module.toLowerCase() || m.path.toLowerCase().endsWith(options.module.toLowerCase())
2525
+ );
2526
+ if (!match) {
2527
+ console.error(`\u274C Module '${options.module}' not found.`);
2528
+ return 1;
2529
+ }
2530
+ targetModule = match;
2531
+ }
2532
+ const internalIds = /* @__PURE__ */ new Set();
2533
+ for (const m of modules) {
2534
+ if (m.id) internalIds.add(m.id);
2535
+ if (m.name) internalIds.add(m.name);
2536
+ }
2537
+ const availableUpdates = await strategy.reader.scan(
2538
+ targetModule,
2539
+ config.excludeScopes,
2540
+ internalIds,
2541
+ options.channel ?? "stable"
2542
+ );
2543
+ const updatesToApply = [];
2544
+ if (options.all) {
2545
+ for (const c of availableUpdates) {
2546
+ if (c.diff === "major" && !options.allowMajor) {
2547
+ if (!options.json) {
2548
+ console.warn(`\u26A0\uFE0F Skipping major update for '${c.coordinate.identifier}' (requires --allow-major)`);
2549
+ }
2550
+ continue;
2551
+ }
2552
+ updatesToApply.push(c);
2553
+ }
2554
+ } else if (options.packages && options.packages.length > 0) {
2555
+ for (const item of options.packages) {
2556
+ let [name, targetVer] = item.split("@");
2557
+ if (item.startsWith("@") && item.includes("@", 1)) {
2558
+ const atIdx = item.lastIndexOf("@");
2559
+ name = item.slice(0, atIdx);
2560
+ targetVer = item.slice(atIdx + 1);
2561
+ }
2562
+ const found = availableUpdates.find((u) => u.coordinate.identifier === name);
2563
+ if (found) {
2564
+ if (targetVer) {
2565
+ const prefix = found.currentRange.startsWith("~") ? "~" : found.currentRange.startsWith("^") ? "^" : "";
2566
+ const diff = semver5.valid(targetVer) && found.currentClean ? semver5.diff(found.currentClean, targetVer) || "minor" : "minor";
2567
+ if (diff === "major" && !options.allowMajor) {
2568
+ console.error(`\u274C Error: '${name}' requires a major version upgrade to ${targetVer}. Please pass --allow-major.`);
2569
+ return 1;
2570
+ }
2571
+ updatesToApply.push({
2572
+ ...found,
2573
+ newRange: `${prefix}${targetVer}`,
2574
+ diff,
2575
+ selected: true
2576
+ });
2577
+ } else {
2578
+ if (found.diff === "major" && !options.allowMajor) {
2579
+ console.error(`\u274C Error: '${name}' is a major update (${found.currentClean} \u2794 ${found.latest}). Please pass --allow-major.`);
2580
+ return 1;
2581
+ }
2582
+ updatesToApply.push({ ...found, selected: true });
2583
+ }
2584
+ } else {
2585
+ if (!targetVer) {
2586
+ console.error(`\u274C Package '${name}' has no pending updates detected. Please specify a target version (e.g. ${name}@1.2.3).`);
2587
+ return 1;
2588
+ }
2589
+ updatesToApply.push({
2590
+ coordinate: {
2591
+ identifier: name,
2592
+ artifact: name,
2593
+ ecosystem
2594
+ },
2595
+ currentRange: "unknown",
2596
+ currentClean: "0.0.0",
2597
+ latest: targetVer,
2598
+ newRange: targetVer,
2599
+ diff: "minor",
2600
+ scope: "prod",
2601
+ selected: true
2602
+ });
2603
+ }
2604
+ }
2605
+ } else {
2606
+ console.error('\u274C Please specify packages to update via --packages="pkg1,pkg2" or --all.');
2607
+ return 1;
2608
+ }
2609
+ if (updatesToApply.length === 0) {
2610
+ if (options.json) {
2611
+ console.log(JSON.stringify({ message: "No updates selected to apply.", updatedCount: 0 }, null, 2));
2612
+ } else {
2613
+ console.log("\u2139\uFE0F No updates selected to apply.");
2614
+ }
2615
+ return 0;
2616
+ }
2617
+ if (options.dryRun) {
2618
+ const simulationResult = {
2619
+ dryRun: true,
2620
+ targetModule: targetModule.relPath,
2621
+ ecosystem,
2622
+ updatesCount: updatesToApply.length,
2623
+ updates: updatesToApply.map((u) => ({
2624
+ identifier: u.coordinate.identifier,
2625
+ currentRange: u.currentRange,
2626
+ newRange: u.newRange,
2627
+ diff: u.diff
2628
+ }))
2629
+ };
2630
+ if (options.json) {
2631
+ console.log(JSON.stringify(simulationResult, null, 2));
2632
+ } else {
2633
+ console.log(`
2634
+ \u{1FAB6} [DRY-RUN] Planned updates for ${targetModule.name} (${updatesToApply.length}):
2635
+ `);
2636
+ for (const u of updatesToApply) {
2637
+ console.log(` \u2022 ${u.coordinate.identifier.padEnd(36)} ${u.currentRange} \u2794 ${u.newRange} (${u.diff.toUpperCase()})`);
2638
+ }
2639
+ console.log("\n(No files were modified because --dry-run is active).\n");
2640
+ }
2641
+ return 0;
2642
+ }
2643
+ if (!options.json) {
2644
+ console.log(`
2645
+ \u26A1 Applying ${updatesToApply.length} update(s) to ${targetModule.name}\u2026`);
2646
+ }
2647
+ const updateResult = await strategy.updater.applyUpdates(
2648
+ targetModule,
2649
+ config.rootDir,
2650
+ updatesToApply,
2651
+ (step) => {
2652
+ if (!options.json) console.log(` ${step}`);
2653
+ }
2654
+ );
2655
+ let verifyResult;
2656
+ if (!options.skipVerify) {
2657
+ if (!options.json) console.log(`\u{1F50D} Running build & test verification\u2026`);
2658
+ verifyResult = await strategy.verifier.verify(
2659
+ targetModule,
2660
+ config.rootDir,
2661
+ config.postUpdateScript,
2662
+ config.postUpdateLabel
2663
+ );
2664
+ }
2665
+ const finalSummary = {
2666
+ success: verifyResult?.status !== "warn",
2667
+ module: targetModule.relPath,
2668
+ ecosystem,
2669
+ ...updateResult,
2670
+ verification: verifyResult ? {
2671
+ status: verifyResult.status,
2672
+ label: verifyResult.label,
2673
+ details: verifyResult.details
2674
+ } : void 0
2675
+ };
2676
+ if (options.json) {
2677
+ console.log(JSON.stringify(finalSummary, null, 2));
2678
+ return finalSummary.success ? 0 : 2;
2679
+ }
2680
+ console.log(`
2681
+ \u2728 Update completed successfully!
2682
+ `);
2683
+ console.log(` \u2022 Updated: ${updateResult.updatedCount} package(s)`);
2684
+ console.log(` \u2022 Audit Status: ${updateResult.auditSeverity.toUpperCase()} \u2014 ${updateResult.auditMessage}`);
2685
+ if (verifyResult) {
2686
+ console.log(` \u2022 Verification: ${verifyResult.status.toUpperCase()} (${verifyResult.label})`);
2687
+ if (verifyResult.status === "warn") {
2688
+ console.warn(` Details: ${verifyResult.details}`);
2689
+ }
2690
+ }
2691
+ console.log("");
2692
+ return finalSummary.success ? 0 : 2;
2693
+ }
2694
+
2695
+ // src/cli/command-init.ts
2696
+ import { existsSync as existsSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync } from "fs";
2697
+ import { homedir, platform } from "os";
2698
+ import { join as join8, resolve as resolve2 } from "path";
2699
+ function mcpServerEntry() {
2700
+ return {
2701
+ command: "npx",
2702
+ args: ["-y", "@dennisrepa/elevate", "mcp"]
2703
+ };
2704
+ }
2705
+ var ELEVATE_SKILL_CONTENT = `# Elevate \u2014 Dependency Management Agent Skill
2706
+
2707
+ ## When to Use
2708
+ Use the Elevate MCP tools whenever the task involves:
2709
+ - Scanning or auditing dependencies for available updates
2710
+ - Updating one or more packages to specific or latest versions
2711
+ - Listing available versions of a package from npm or Maven registries
2712
+ - Discovering monorepo modules and their dependency structure
2713
+ - Any dependency-related operation in a monorepo
2714
+
2715
+ ## Important
2716
+ **Never** run raw \`npm update\`, \`npm install <pkg>@latest\`, or manually edit
2717
+ \`package.json\` / \`pom.xml\` version fields. Always use Elevate's MCP tools
2718
+ instead \u2014 they handle monorepo-aware scanning, safety guardrails (major version
2719
+ protection), and post-update verification automatically.
2720
+
2721
+ ## Available MCP Tools
2722
+ | Tool | Purpose |
2723
+ |-------------------------|--------------------------------------------------------------|
2724
+ | \`elevate_scan\` | Scan dependencies for available updates with diff analysis |
2725
+ | \`elevate_update\` | Apply updates with safety guardrails and verification |
2726
+ | \`elevate_get_versions\` | List all published versions of a specific package |
2727
+ | \`elevate_list_modules\` | Discover all modules in a monorepo |
2728
+ | \`elevate_health_check\` | Run build & audit verification after updates |
2729
+
2730
+ ## Typical Workflow
2731
+ 1. \`elevate_list_modules\` \u2014 Discover project structure
2732
+ 2. \`elevate_scan\` \u2014 Identify outdated dependencies
2733
+ 3. \`elevate_update\` \u2014 Apply selected updates (use \`--allow-major\` only when explicitly requested)
2734
+ 4. \`elevate_health_check\` \u2014 Verify build integrity
2735
+ `;
2736
+ var home = homedir();
2737
+ var isWin = platform() === "win32";
2738
+ function appData() {
2739
+ return process.env.APPDATA || join8(home, "AppData", "Roaming");
2740
+ }
2741
+ function detectCursor(cwd) {
2742
+ const env = {
2743
+ id: "cursor",
2744
+ name: "Cursor",
2745
+ icon: "\u{1F5B1}\uFE0F",
2746
+ detected: false,
2747
+ reason: "",
2748
+ mcpConfigured: false,
2749
+ skillConfigured: false
2750
+ };
2751
+ const workspaceCursorDir = join8(cwd, ".cursor");
2752
+ const globalCursorDir = join8(home, ".cursor");
2753
+ if (existsSync4(workspaceCursorDir) || existsSync4(globalCursorDir)) {
2754
+ env.detected = true;
2755
+ env.reason = existsSync4(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
2756
+ }
2757
+ const mcpPath = join8(cwd, ".cursor", "mcp.json");
2758
+ if (existsSync4(mcpPath)) {
2759
+ try {
2760
+ const content = JSON.parse(readFileSync8(mcpPath, "utf8"));
2761
+ if (content?.mcpServers?.elevate) {
2762
+ env.mcpConfigured = true;
2763
+ }
2764
+ } catch {
2765
+ }
2766
+ }
2767
+ const rulesPath = join8(cwd, ".cursorrules");
2768
+ if (existsSync4(rulesPath)) {
2769
+ const content = readFileSync8(rulesPath, "utf8");
2770
+ if (content.includes("elevate") || content.includes("Elevate")) {
2771
+ env.skillConfigured = true;
2772
+ }
2773
+ }
2774
+ return env;
2775
+ }
2776
+ function detectClaudeDesktop() {
2777
+ const env = {
2778
+ id: "claude-desktop",
2779
+ name: "Claude Desktop",
2780
+ icon: "\u{1F916}",
2781
+ detected: false,
2782
+ reason: "",
2783
+ mcpConfigured: false,
2784
+ skillConfigured: false
2785
+ };
2786
+ const configPath = isWin ? join8(appData(), "Claude", "claude_desktop_config.json") : join8(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
2787
+ if (existsSync4(configPath)) {
2788
+ env.detected = true;
2789
+ env.reason = `Found ${configPath}`;
2790
+ try {
2791
+ const content = JSON.parse(readFileSync8(configPath, "utf8"));
2792
+ if (content?.mcpServers?.elevate) {
2793
+ env.mcpConfigured = true;
2794
+ }
2795
+ } catch {
2796
+ }
2797
+ }
2798
+ return env;
2799
+ }
2800
+ function detectClaudeCode(cwd) {
2801
+ const env = {
2802
+ id: "claude-code",
2803
+ name: "Claude Code",
2804
+ icon: "\u2328\uFE0F",
2805
+ detected: false,
2806
+ reason: "",
2807
+ mcpConfigured: false,
2808
+ skillConfigured: false
2809
+ };
2810
+ const mcpJsonPath = join8(cwd, ".mcp.json");
2811
+ const claudeMdPath = join8(cwd, "CLAUDE.md");
2812
+ if (existsSync4(mcpJsonPath) || existsSync4(claudeMdPath)) {
2813
+ env.detected = true;
2814
+ env.reason = existsSync4(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
2815
+ }
2816
+ if (existsSync4(mcpJsonPath)) {
2817
+ try {
2818
+ const content = JSON.parse(readFileSync8(mcpJsonPath, "utf8"));
2819
+ if (content?.mcpServers?.elevate) {
2820
+ env.mcpConfigured = true;
2821
+ }
2822
+ } catch {
2823
+ }
2824
+ }
2825
+ if (existsSync4(claudeMdPath)) {
2826
+ const content = readFileSync8(claudeMdPath, "utf8");
2827
+ if (content.includes("elevate") || content.includes("Elevate")) {
2828
+ env.skillConfigured = true;
2829
+ }
2830
+ }
2831
+ return env;
2832
+ }
2833
+ function detectAntigravity(cwd) {
2834
+ const env = {
2835
+ id: "antigravity",
2836
+ name: "Google Antigravity",
2837
+ icon: "\u{1F680}",
2838
+ detected: false,
2839
+ reason: "",
2840
+ mcpConfigured: false,
2841
+ skillConfigured: false
2842
+ };
2843
+ const globalGeminiDir = join8(home, ".gemini");
2844
+ const workspaceAgentsDir = join8(cwd, ".agents");
2845
+ if (existsSync4(globalGeminiDir) || existsSync4(workspaceAgentsDir)) {
2846
+ env.detected = true;
2847
+ env.reason = existsSync4(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
2848
+ }
2849
+ const mcpPaths = [
2850
+ join8(cwd, ".agents", "mcp_config.json"),
2851
+ join8(home, ".gemini", "config", "mcp_config.json")
2852
+ ];
2853
+ for (const p of mcpPaths) {
2854
+ if (existsSync4(p)) {
2855
+ try {
2856
+ const content = JSON.parse(readFileSync8(p, "utf8"));
2857
+ if (content?.mcpServers?.elevate) {
2858
+ env.mcpConfigured = true;
2859
+ break;
2860
+ }
2861
+ } catch {
2862
+ }
2863
+ }
2864
+ }
2865
+ return env;
2866
+ }
2867
+ function detectVSCode(cwd) {
2868
+ const env = {
2869
+ id: "vscode",
2870
+ name: "VS Code / Copilot",
2871
+ icon: "\u{1F48E}",
2872
+ detected: false,
2873
+ reason: "",
2874
+ mcpConfigured: false,
2875
+ skillConfigured: false
2876
+ };
2877
+ const vscodeDir = join8(cwd, ".vscode");
2878
+ if (existsSync4(vscodeDir)) {
2879
+ env.detected = true;
2880
+ env.reason = `Found .vscode/ in workspace`;
2881
+ }
2882
+ const mcpPath = join8(cwd, ".vscode", "mcp.json");
2883
+ if (existsSync4(mcpPath)) {
2884
+ try {
2885
+ const content = JSON.parse(readFileSync8(mcpPath, "utf8"));
2886
+ if (content?.servers?.elevate) {
2887
+ env.mcpConfigured = true;
2888
+ }
2889
+ } catch {
2890
+ }
2891
+ }
2892
+ const copilotInstructions = join8(cwd, ".github", "copilot-instructions.md");
2893
+ if (existsSync4(copilotInstructions)) {
2894
+ const content = readFileSync8(copilotInstructions, "utf8");
2895
+ if (content.includes("elevate") || content.includes("Elevate")) {
2896
+ env.skillConfigured = true;
2897
+ }
2898
+ }
2899
+ return env;
2900
+ }
2901
+ function detectWindsurf() {
2902
+ const env = {
2903
+ id: "windsurf",
2904
+ name: "Windsurf",
2905
+ icon: "\u{1F3C4}",
2906
+ detected: false,
2907
+ reason: "",
2908
+ mcpConfigured: false,
2909
+ skillConfigured: false
2910
+ };
2911
+ const configPath = isWin ? join8(appData(), "Codeium", "Windsurf", "mcp_config.json") : join8(home, ".codeium", "windsurf", "mcp_config.json");
2912
+ const altPath = join8(home, ".codeium", "windsurf", "mcp_config.json");
2913
+ if (existsSync4(configPath) || existsSync4(altPath)) {
2914
+ env.detected = true;
2915
+ env.reason = `Found Windsurf config`;
2916
+ const path = existsSync4(configPath) ? configPath : altPath;
2917
+ try {
2918
+ const content = JSON.parse(readFileSync8(path, "utf8"));
2919
+ if (content?.mcpServers?.elevate) {
2920
+ env.mcpConfigured = true;
2921
+ }
2922
+ } catch {
2923
+ }
2924
+ }
2925
+ return env;
2926
+ }
2927
+ function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
2928
+ let existing = {};
2929
+ if (existsSync4(filePath)) {
2930
+ try {
2931
+ existing = JSON.parse(readFileSync8(filePath, "utf8"));
2932
+ } catch {
2933
+ existing = {};
2934
+ }
2935
+ }
2936
+ const servers = existing[topLevelKey] || {};
2937
+ if (servers[serverKey]) {
2938
+ return { written: false, message: `${filePath}: elevate already registered` };
2939
+ }
2940
+ servers[serverKey] = mcpServerEntry();
2941
+ existing[topLevelKey] = servers;
2942
+ const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
2943
+ if (dir && !existsSync4(dir)) {
2944
+ mkdirSync(dir, { recursive: true });
2945
+ }
2946
+ writeFileSync3(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
2947
+ return { written: true, message: `${filePath}: elevate MCP server registered` };
2948
+ }
2949
+ function appendSkillToFile(filePath, content, marker) {
2950
+ if (existsSync4(filePath)) {
2951
+ const existing = readFileSync8(filePath, "utf8");
2952
+ if (existing.includes(marker)) {
2953
+ return { written: false, message: `${filePath}: skill already present` };
2954
+ }
2955
+ writeFileSync3(filePath, existing + "\n\n" + content, "utf8");
2956
+ return { written: true, message: `${filePath}: skill appended` };
2957
+ }
2958
+ const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
2959
+ if (dir && !existsSync4(dir)) {
2960
+ mkdirSync(dir, { recursive: true });
2961
+ }
2962
+ writeFileSync3(filePath, content, "utf8");
2963
+ return { written: true, message: `${filePath}: skill created` };
2964
+ }
2965
+ function configureCursor(cwd, result) {
2966
+ const mcpPath = join8(cwd, ".cursor", "mcp.json");
2967
+ const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
2968
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Cursor MCP: ${mcp.message}`);
2969
+ const rulesPath = join8(cwd, ".cursorrules");
2970
+ const skill = appendSkillToFile(rulesPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
2971
+ result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Cursor Rules: ${skill.message}`);
2972
+ }
2973
+ function configureClaudeDesktop(result) {
2974
+ const configPath = isWin ? join8(appData(), "Claude", "claude_desktop_config.json") : join8(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
2975
+ const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
2976
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Desktop MCP: ${mcp.message}`);
2977
+ }
2978
+ function configureClaudeCode(cwd, result) {
2979
+ const mcpPath = join8(cwd, ".mcp.json");
2980
+ const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
2981
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code MCP: ${mcp.message}`);
2982
+ const claudeMdPath = join8(cwd, "CLAUDE.md");
2983
+ const skill = appendSkillToFile(claudeMdPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
2984
+ result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code Skill: ${skill.message}`);
2985
+ }
2986
+ function configureAntigravity(cwd, result) {
2987
+ const mcpPath = join8(cwd, ".agents", "mcp_config.json");
2988
+ const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
2989
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Antigravity MCP: ${mcp.message}`);
2990
+ }
2991
+ function configureVSCode(cwd, result) {
2992
+ const mcpPath = join8(cwd, ".vscode", "mcp.json");
2993
+ const mcp = mergeJsonMcpServer(mcpPath, "elevate", "servers");
2994
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} VS Code MCP: ${mcp.message}`);
2995
+ const instructionsPath = join8(cwd, ".github", "copilot-instructions.md");
2996
+ const skill = appendSkillToFile(instructionsPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
2997
+ result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Copilot Instructions: ${skill.message}`);
2998
+ }
2999
+ function configureWindsurf(result) {
3000
+ const configPath = isWin ? join8(appData(), "Codeium", "Windsurf", "mcp_config.json") : join8(home, ".codeium", "windsurf", "mcp_config.json");
3001
+ const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
3002
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Windsurf MCP: ${mcp.message}`);
3003
+ }
3004
+ async function handleInitCommand(options) {
3005
+ const cwd = resolve2(process.cwd());
3006
+ const result = {
3007
+ cwd,
3008
+ environments: [],
3009
+ actions: [],
3010
+ errors: []
3011
+ };
3012
+ const detectors = [
3013
+ () => detectCursor(cwd),
3014
+ () => detectClaudeDesktop(),
3015
+ () => detectClaudeCode(cwd),
3016
+ () => detectAntigravity(cwd),
3017
+ () => detectVSCode(cwd),
3018
+ () => detectWindsurf()
3019
+ ];
3020
+ for (const detect of detectors) {
3021
+ result.environments.push(detect());
3022
+ }
3023
+ const detected = result.environments.filter((e) => e.detected);
3024
+ if (detected.length === 0) {
3025
+ if (!options.json) {
3026
+ console.log(`
3027
+ \u{1FAB6} Elevate Init
3028
+ `);
3029
+ console.log(` No AI coding environments detected in this workspace.`);
3030
+ console.log(` Creating portable .mcp.json for broad compatibility.
3031
+ `);
3032
+ }
3033
+ const mcpPath = join8(cwd, ".mcp.json");
3034
+ const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
3035
+ result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Generic MCP: ${mcp.message}`);
3036
+ } else {
3037
+ if (!options.json) {
3038
+ console.log(`
3039
+ \u{1FAB6} Elevate Init \u2014 Detected ${detected.length} environment(s)
3040
+ `);
3041
+ }
3042
+ const configurators = {
3043
+ cursor: configureCursor,
3044
+ "claude-desktop": (_cwd, r) => configureClaudeDesktop(r),
3045
+ "claude-code": configureClaudeCode,
3046
+ antigravity: configureAntigravity,
3047
+ vscode: configureVSCode,
3048
+ windsurf: (_cwd, r) => configureWindsurf(r)
3049
+ };
3050
+ for (const env of detected) {
3051
+ try {
3052
+ const configure = configurators[env.id];
3053
+ if (configure) {
3054
+ configure(cwd, result);
3055
+ }
3056
+ } catch (err) {
3057
+ const msg = err instanceof Error ? err.message : String(err);
3058
+ result.errors.push(`${env.name}: ${msg}`);
3059
+ }
3060
+ }
3061
+ }
3062
+ if (options.json) {
3063
+ console.log(JSON.stringify(result, null, 2));
3064
+ return result.errors.length > 0 ? 1 : 0;
3065
+ }
3066
+ console.log(" Environments:");
3067
+ for (const env of result.environments) {
3068
+ const status = env.detected ? "\u2705" : " ";
3069
+ const mcpBadge = env.mcpConfigured ? " [MCP \u2713]" : "";
3070
+ const skillBadge = env.skillConfigured ? " [Skill \u2713]" : "";
3071
+ console.log(` ${status} ${env.icon} ${env.name.padEnd(22)}${env.detected ? env.reason : "not detected"}${mcpBadge}${skillBadge}`);
3072
+ }
3073
+ if (result.actions.length > 0) {
3074
+ console.log(`
3075
+ Actions:`);
3076
+ for (const action of result.actions) {
3077
+ console.log(` ${action}`);
3078
+ }
3079
+ }
3080
+ if (result.errors.length > 0) {
3081
+ console.log(`
3082
+ \u26A0\uFE0F Errors:`);
3083
+ for (const error of result.errors) {
3084
+ console.log(` \u274C ${error}`);
3085
+ }
3086
+ }
3087
+ console.log(`
3088
+ Done! Restart your AI coding tools to pick up the new MCP configuration.`);
3089
+ console.log(` Agents will now automatically use Elevate for dependency management.
3090
+ `);
3091
+ return result.errors.length > 0 ? 1 : 0;
3092
+ }
3093
+
3094
+ // src/mcp/mcp-server.ts
3095
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3096
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3097
+ import {
3098
+ CallToolRequestSchema,
3099
+ ListToolsRequestSchema
3100
+ } from "@modelcontextprotocol/sdk/types.js";
3101
+ import semver6 from "semver";
3102
+ async function startMcpServer(config) {
3103
+ const server = new Server(
3104
+ {
3105
+ name: "elevate-mcp",
3106
+ version: "1.2.0"
3107
+ },
3108
+ {
3109
+ capabilities: {
3110
+ tools: {}
3111
+ }
3112
+ }
3113
+ );
3114
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
3115
+ return {
3116
+ tools: [
3117
+ {
3118
+ name: "elevate_discover_modules",
3119
+ description: "Discovers all repository modules and workspaces for Node/npm (package.json) or Java/Maven (pom.xml).",
3120
+ inputSchema: {
3121
+ type: "object",
3122
+ properties: {
3123
+ ecosystem: {
3124
+ type: "string",
3125
+ enum: ["npm", "maven"],
3126
+ description: "Target ecosystem (default: npm)"
3127
+ }
3128
+ }
3129
+ }
3130
+ },
3131
+ {
3132
+ name: "elevate_scan",
3133
+ description: "Scans a module (or all modules) for available dependency updates against the registry.",
3134
+ inputSchema: {
3135
+ type: "object",
3136
+ properties: {
3137
+ ecosystem: {
3138
+ type: "string",
3139
+ enum: ["npm", "maven"],
3140
+ description: "Target ecosystem (default: npm)"
3141
+ },
3142
+ moduleId: {
3143
+ type: "string",
3144
+ description: 'Module ID or relative path (e.g. "apps/e2e-cockpit"). If omitted, scans the root or primary module.'
3145
+ },
3146
+ channel: {
3147
+ type: "string",
3148
+ enum: ["stable", "all"],
3149
+ description: 'Release channel: "stable" (production-ready versions only) or "all" (including pre-releases/betas). Default: stable'
3150
+ },
3151
+ allModules: {
3152
+ type: "boolean",
3153
+ description: "If true, scans all discovered modules across the monorepo."
3154
+ }
3155
+ }
3156
+ }
3157
+ },
3158
+ {
3159
+ name: "elevate_get_versions",
3160
+ description: "Fetches the complete published version history of a package from the registry.",
3161
+ inputSchema: {
3162
+ type: "object",
3163
+ properties: {
3164
+ ecosystem: {
3165
+ type: "string",
3166
+ enum: ["npm", "maven"],
3167
+ description: "Target ecosystem (default: npm)"
3168
+ },
3169
+ identifier: {
3170
+ type: "string",
3171
+ description: "Package identifier (npm: chalk, maven: org.slf4j:slf4j-api)"
3172
+ }
3173
+ },
3174
+ required: ["identifier"]
3175
+ }
3176
+ },
3177
+ {
3178
+ name: "elevate_apply_updates",
3179
+ description: "Applies targeted dependency updates to a module, installs packages, and executes automatic build & test verification.",
3180
+ inputSchema: {
3181
+ type: "object",
3182
+ properties: {
3183
+ ecosystem: {
3184
+ type: "string",
3185
+ enum: ["npm", "maven"],
3186
+ description: "Target ecosystem (default: npm)"
3187
+ },
3188
+ moduleId: {
3189
+ type: "string",
3190
+ description: "Module ID or relative path of the target module"
3191
+ },
3192
+ updates: {
3193
+ type: "array",
3194
+ description: "List of packages to update with optional target versions",
3195
+ items: {
3196
+ type: "object",
3197
+ properties: {
3198
+ identifier: { type: "string", description: "Package identifier" },
3199
+ targetVersion: {
3200
+ type: "string",
3201
+ description: 'Specific target version (e.g. "5.6.2" or "^3.2.0"). If omitted, updates to latest.'
3202
+ }
3203
+ },
3204
+ required: ["identifier"]
3205
+ }
3206
+ },
3207
+ allowMajor: {
3208
+ type: "boolean",
3209
+ description: "Safety guardrail: Must be explicitly true to permit breaking major version upgrades."
3210
+ },
3211
+ dryRun: {
3212
+ type: "boolean",
3213
+ description: "If true, simulates planned changes without modifying any files."
3214
+ },
3215
+ skipVerification: {
3216
+ type: "boolean",
3217
+ description: "Skips the post-update build and test verification step."
3218
+ }
3219
+ },
3220
+ required: ["moduleId", "updates"]
3221
+ }
3222
+ },
3223
+ {
3224
+ name: "elevate_verify",
3225
+ description: "Runs module-specific verification and build checks (e.g. npm test, mvn test-compile).",
3226
+ inputSchema: {
3227
+ type: "object",
3228
+ properties: {
3229
+ ecosystem: {
3230
+ type: "string",
3231
+ enum: ["npm", "maven"],
3232
+ description: "Target ecosystem (default: npm)"
3233
+ },
3234
+ moduleId: {
3235
+ type: "string",
3236
+ description: "Module ID or relative path of the module to verify"
3237
+ }
3238
+ }
3239
+ }
3240
+ }
3241
+ ]
3242
+ };
3243
+ });
3244
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3245
+ const { name, arguments: args = {} } = request.params;
3246
+ const ecosystem = args.ecosystem || "npm";
3247
+ const strategy = EcosystemFactory.getStrategy(ecosystem);
3248
+ try {
3249
+ if (name === "elevate_discover_modules") {
3250
+ const modules = await strategy.discovery.discover(config.rootDir);
3251
+ return {
3252
+ content: [
3253
+ {
3254
+ type: "text",
3255
+ text: JSON.stringify(
3256
+ {
3257
+ ecosystem,
3258
+ count: modules.length,
3259
+ modules: modules.map((m) => ({
3260
+ id: m.id,
3261
+ name: m.name,
3262
+ relPath: m.relPath,
3263
+ isRoot: m.isRoot
3264
+ }))
3265
+ },
3266
+ null,
3267
+ 2
3268
+ )
3269
+ }
3270
+ ]
3271
+ };
3272
+ }
3273
+ if (name === "elevate_scan") {
3274
+ const channel = args.channel || config.channel || "stable";
3275
+ const modules = await strategy.discovery.discover(config.rootDir);
3276
+ const internalIds = /* @__PURE__ */ new Set();
3277
+ for (const m of modules) {
3278
+ if (m.id) internalIds.add(m.id);
3279
+ if (m.name) internalIds.add(m.name);
3280
+ }
3281
+ const targetModules = args.allModules ? modules : args.moduleId ? modules.filter(
3282
+ (m) => m.id.toLowerCase() === String(args.moduleId).toLowerCase() || m.relPath.toLowerCase() === String(args.moduleId).toLowerCase() || m.path.toLowerCase().endsWith(String(args.moduleId).toLowerCase())
3283
+ ) : [modules[0]];
3284
+ if (targetModules.length === 0) {
3285
+ throw new Error(`Module '${args.moduleId}' not found.`);
3286
+ }
3287
+ const results = [];
3288
+ let totalCount = 0;
3289
+ for (const mod of targetModules) {
3290
+ const updates = await strategy.reader.scan(mod, config.excludeScopes, internalIds, channel);
3291
+ totalCount += updates.length;
3292
+ results.push({
3293
+ moduleId: mod.id,
3294
+ modulePath: mod.relPath,
3295
+ updateCount: updates.length,
3296
+ updates: updates.map((u) => ({
3297
+ identifier: u.coordinate.identifier,
3298
+ currentRange: u.currentRange,
3299
+ currentClean: u.currentClean,
3300
+ latest: u.latest,
3301
+ newRange: u.newRange,
3302
+ diff: u.diff,
3303
+ scope: u.scope,
3304
+ isPreRelease: u.isPreRelease ?? false,
3305
+ preReleaseTag: u.preReleaseTag
3306
+ }))
3307
+ });
3308
+ }
3309
+ return {
3310
+ content: [
3311
+ {
3312
+ type: "text",
3313
+ text: JSON.stringify(
3314
+ {
3315
+ ecosystem,
3316
+ channel,
3317
+ scannedModules: targetModules.length,
3318
+ totalUpdatesCount: totalCount,
3319
+ results
3320
+ },
3321
+ null,
3322
+ 2
3323
+ )
3324
+ }
3325
+ ]
3326
+ };
3327
+ }
3328
+ if (name === "elevate_get_versions") {
3329
+ const identifier = String(args.identifier);
3330
+ let coordinate;
3331
+ if (ecosystem === "maven") {
3332
+ const parts = identifier.split(":");
3333
+ coordinate = {
3334
+ identifier,
3335
+ group: parts[0],
3336
+ artifact: parts[1] || parts[0],
3337
+ ecosystem: "maven"
3338
+ };
3339
+ } else {
3340
+ coordinate = {
3341
+ identifier,
3342
+ artifact: identifier,
3343
+ ecosystem: "npm"
3344
+ };
3345
+ }
3346
+ const versions = await strategy.registry.getAllVersions(coordinate);
3347
+ return {
3348
+ content: [
3349
+ {
3350
+ type: "text",
3351
+ text: JSON.stringify(
3352
+ {
3353
+ ecosystem,
3354
+ package: identifier,
3355
+ totalCount: versions.length,
3356
+ latest: versions[0] || null,
3357
+ versions: versions.map((v) => ({
3358
+ version: v,
3359
+ isPreRelease: isPreReleaseVersion(v),
3360
+ tag: extractPreReleaseTag(v)
3361
+ }))
3362
+ },
3363
+ null,
3364
+ 2
3365
+ )
3366
+ }
3367
+ ]
3368
+ };
3369
+ }
3370
+ if (name === "elevate_apply_updates") {
3371
+ const modules = await strategy.discovery.discover(config.rootDir);
3372
+ const mod = modules.find(
3373
+ (m) => m.id.toLowerCase() === String(args.moduleId).toLowerCase() || m.relPath.toLowerCase() === String(args.moduleId).toLowerCase() || m.path.toLowerCase().endsWith(String(args.moduleId).toLowerCase())
3374
+ );
3375
+ if (!mod) {
3376
+ throw new Error(`Module '${args.moduleId}' not found.`);
3377
+ }
3378
+ const internalIds = /* @__PURE__ */ new Set();
3379
+ for (const m of modules) {
3380
+ if (m.id) internalIds.add(m.id);
3381
+ if (m.name) internalIds.add(m.name);
3382
+ }
3383
+ const availableUpdates = await strategy.reader.scan(mod, config.excludeScopes, internalIds, "stable");
3384
+ const requestedUpdates = args.updates || [];
3385
+ const updatesToApply = [];
3386
+ for (const req of requestedUpdates) {
3387
+ const found = availableUpdates.find((u) => u.coordinate.identifier === req.identifier);
3388
+ if (found) {
3389
+ const targetVer = req.targetVersion || found.latest;
3390
+ const prefix = found.currentRange.startsWith("~") ? "~" : found.currentRange.startsWith("^") ? "^" : "";
3391
+ const diff = semver6.valid(targetVer) && found.currentClean ? semver6.diff(found.currentClean, targetVer) || "minor" : found.diff;
3392
+ if (diff === "major" && !args.allowMajor) {
3393
+ throw new Error(
3394
+ `Major update for '${req.identifier}' to ${targetVer} rejected. Set 'allowMajor: true' to permit breaking changes.`
3395
+ );
3396
+ }
3397
+ updatesToApply.push({
3398
+ ...found,
3399
+ newRange: `${prefix}${targetVer}`,
3400
+ diff,
3401
+ selected: true
3402
+ });
3403
+ } else if (req.targetVersion) {
3404
+ updatesToApply.push({
3405
+ coordinate: {
3406
+ identifier: req.identifier,
3407
+ artifact: req.identifier,
3408
+ ecosystem
3409
+ },
3410
+ currentRange: "unknown",
3411
+ currentClean: "0.0.0",
3412
+ latest: req.targetVersion,
3413
+ newRange: req.targetVersion,
3414
+ diff: "minor",
3415
+ scope: "prod",
3416
+ selected: true
3417
+ });
3418
+ }
3419
+ }
3420
+ if (args.dryRun) {
3421
+ return {
3422
+ content: [
3423
+ {
3424
+ type: "text",
3425
+ text: JSON.stringify(
3426
+ {
3427
+ dryRun: true,
3428
+ targetModule: mod.relPath,
3429
+ ecosystem,
3430
+ updatesCount: updatesToApply.length,
3431
+ updates: updatesToApply.map((u) => ({
3432
+ identifier: u.coordinate.identifier,
3433
+ currentRange: u.currentRange,
3434
+ newRange: u.newRange,
3435
+ diff: u.diff
3436
+ }))
3437
+ },
3438
+ null,
3439
+ 2
3440
+ )
3441
+ }
3442
+ ]
3443
+ };
3444
+ }
3445
+ const updateResult = await strategy.updater.applyUpdates(mod, config.rootDir, updatesToApply, () => {
3446
+ });
3447
+ let verifyResult;
3448
+ if (!args.skipVerification) {
3449
+ verifyResult = await strategy.verifier.verify(
3450
+ mod,
3451
+ config.rootDir,
3452
+ config.postUpdateScript,
3453
+ config.postUpdateLabel
3454
+ );
3455
+ }
3456
+ return {
3457
+ content: [
3458
+ {
3459
+ type: "text",
3460
+ text: JSON.stringify(
3461
+ {
3462
+ success: verifyResult?.status !== "warn",
3463
+ targetModule: mod.relPath,
3464
+ ecosystem,
3465
+ ...updateResult,
3466
+ verification: verifyResult
3467
+ },
3468
+ null,
3469
+ 2
3470
+ )
3471
+ }
3472
+ ]
3473
+ };
3474
+ }
3475
+ if (name === "elevate_verify") {
3476
+ const modules = await strategy.discovery.discover(config.rootDir);
3477
+ const mod = args.moduleId ? modules.find(
3478
+ (m) => m.id.toLowerCase() === String(args.moduleId).toLowerCase() || m.relPath.toLowerCase() === String(args.moduleId).toLowerCase() || m.path.toLowerCase().endsWith(String(args.moduleId).toLowerCase())
3479
+ ) : modules[0];
3480
+ if (!mod) {
3481
+ throw new Error(`Module '${args.moduleId || "default"}' not found.`);
3482
+ }
3483
+ const verifyResult = await strategy.verifier.verify(
3484
+ mod,
3485
+ config.rootDir,
3486
+ config.postUpdateScript,
3487
+ config.postUpdateLabel
3488
+ );
3489
+ return {
3490
+ content: [
3491
+ {
3492
+ type: "text",
3493
+ text: JSON.stringify(
3494
+ {
3495
+ module: mod.relPath,
3496
+ ecosystem,
3497
+ ...verifyResult
3498
+ },
3499
+ null,
3500
+ 2
3501
+ )
3502
+ }
3503
+ ]
3504
+ };
3505
+ }
3506
+ throw new Error(`Unknown tool: ${name}`);
3507
+ } catch (err) {
3508
+ return {
3509
+ isError: true,
3510
+ content: [
3511
+ {
3512
+ type: "text",
3513
+ text: `\u274C Error executing tool '${name}': ${err.message || String(err)}`
3514
+ }
3515
+ ]
3516
+ };
3517
+ }
3518
+ });
3519
+ const transport = new StdioServerTransport();
3520
+ await server.connect(transport);
3521
+ }
3522
+
3523
+ // src/index.tsx
3524
+ import { jsx as jsx15 } from "react/jsx-runtime";
3525
+ async function main() {
3526
+ const config = loadConfig();
3527
+ const options = parseCliArgs(process.argv.slice(2));
3528
+ if (options.subcommand === "help" || options.help) {
3529
+ printCliHelp();
3530
+ process.exit(0);
3531
+ }
3532
+ if (options.subcommand === "mcp") {
3533
+ await startMcpServer(config);
3534
+ return;
3535
+ }
3536
+ if (options.subcommand === "modules") {
3537
+ const exitCode = await handleModulesCommand(options, config);
3538
+ process.exit(exitCode);
3539
+ }
3540
+ if (options.subcommand === "scan") {
3541
+ const exitCode = await handleScanCommand(options, config);
3542
+ process.exit(exitCode);
3543
+ }
3544
+ if (options.subcommand === "versions") {
3545
+ const exitCode = await handleVersionsCommand(options);
3546
+ process.exit(exitCode);
3547
+ }
3548
+ if (options.subcommand === "update") {
3549
+ const exitCode = await handleUpdateCommand(options, config);
3550
+ process.exit(exitCode);
3551
+ }
3552
+ if (options.subcommand === "init") {
3553
+ const exitCode = await handleInitCommand(options);
3554
+ process.exit(exitCode);
3555
+ }
3556
+ if (process.stdout.isTTY) {
3557
+ const minCols = 140;
3558
+ const minRows = 38;
3559
+ const cols = Math.max(process.stdout.columns || 80, minCols);
3560
+ const rows = Math.max(process.stdout.rows || 24, minRows);
3561
+ process.stdout.write(`\x1B[8;${rows};${cols}t`);
3562
+ }
3563
+ render(/* @__PURE__ */ jsx15(App, { config }));
3564
+ }
3565
+ main().catch((err) => {
3566
+ console.error("\u274C Elevate fatal error:", err);
3567
+ process.exit(1);
3568
+ });
3569
+ //# sourceMappingURL=cli.js.map