@adhisang/minecraft-modding-mcp 4.1.1 → 4.2.1

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +6 -2
  3. package/dist/cache-registry.js +84 -5
  4. package/dist/entry-tools/analyze-symbol-service.d.ts +12 -0
  5. package/dist/entry-tools/analyze-symbol-service.js +7 -0
  6. package/dist/entry-tools/batch-class-members-service.d.ts +1 -0
  7. package/dist/entry-tools/batch-class-members-service.js +2 -0
  8. package/dist/entry-tools/batch-class-source-service.d.ts +1 -0
  9. package/dist/entry-tools/batch-class-source-service.js +2 -0
  10. package/dist/entry-tools/batch-mappings-service.d.ts +1 -0
  11. package/dist/entry-tools/batch-mappings-service.js +1 -0
  12. package/dist/entry-tools/batch-symbol-exists-service.d.ts +1 -0
  13. package/dist/entry-tools/batch-symbol-exists-service.js +2 -0
  14. package/dist/entry-tools/compare-minecraft-service.d.ts +9 -0
  15. package/dist/entry-tools/compare-minecraft-service.js +3 -1
  16. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +1 -0
  17. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +3 -1
  18. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +1 -0
  19. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +1 -0
  20. package/dist/entry-tools/inspect-minecraft/handlers/search.js +2 -1
  21. package/dist/entry-tools/inspect-minecraft/internal.d.ts +17 -0
  22. package/dist/entry-tools/inspect-minecraft/internal.js +10 -0
  23. package/dist/entry-tools/inspect-minecraft-service.d.ts +40 -0
  24. package/dist/entry-tools/validate-project/cases/project-summary.js +8 -3
  25. package/dist/entry-tools/validate-project/internal.d.ts +6 -2
  26. package/dist/entry-tools/validate-project/internal.js +9 -7
  27. package/dist/entry-tools/validate-project-service.d.ts +10 -0
  28. package/dist/entry-tools/validate-project-service.js +1 -0
  29. package/dist/entry-tools/verify-mixin-target-service.d.ts +2 -0
  30. package/dist/entry-tools/verify-mixin-target-service.js +1 -0
  31. package/dist/errors.js +4 -1
  32. package/dist/gradle-paths.d.ts +8 -3
  33. package/dist/gradle-paths.js +34 -5
  34. package/dist/index.js +24 -7
  35. package/dist/java-process.d.ts +9 -0
  36. package/dist/java-process.js +11 -5
  37. package/dist/mapping/loaders/tiny-loom.d.ts +1 -1
  38. package/dist/mapping/loaders/tiny-loom.js +5 -2
  39. package/dist/mapping/types.d.ts +5 -0
  40. package/dist/mapping-service.d.ts +1 -0
  41. package/dist/mapping-service.js +16 -13
  42. package/dist/mixin-validator.d.ts +1 -1
  43. package/dist/mixin-validator.js +1 -1
  44. package/dist/nbt/java-nbt-codec.js +81 -2
  45. package/dist/source/access-validate.js +12 -8
  46. package/dist/source/artifact-resolver.d.ts +5 -0
  47. package/dist/source/artifact-resolver.js +23 -7
  48. package/dist/source/cache-metrics.js +2 -2
  49. package/dist/source/class-source/members-builder.d.ts +1 -0
  50. package/dist/source/class-source/members-builder.js +1 -1
  51. package/dist/source/class-source.d.ts +1 -0
  52. package/dist/source/class-source.js +9 -1
  53. package/dist/source/indexer.d.ts +31 -6
  54. package/dist/source/indexer.js +90 -23
  55. package/dist/source/lifecycle/diff.js +6 -6
  56. package/dist/source/lifecycle/mapping-helpers.d.ts +3 -3
  57. package/dist/source/lifecycle/mapping-helpers.js +17 -9
  58. package/dist/source/lifecycle/trace.js +2 -2
  59. package/dist/source/search.js +1 -0
  60. package/dist/source/symbol-resolver.js +4 -1
  61. package/dist/source/validate-mixin/pipeline/mapping-health.js +2 -1
  62. package/dist/source/validate-mixin/pipeline/resolve.js +1 -0
  63. package/dist/source/validate-mixin/pipeline/target-lookup.js +12 -7
  64. package/dist/source-service.d.ts +18 -1
  65. package/dist/source-service.js +2 -2
  66. package/dist/tiny-remapper-service.js +3 -3
  67. package/dist/tool-contract-manifest.d.ts +1 -1
  68. package/dist/tool-contract-manifest.js +2 -2
  69. package/dist/tool-schemas.d.ts +91 -0
  70. package/dist/tool-schemas.js +20 -0
  71. package/docs/README-ja.md +2 -2
  72. package/docs/tool-reference.md +5 -1
  73. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
- import { mkdir, open, rename, stat, unlink } from "node:fs/promises";
4
- import { join } from "node:path";
3
+ import { mkdir, open, rename, rm, stat } from "node:fs/promises";
4
+ import { basename, dirname, join } from "node:path";
5
5
  import { buildSuggestedCall } from "../build-suggested-call.js";
6
6
  import { decompileBinaryJar } from "../decompiler/vineflower.js";
7
7
  import { ERROR_CODES, createError, isAppError } from "../errors.js";
@@ -15,6 +15,13 @@ import { resolveVineflowerJar } from "../vineflower-resolver.js";
15
15
  import { enforceCacheLimits, recordRemappedJarBytes, releaseRemappedJarBytes, touchCacheMetrics, upsertCacheMetrics } from "./cache-metrics.js";
16
16
  import { normalizePathStyle } from "./shared-utils.js";
17
17
  export const INDEX_SCHEMA_VERSION = 1;
18
+ const defaultBinaryRemapDeps = {
19
+ resolveTinyRemapperJar,
20
+ resolveMojangTinyFile,
21
+ remapJar,
22
+ now: () => Date.now(),
23
+ randomSuffix: () => Math.random().toString(36).slice(2, 8) || "0"
24
+ };
18
25
  function chunkArray(items, chunkSize) {
19
26
  const size = Math.max(1, Math.trunc(chunkSize));
20
27
  if (items.length === 0) {
@@ -354,13 +361,12 @@ async function rebuildMissingArtifactIndex(svc, resolved, reason) {
354
361
  * binary remap, run tiny-remapper now and return the remapped jar path.
355
362
  * Otherwise return the original binaryJarPath unchanged.
356
363
  *
357
- * Cache safety: writes to a per-attempt temp file then atomic-renames into
358
- * <cacheDir>/remapped/<artifactId>.jar. A per-target inflight Promise map
359
- * collapses concurrent calls so two simultaneous resolveArtifact calls for
360
- * the same artifactId share one tiny-remapper run instead of racing on the
361
- * same output path.
364
+ * Cache safety: writes to a per-attempt `.jar` temp path, validates ZIP magic,
365
+ * then atomic-renames into <cacheDir>/remapped/<artifactId>.jar. A per-target
366
+ * inflight Promise map collapses concurrent calls so simultaneous
367
+ * resolveArtifact calls for the same artifactId share one tiny-remapper run.
362
368
  */
363
- export async function maybeRemapBinaryForMojang(svc, resolved) {
369
+ export async function maybeRemapBinaryForMojang(svc, resolved, deps = defaultBinaryRemapDeps) {
364
370
  const binaryJarPath = resolved.binaryJarPath;
365
371
  if (!binaryJarPath) {
366
372
  throw createError({
@@ -400,10 +406,19 @@ export async function maybeRemapBinaryForMojang(svc, resolved) {
400
406
  remappedJarPath
401
407
  });
402
408
  try {
403
- await unlink(remappedJarPath);
409
+ await rm(remappedJarPath, { recursive: true, force: true });
404
410
  }
405
- catch {
406
- // ignore: race with another process or already-deleted file.
411
+ catch (caughtError) {
412
+ releaseRemappedJarBytes(svc, resolved.artifactId);
413
+ throw createError({
414
+ code: ERROR_CODES.REMAP_FAILED,
415
+ message: "Failed to remove corrupt binary remap cache entry.",
416
+ details: {
417
+ artifactId: resolved.artifactId,
418
+ remappedJarPath,
419
+ cause: caughtError instanceof Error ? caughtError.message : String(caughtError)
420
+ }
421
+ });
407
422
  }
408
423
  releaseRemappedJarBytes(svc, resolved.artifactId);
409
424
  }
@@ -411,12 +426,12 @@ export async function maybeRemapBinaryForMojang(svc, resolved) {
411
426
  if (inflight) {
412
427
  return inflight;
413
428
  }
414
- const remapPromise = runBinaryRemap(svc, {
429
+ const remapPromise = runBinaryRemapWithDeps(svc, {
415
430
  version: resolved.version,
416
431
  inputJar: binaryJarPath,
417
432
  remappedDir,
418
433
  remappedJarPath
419
- });
434
+ }, deps);
420
435
  svc.state.inflightRemaps.set(remappedJarPath, remapPromise);
421
436
  try {
422
437
  const path = await remapPromise;
@@ -430,6 +445,9 @@ export async function maybeRemapBinaryForMojang(svc, resolved) {
430
445
  export async function recordRemappedJarBytesFromDisk(svc, artifactId, path) {
431
446
  try {
432
447
  const fileStat = await stat(path);
448
+ if (!fileStat.isFile()) {
449
+ return;
450
+ }
433
451
  recordRemappedJarBytes(svc, artifactId, fileStat.size);
434
452
  }
435
453
  catch {
@@ -467,14 +485,45 @@ export async function isUsableJarFile(path) {
467
485
  await handle?.close().catch(() => undefined);
468
486
  }
469
487
  }
488
+ export function buildBinaryRemapTempPath(remappedJarPath, input) {
489
+ // Keep the final extension as `.jar`; the remap output is rejected unless it
490
+ // is a regular ZIP/JAR file, and manage-cache recognizes this temp shape.
491
+ const fileName = basename(remappedJarPath);
492
+ const artifactId = fileName.endsWith(".jar")
493
+ ? fileName.slice(0, -".jar".length)
494
+ : fileName;
495
+ return join(dirname(remappedJarPath), `${artifactId}.tmp.${input.pid}.${input.now}.${input.randomSuffix}.jar`);
496
+ }
497
+ async function describePathKind(path) {
498
+ try {
499
+ const stats = await stat(path);
500
+ if (stats.isFile()) {
501
+ return "file";
502
+ }
503
+ if (stats.isDirectory()) {
504
+ return "directory";
505
+ }
506
+ return "other";
507
+ }
508
+ catch {
509
+ return "missing";
510
+ }
511
+ }
470
512
  export async function runBinaryRemap(svc, input) {
471
- const tinyRemapperJarPath = await resolveTinyRemapperJar(svc.config.cacheDir, svc.config.tinyRemapperJarPath);
472
- const mojangTiny = await resolveMojangTinyFile(input.version, svc.config);
513
+ return runBinaryRemapWithDeps(svc, input, defaultBinaryRemapDeps);
514
+ }
515
+ export async function runBinaryRemapWithDeps(svc, input, deps) {
516
+ const tinyRemapperJarPath = await deps.resolveTinyRemapperJar(svc.config.cacheDir, svc.config.tinyRemapperJarPath);
517
+ const mojangTiny = await deps.resolveMojangTinyFile(input.version, svc.config);
473
518
  await mkdir(input.remappedDir, { recursive: true });
474
- const tempPath = `${input.remappedJarPath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
519
+ const tempPath = buildBinaryRemapTempPath(input.remappedJarPath, {
520
+ pid: process.pid,
521
+ now: deps.now(),
522
+ randomSuffix: deps.randomSuffix()
523
+ });
475
524
  const remapStartedAt = Date.now();
476
525
  try {
477
- await remapJar(tinyRemapperJarPath, {
526
+ await deps.remapJar(tinyRemapperJarPath, {
478
527
  inputJar: input.inputJar,
479
528
  outputJar: tempPath,
480
529
  mappingsFile: mojangTiny.path,
@@ -483,20 +532,38 @@ export async function runBinaryRemap(svc, input) {
483
532
  timeoutMs: svc.config.remapTimeoutMs,
484
533
  maxMemoryMb: svc.config.remapMaxMemoryMb
485
534
  });
486
- const tempStats = await stat(tempPath);
487
- if (tempStats.size === 0) {
535
+ if (!(await isUsableJarFile(tempPath))) {
536
+ throw createError({
537
+ code: ERROR_CODES.REMAP_FAILED,
538
+ message: "tiny-remapper produced an invalid output jar.",
539
+ details: {
540
+ inputJar: input.inputJar,
541
+ tempPath,
542
+ outputKind: await describePathKind(tempPath)
543
+ }
544
+ });
545
+ }
546
+ try {
547
+ await rename(tempPath, input.remappedJarPath);
548
+ }
549
+ catch (caughtError) {
488
550
  throw createError({
489
551
  code: ERROR_CODES.REMAP_FAILED,
490
- message: "tiny-remapper produced an empty output jar.",
491
- details: { inputJar: input.inputJar, tempPath }
552
+ message: "Failed to finalize binary remap cache entry.",
553
+ details: {
554
+ inputJar: input.inputJar,
555
+ tempPath,
556
+ remappedJarPath: input.remappedJarPath,
557
+ outputKind: await describePathKind(input.remappedJarPath),
558
+ cause: caughtError instanceof Error ? caughtError.message : String(caughtError)
559
+ }
492
560
  });
493
561
  }
494
- await rename(tempPath, input.remappedJarPath);
495
562
  return input.remappedJarPath;
496
563
  }
497
564
  catch (caughtError) {
498
565
  try {
499
- await unlink(tempPath);
566
+ await rm(tempPath, { recursive: true, force: true });
500
567
  }
501
568
  catch {
502
569
  // tempPath may not exist if remapJar failed before writing anything; ignore.
@@ -63,10 +63,10 @@ export async function diffClassSignatures(svc, input) {
63
63
  });
64
64
  }
65
65
  const mappingWarnings = [];
66
- const obfuscatedFromClassName = await resolveToObfuscatedClassName(svc, className, fromVersion, mapping, input.sourcePriority, mappingWarnings);
66
+ const obfuscatedFromClassName = await resolveToObfuscatedClassName(svc, className, fromVersion, mapping, input.sourcePriority, mappingWarnings, input.gradleUserHome);
67
67
  const obfuscatedToClassName = fromVersion === toVersion
68
68
  ? obfuscatedFromClassName
69
- : await resolveToObfuscatedClassName(svc, className, toVersion, mapping, input.sourcePriority, mappingWarnings);
69
+ : await resolveToObfuscatedClassName(svc, className, toVersion, mapping, input.sourcePriority, mappingWarnings, input.gradleUserHome);
70
70
  const [fromResolved, toResolved] = await Promise.all([
71
71
  svc.versionService.resolveVersionJar(fromVersion),
72
72
  svc.versionService.resolveVersionJar(toVersion)
@@ -176,8 +176,8 @@ export async function diffClassSignatures(svc, input) {
176
176
  // Remap diff delta members for non-obfuscated mappings
177
177
  const remapDelta = async (delta, kind) => {
178
178
  const [addedResult, removedResult] = await Promise.all([
179
- svc.remapSignatureMembers(delta.added, kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings),
180
- svc.remapSignatureMembers(delta.removed, kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings)
179
+ svc.remapSignatureMembers(delta.added, kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings, undefined, input.gradleUserHome),
180
+ svc.remapSignatureMembers(delta.removed, kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings, undefined, input.gradleUserHome)
181
181
  ]);
182
182
  const remappedModified = await Promise.all(delta.modified.map(async (change) => {
183
183
  if (!change.from || !change.to) {
@@ -194,8 +194,8 @@ export async function diffClassSignatures(svc, input) {
194
194
  });
195
195
  }
196
196
  const [fromResult, toResult] = await Promise.all([
197
- svc.remapSignatureMembers([change.from], kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings),
198
- svc.remapSignatureMembers([change.to], kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings)
197
+ svc.remapSignatureMembers([change.from], kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings, undefined, input.gradleUserHome),
198
+ svc.remapSignatureMembers([change.to], kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings, undefined, input.gradleUserHome)
199
199
  ]);
200
200
  const fromMember = fromResult.members[0];
201
201
  const toMember = toResult.members[0];
@@ -11,12 +11,12 @@ export declare function rejectLifecycleClassLikeInput(svc: SourceService, input:
11
11
  sourcePriority?: MappingSourcePriority;
12
12
  }): void;
13
13
  export declare function releaseLifecycleMappingGraph(svc: SourceService, version: string, sourcePriority: MappingSourcePriority | undefined): void;
14
- export declare function resolveToObfuscatedClassName(svc: SourceService, className: string, version: string, mapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[]): Promise<string>;
15
- export declare function resolveToObfuscatedMemberName(svc: SourceService, name: string, ownerInSourceMapping: string, descriptor: string | undefined, kind: "field" | "method", version: string, mapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[]): Promise<{
14
+ export declare function resolveToObfuscatedClassName(svc: SourceService, className: string, version: string, mapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], gradleUserHome?: string): Promise<string>;
15
+ export declare function resolveToObfuscatedMemberName(svc: SourceService, name: string, ownerInSourceMapping: string, descriptor: string | undefined, kind: "field" | "method", version: string, mapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], gradleUserHome?: string): Promise<{
16
16
  name: string;
17
17
  descriptor?: string;
18
18
  }>;
19
- export declare function remapSignatureMembers(svc: SourceService, members: SignatureMember[], kind: "field" | "method", version: string, sourceMapping: SourceMapping, targetMapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], projectPath?: string): Promise<{
19
+ export declare function remapSignatureMembers(svc: SourceService, members: SignatureMember[], kind: "field" | "method", version: string, sourceMapping: SourceMapping, targetMapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], projectPath?: string, gradleUserHome?: string): Promise<{
20
20
  members: SignatureMember[];
21
21
  failedNames: Set<string>;
22
22
  }>;
@@ -58,18 +58,19 @@ export function releaseLifecycleMappingGraph(svc, version, sourcePriority) {
58
58
  svc.mappingService.releaseGraphCacheEntry(version, sourcePriority);
59
59
  }
60
60
  }
61
- export async function resolveToObfuscatedClassName(svc, className, version, mapping, sourcePriority, warnings) {
61
+ export async function resolveToObfuscatedClassName(svc, className, version, mapping, sourcePriority, warnings, gradleUserHome) {
62
62
  return svc.resolveClassNameForLookup({
63
63
  className,
64
64
  version,
65
65
  sourceMapping: mapping,
66
66
  targetMapping: "obfuscated",
67
67
  sourcePriority,
68
+ gradleUserHome,
68
69
  warnings,
69
70
  context: "bytecode lookup"
70
71
  });
71
72
  }
72
- export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapping, descriptor, kind, version, mapping, sourcePriority, warnings) {
73
+ export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapping, descriptor, kind, version, mapping, sourcePriority, warnings, gradleUserHome) {
73
74
  if (mapping === "obfuscated") {
74
75
  return {
75
76
  name,
@@ -89,7 +90,8 @@ export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapp
89
90
  descriptor,
90
91
  sourceMapping: mapping,
91
92
  targetMapping: "obfuscated",
92
- sourcePriority
93
+ sourcePriority,
94
+ gradleUserHome
93
95
  })
94
96
  : await svc.mappingService.findMapping({
95
97
  version,
@@ -108,7 +110,8 @@ export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapp
108
110
  : undefined,
109
111
  sourceMapping: mapping,
110
112
  targetMapping: "obfuscated",
111
- sourcePriority
113
+ sourcePriority,
114
+ gradleUserHome
112
115
  });
113
116
  warnings.push(...mapped.warnings);
114
117
  if (mapped.resolved && mapped.resolvedSymbol) {
@@ -127,7 +130,8 @@ export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapp
127
130
  signatureMode: "exact",
128
131
  sourceMapping: mapping,
129
132
  targetMapping: "obfuscated",
130
- sourcePriority
133
+ sourcePriority,
134
+ gradleUserHome
131
135
  });
132
136
  warnings.push(...fallbackMapped.warnings);
133
137
  if (fallbackMapped.resolved && fallbackMapped.resolvedSymbol) {
@@ -147,7 +151,7 @@ export async function resolveToObfuscatedMemberName(svc, name, ownerInSourceMapp
147
151
  descriptor: kind === "method" ? descriptor : undefined
148
152
  };
149
153
  }
150
- export async function remapSignatureMembers(svc, members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath) {
154
+ export async function remapSignatureMembers(svc, members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath, gradleUserHome) {
151
155
  const failedNames = new Set();
152
156
  if (sourceMapping === targetMapping) {
153
157
  return { members, failedNames };
@@ -174,7 +178,8 @@ export async function remapSignatureMembers(svc, members, kind, version, sourceM
174
178
  sourceMapping,
175
179
  targetMapping,
176
180
  sourcePriority,
177
- projectPath
181
+ projectPath,
182
+ gradleUserHome
178
183
  });
179
184
  if (mapped.resolved && mapped.resolvedSymbol) {
180
185
  ownerToRemapped.set(obfuscatedFqn, mapped.resolvedSymbol.name);
@@ -207,7 +212,8 @@ export async function remapSignatureMembers(svc, members, kind, version, sourceM
207
212
  sourceMapping,
208
213
  targetMapping,
209
214
  sourcePriority,
210
- projectPath
215
+ projectPath,
216
+ gradleUserHome
211
217
  });
212
218
  if (mapped.resolved && mapped.resolvedSymbol) {
213
219
  ownerToRemapped.set(dotFqn, mapped.resolvedSymbol.name);
@@ -242,7 +248,8 @@ export async function remapSignatureMembers(svc, members, kind, version, sourceM
242
248
  sourceMapping,
243
249
  targetMapping,
244
250
  sourcePriority,
245
- projectPath
251
+ projectPath,
252
+ gradleUserHome
246
253
  });
247
254
  if (exactResult.resolved && exactResult.resolvedSymbol) {
248
255
  memberKeyToRemapped.set(key, exactResult.resolvedSymbol.name);
@@ -270,6 +277,7 @@ export async function remapSignatureMembers(svc, members, kind, version, sourceM
270
277
  targetMapping,
271
278
  sourcePriority,
272
279
  projectPath,
280
+ gradleUserHome,
273
281
  disambiguation: {
274
282
  ownerHint: targetOwner,
275
283
  descriptorHint: remappedDescriptorHint
@@ -123,8 +123,8 @@ export async function traceSymbolLifecycle(svc, input) {
123
123
  const versionWarnings = [];
124
124
  try {
125
125
  const [obfuscatedClassName, obfuscatedMethod, resolvedJar] = await Promise.all([
126
- resolveToObfuscatedClassName(svc, userClassName, version, mapping, input.sourcePriority, versionWarnings),
127
- resolveToObfuscatedMemberName(svc, userMethodName, userClassName, descriptor, "method", version, mapping, input.sourcePriority, versionWarnings),
126
+ resolveToObfuscatedClassName(svc, userClassName, version, mapping, input.sourcePriority, versionWarnings, input.gradleUserHome),
127
+ resolveToObfuscatedMemberName(svc, userMethodName, userClassName, descriptor, "method", version, mapping, input.sourcePriority, versionWarnings, input.gradleUserHome),
128
128
  svc.versionService.resolveVersionJar(version)
129
129
  ]);
130
130
  const signature = await svc.explorerService.getSignature({
@@ -250,6 +250,7 @@ export async function searchClassSource(svc, input) {
250
250
  sourceMapping: input.queryNamespace,
251
251
  targetMapping: artifactMapping,
252
252
  sourcePriority: input.sourcePriority,
253
+ gradleUserHome: input.gradleUserHome,
253
254
  signatureMode: "name-only",
254
255
  maxCandidates: 5
255
256
  });
@@ -106,6 +106,7 @@ export async function resolveWorkspaceSymbol(svc, input) {
106
106
  sourceMapping: input.sourceMapping,
107
107
  targetMapping: mappingApplied,
108
108
  sourcePriority: input.sourcePriority,
109
+ gradleUserHome: input.gradleUserHome,
109
110
  maxCandidates: input.maxCandidates
110
111
  });
111
112
  return {
@@ -121,7 +122,8 @@ export async function resolveWorkspaceSymbol(svc, input) {
121
122
  className,
122
123
  classNameMapping: input.sourceMapping,
123
124
  includeKinds: ["class"],
124
- sourcePriority: input.sourcePriority
125
+ sourcePriority: input.sourcePriority,
126
+ gradleUserHome: input.gradleUserHome
125
127
  });
126
128
  const resolvedClass = matrix.classIdentity[mappingApplied];
127
129
  if (!resolvedClass) {
@@ -180,6 +182,7 @@ export async function resolveWorkspaceSymbol(svc, input) {
180
182
  sourceMapping: input.sourceMapping,
181
183
  targetMapping: mappingApplied,
182
184
  sourcePriority: input.sourcePriority,
185
+ gradleUserHome: input.gradleUserHome,
183
186
  maxCandidates: input.maxCandidates
184
187
  });
185
188
  const filtered = mapped.candidates.filter((candidate) => candidate.kind === kind);
@@ -5,7 +5,8 @@ export async function runMappingHealthStage(svc, ctx) {
5
5
  const health = await svc.mappingService.checkMappingHealth({
6
6
  version: ctx.version,
7
7
  requestedMapping: ctx.requestedMapping,
8
- sourcePriority: ctx.currentSourcePriority
8
+ sourcePriority: ctx.currentSourcePriority,
9
+ gradleUserHome: ctx.input.gradleUserHome
9
10
  });
10
11
  const jarAvailable = existsSync(ctx.jarPath);
11
12
  ctx.healthReport = {
@@ -36,6 +36,7 @@ export async function runResolveStage(svc, ctx) {
36
36
  mapping: ctx.requestedMapping,
37
37
  sourcePriority: ctx.currentSourcePriority,
38
38
  projectPath: ctx.input.projectPath,
39
+ gradleUserHome: ctx.input.gradleUserHome,
39
40
  scope: ctx.input.scope,
40
41
  preferProjectVersion: false
41
42
  });
@@ -9,7 +9,8 @@ function findValidateMixinClassMapping(svc, input) {
9
9
  sourceMapping: input.sourceMapping,
10
10
  targetMapping: input.targetMapping,
11
11
  sourcePriority: input.sourcePriority,
12
- projectPath: input.projectPath
12
+ projectPath: input.projectPath,
13
+ gradleUserHome: input.gradleUserHome
13
14
  });
14
15
  }
15
16
  const cacheKey = [
@@ -18,7 +19,8 @@ function findValidateMixinClassMapping(svc, input) {
18
19
  input.sourceMapping,
19
20
  input.targetMapping,
20
21
  input.sourcePriority,
21
- input.projectPath ?? ""
22
+ input.projectPath ?? "",
23
+ input.gradleUserHome ?? ""
22
24
  ].join("\0");
23
25
  const cached = cache.get(cacheKey);
24
26
  if (cached) {
@@ -31,7 +33,8 @@ function findValidateMixinClassMapping(svc, input) {
31
33
  sourceMapping: input.sourceMapping,
32
34
  targetMapping: input.targetMapping,
33
35
  sourcePriority: input.sourcePriority,
34
- projectPath: input.projectPath
36
+ projectPath: input.projectPath,
37
+ gradleUserHome: input.gradleUserHome
35
38
  }).catch((error) => {
36
39
  cache.delete(cacheKey);
37
40
  throw error;
@@ -78,6 +81,7 @@ export async function processSingleMixinTarget(svc, ctx, target, targetIndex) {
78
81
  targetMapping: ctx.signatureLookupMapping,
79
82
  sourcePriority: ctx.currentSourcePriority,
80
83
  projectPath: ctx.input.projectPath,
84
+ gradleUserHome: ctx.input.gradleUserHome,
81
85
  batchCaches: ctx.input.batchCaches
82
86
  });
83
87
  if (mapped.resolved && mapped.resolvedSymbol) {
@@ -110,9 +114,9 @@ export async function processSingleMixinTarget(svc, ctx, target, targetIndex) {
110
114
  if (ctx.requestedMapping !== ctx.signatureLookupMapping) {
111
115
  try {
112
116
  const [ctorResult, methodResult, fieldResult] = await Promise.all([
113
- svc.remapSignatureMembers(sig.constructors, "method", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath),
114
- svc.remapSignatureMembers(sig.methods, "method", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath),
115
- svc.remapSignatureMembers(sig.fields, "field", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath)
117
+ svc.remapSignatureMembers(sig.constructors, "method", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath, ctx.input.gradleUserHome),
118
+ svc.remapSignatureMembers(sig.methods, "method", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath, ctx.input.gradleUserHome),
119
+ svc.remapSignatureMembers(sig.fields, "field", ctx.version, ctx.signatureLookupMapping, ctx.requestedMapping, ctx.currentSourcePriority, ctx.warnings, ctx.input.projectPath, ctx.input.gradleUserHome)
116
120
  ]);
117
121
  constructors = ctorResult.members;
118
122
  methods = methodResult.members;
@@ -160,7 +164,8 @@ export async function processSingleMixinTarget(svc, ctx, target, targetIndex) {
160
164
  try {
161
165
  const existenceCheck = await svc.mappingService.checkSymbolExists({
162
166
  version: ctx.version, kind: "class", name: resolvedClassName,
163
- sourceMapping: ctx.requestedMapping, nameMode: "auto", sourcePriority: ctx.currentSourcePriority
167
+ sourceMapping: ctx.requestedMapping, nameMode: "auto", sourcePriority: ctx.currentSourcePriority,
168
+ gradleUserHome: ctx.input.gradleUserHome
164
169
  });
165
170
  if (existenceCheck.resolved) {
166
171
  ctx.symbolExistsButSignatureFailed.add(target.className);
@@ -26,6 +26,7 @@ export type ResolveArtifactInput = {
26
26
  sourcePriority?: MappingSourcePriority;
27
27
  allowDecompile?: boolean;
28
28
  projectPath?: string;
29
+ gradleUserHome?: string;
29
30
  scope?: ArtifactScope;
30
31
  preferProjectVersion?: boolean;
31
32
  strictVersion?: boolean;
@@ -58,6 +59,7 @@ export type ProbeMinecraftArtifactInput = {
58
59
  mapping?: SourceMapping;
59
60
  sourcePriority?: MappingSourcePriority;
60
61
  projectPath?: string;
62
+ gradleUserHome?: string;
61
63
  scope?: ArtifactScope;
62
64
  preferProjectVersion?: boolean;
63
65
  };
@@ -105,6 +107,7 @@ export type SearchClassSourceInput = {
105
107
  cursor?: string;
106
108
  queryNamespace?: SourceMapping;
107
109
  sourcePriority?: MappingSourcePriority;
110
+ gradleUserHome?: string;
108
111
  };
109
112
  export type SearchClassSourceOutput = {
110
113
  hits: SearchSourceHit[];
@@ -165,6 +168,7 @@ export type ResolveWorkspaceSymbolInput = {
165
168
  descriptor?: string;
166
169
  sourceMapping: SourceMapping;
167
170
  sourcePriority?: MappingSourcePriority;
171
+ gradleUserHome?: string;
168
172
  maxCandidates?: number;
169
173
  };
170
174
  export type ResolveWorkspaceSymbolOutput = MappingSymbolResolutionOutput & {
@@ -180,6 +184,7 @@ export type GetClassSourceInput = {
180
184
  sourcePriority?: MappingSourcePriority;
181
185
  allowDecompile?: boolean;
182
186
  projectPath?: string;
187
+ gradleUserHome?: string;
183
188
  scope?: ArtifactScope;
184
189
  preferProjectVersion?: boolean;
185
190
  strictVersion?: boolean;
@@ -241,6 +246,7 @@ export type GetClassMembersInput = {
241
246
  memberPattern?: string;
242
247
  maxMembers?: number;
243
248
  projectPath?: string;
249
+ gradleUserHome?: string;
244
250
  scope?: ArtifactScope;
245
251
  preferProjectVersion?: boolean;
246
252
  strictVersion?: boolean;
@@ -302,6 +308,7 @@ export type TraceSymbolLifecycleInput = {
302
308
  toVersion?: string;
303
309
  mapping?: SourceMapping;
304
310
  sourcePriority?: MappingSourcePriority;
311
+ gradleUserHome?: string;
305
312
  includeSnapshots?: boolean;
306
313
  maxVersions?: number;
307
314
  includeTimeline?: boolean;
@@ -352,6 +359,7 @@ export type DiffClassSignaturesInput = {
352
359
  toVersion: string;
353
360
  mapping?: SourceMapping;
354
361
  sourcePriority?: MappingSourcePriority;
362
+ gradleUserHome?: string;
355
363
  includeFullDiff?: boolean;
356
364
  };
357
365
  export type DiffClassSignaturesOutput = {
@@ -417,6 +425,7 @@ export type ValidateMixinInput = {
417
425
  sourcePriority?: MappingSourcePriority;
418
426
  scope?: ArtifactScope;
419
427
  projectPath?: string;
428
+ gradleUserHome?: string;
420
429
  preferProjectVersion?: boolean;
421
430
  minSeverity?: "error" | "warning" | "all";
422
431
  hideUncertain?: boolean;
@@ -488,6 +497,7 @@ export type ValidateAccessWidenerInput = {
488
497
  mapping?: SourceMapping;
489
498
  sourcePriority?: MappingSourcePriority;
490
499
  projectPath?: string;
500
+ gradleUserHome?: string;
491
501
  scope?: ArtifactScope;
492
502
  preferProjectVersion?: boolean;
493
503
  };
@@ -498,6 +508,7 @@ export type ValidateAccessTransformerInput = {
498
508
  atNamespace?: AccessTransformerNamespace;
499
509
  sourcePriority?: MappingSourcePriority;
500
510
  projectPath?: string;
511
+ gradleUserHome?: string;
501
512
  scope?: ArtifactScope;
502
513
  preferProjectVersion?: boolean;
503
514
  };
@@ -532,6 +543,7 @@ export declare class SourceService {
532
543
  version: string;
533
544
  awNamespace: SourceMapping;
534
545
  projectPath?: string;
546
+ gradleUserHome?: string;
535
547
  scope?: ArtifactScope;
536
548
  preferProjectVersion?: boolean;
537
549
  }): Promise<RuntimeValidationProvenance<SourceMapping>>;
@@ -543,6 +555,7 @@ export declare class SourceService {
543
555
  version: string;
544
556
  atNamespace: AccessTransformerNamespace;
545
557
  projectPath?: string;
558
+ gradleUserHome?: string;
546
559
  scope?: ArtifactScope;
547
560
  preferProjectVersion?: boolean;
548
561
  }): Promise<RuntimeValidationProvenance<AccessTransformerNamespace>>;
@@ -555,15 +568,18 @@ export declare class SourceService {
555
568
  discoverVersionSourceJar(input: {
556
569
  version: string;
557
570
  projectPath?: string;
571
+ gradleUserHome?: string;
558
572
  }): ReturnType<typeof artifactResolver.discoverVersionSourceJar>;
559
573
  discoverAccessWidenerRuntimeCandidates(input: {
560
574
  version: string;
561
575
  projectPath?: string;
576
+ gradleUserHome?: string;
562
577
  requestedScope: ArtifactScope;
563
578
  }): ReturnType<typeof artifactResolver.discoverAccessWidenerRuntimeCandidates>;
564
579
  discoverAccessTransformerRuntimeCandidates(input: {
565
580
  version: string;
566
581
  projectPath?: string;
582
+ gradleUserHome?: string;
567
583
  requestedScope: ArtifactScope;
568
584
  atNamespace: AccessTransformerNamespace;
569
585
  loader: import("./workspace-mapping-service.js").WorkspaceProjectLoader | "unknown";
@@ -597,7 +613,7 @@ export declare class SourceService {
597
613
  getClassMembers(input: GetClassMembersInput): Promise<GetClassMembersOutput>;
598
614
  validateMixin(input: ValidateMixinInput, options?: ValidateMixinOptions): Promise<ValidateMixinOutput>;
599
615
  validateMixinSingle(input: validateMixinModule.ValidateMixinSingleInput): Promise<MixinValidationResult>;
600
- remapSignatureMembers(members: SignatureMember[], kind: "field" | "method", version: string, sourceMapping: SourceMapping, targetMapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], projectPath?: string): Promise<{
616
+ remapSignatureMembers(members: SignatureMember[], kind: "field" | "method", version: string, sourceMapping: SourceMapping, targetMapping: SourceMapping, sourcePriority: MappingSourcePriority | undefined, warnings: string[], projectPath?: string, gradleUserHome?: string): Promise<{
601
617
  members: SignatureMember[];
602
618
  failedNames: Set<string>;
603
619
  }>;
@@ -612,6 +628,7 @@ export declare class SourceService {
612
628
  sourceMapping: SourceMapping;
613
629
  targetMapping: SourceMapping;
614
630
  sourcePriority: MappingSourcePriority | undefined;
631
+ gradleUserHome?: string;
615
632
  warnings: string[];
616
633
  context: string;
617
634
  }): Promise<string>;
@@ -176,8 +176,8 @@ export class SourceService {
176
176
  async validateMixinSingle(input) {
177
177
  return validateMixinModule.validateMixinSingle(this, input);
178
178
  }
179
- async remapSignatureMembers(members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath) {
180
- return lifecycle.remapSignatureMembers(this, members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath);
179
+ async remapSignatureMembers(members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath, gradleUserHome) {
180
+ return lifecycle.remapSignatureMembers(this, members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings, projectPath, gradleUserHome);
181
181
  }
182
182
  async validateAccessWidener(input) {
183
183
  return accessValidate.validateAccessWidener(this, input);
@@ -1,10 +1,10 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { createError, ERROR_CODES } from "./errors.js";
3
- import { assertJavaAvailable, runJavaProcess } from "./java-process.js";
3
+ import { javaRunner } from "./java-process.js";
4
4
  import { log } from "./logger.js";
5
5
  export async function remapJar(tinyRemapperJarPath, options) {
6
6
  const { inputJar, outputJar, mappingsFile, fromNamespace, toNamespace, threads = 4, rebuildSourceFilenames = false, timeoutMs = 600_000, maxMemoryMb = 4096 } = options;
7
- await assertJavaAvailable();
7
+ await javaRunner.assertAvailable();
8
8
  log("info", "remap.start", {
9
9
  inputJar,
10
10
  outputJar,
@@ -21,7 +21,7 @@ export async function remapJar(tinyRemapperJarPath, options) {
21
21
  args.push("--rebuildSourceFilenames");
22
22
  }
23
23
  try {
24
- const result = await runJavaProcess({
24
+ const result = await javaRunner.run({
25
25
  jarPath: tinyRemapperJarPath,
26
26
  args,
27
27
  timeoutMs,
@@ -1,4 +1,4 @@
1
- export declare const TOOL_SURFACE_SECTION_IDS: readonly ["v3-entry-tools", "source-exploration", "version-comparison-symbol-tracking", "mapping-symbols", "nbt-utilities", "mod-analysis", "validation", "registry-diagnostics", "batch-lookup"];
1
+ export declare const TOOL_SURFACE_SECTION_IDS: readonly ["top-level-workflow-tools", "source-exploration", "version-comparison-symbol-tracking", "mapping-symbols", "nbt-utilities", "mod-analysis", "validation", "registry-diagnostics", "batch-lookup"];
2
2
  export type ToolSurfaceSectionId = (typeof TOOL_SURFACE_SECTION_IDS)[number];
3
3
  export type ToolSurfaceLocale = "en" | "ja";
4
4
  export declare function renderToolSurfaceSection(locale: ToolSurfaceLocale, sectionId: ToolSurfaceSectionId): string;
@@ -1,5 +1,5 @@
1
1
  export const TOOL_SURFACE_SECTION_IDS = [
2
- "v3-entry-tools",
2
+ "top-level-workflow-tools",
3
3
  "source-exploration",
4
4
  "version-comparison-symbol-tracking",
5
5
  "mapping-symbols",
@@ -10,7 +10,7 @@ export const TOOL_SURFACE_SECTION_IDS = [
10
10
  "batch-lookup"
11
11
  ];
12
12
  const SECTION_ROWS = {
13
- "v3-entry-tools": {
13
+ "top-level-workflow-tools": {
14
14
  en: [
15
15
  "| `inspect-minecraft` | Inspect versions, artifacts, classes, files, source text, and workspace-aware lookup flows |",
16
16
  "| `analyze-symbol` | Handle symbol existence checks, namespace mapping, lifecycle tracing, workspace symbol resolution, and API overviews |",