@adhisang/minecraft-modding-mcp 4.1.0 → 4.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +21 -2
  2. package/README.md +9 -2
  3. package/dist/entry-tools/analyze-symbol-service.d.ts +12 -0
  4. package/dist/entry-tools/analyze-symbol-service.js +7 -0
  5. package/dist/entry-tools/batch-class-members-service.d.ts +1 -0
  6. package/dist/entry-tools/batch-class-members-service.js +2 -0
  7. package/dist/entry-tools/batch-class-source-service.d.ts +1 -0
  8. package/dist/entry-tools/batch-class-source-service.js +2 -0
  9. package/dist/entry-tools/batch-mappings-service.d.ts +1 -0
  10. package/dist/entry-tools/batch-mappings-service.js +1 -0
  11. package/dist/entry-tools/batch-symbol-exists-service.d.ts +1 -0
  12. package/dist/entry-tools/batch-symbol-exists-service.js +2 -0
  13. package/dist/entry-tools/compare-minecraft-service.d.ts +9 -0
  14. package/dist/entry-tools/compare-minecraft-service.js +3 -1
  15. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +1 -0
  16. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +3 -1
  17. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +1 -0
  18. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +1 -0
  19. package/dist/entry-tools/inspect-minecraft/handlers/search.js +2 -1
  20. package/dist/entry-tools/inspect-minecraft/internal.d.ts +17 -0
  21. package/dist/entry-tools/inspect-minecraft/internal.js +10 -0
  22. package/dist/entry-tools/inspect-minecraft-service.d.ts +40 -0
  23. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +6 -1
  24. package/dist/entry-tools/validate-project/cases/project-summary.js +83 -9
  25. package/dist/entry-tools/validate-project/internal.d.ts +29 -18
  26. package/dist/entry-tools/validate-project/internal.js +37 -19
  27. package/dist/entry-tools/validate-project-service.d.ts +15 -1
  28. package/dist/entry-tools/validate-project-service.js +3 -2
  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/gradle-paths.d.ts +8 -3
  32. package/dist/gradle-paths.js +34 -5
  33. package/dist/index.js +29 -9
  34. package/dist/mapping/loaders/tiny-loom.d.ts +1 -1
  35. package/dist/mapping/loaders/tiny-loom.js +5 -2
  36. package/dist/mapping/types.d.ts +5 -0
  37. package/dist/mapping-service.d.ts +6 -1
  38. package/dist/mapping-service.js +26 -18
  39. package/dist/mixin-validator.d.ts +1 -1
  40. package/dist/mixin-validator.js +1 -1
  41. package/dist/source/access-validate.js +12 -8
  42. package/dist/source/artifact-resolver.d.ts +7 -1
  43. package/dist/source/artifact-resolver.js +120 -7
  44. package/dist/source/class-source/members-builder.d.ts +1 -0
  45. package/dist/source/class-source/members-builder.js +1 -1
  46. package/dist/source/class-source.d.ts +1 -0
  47. package/dist/source/class-source.js +9 -1
  48. package/dist/source/indexer.js +17 -0
  49. package/dist/source/lifecycle/diff.js +6 -6
  50. package/dist/source/lifecycle/mapping-helpers.d.ts +3 -3
  51. package/dist/source/lifecycle/mapping-helpers.js +17 -9
  52. package/dist/source/lifecycle/trace.js +2 -2
  53. package/dist/source/search.js +1 -0
  54. package/dist/source/state.d.ts +5 -0
  55. package/dist/source/state.js +5 -0
  56. package/dist/source/symbol-resolver.js +4 -1
  57. package/dist/source/validate-mixin/pipeline/mapping-health.js +2 -1
  58. package/dist/source/validate-mixin/pipeline/resolve.js +1 -0
  59. package/dist/source/validate-mixin/pipeline/target-lookup.js +12 -7
  60. package/dist/source-resolver.d.ts +1 -0
  61. package/dist/source-resolver.js +1 -1
  62. package/dist/source-service.d.ts +35 -1
  63. package/dist/source-service.js +5 -2
  64. package/dist/tool-contract-manifest.d.ts +1 -1
  65. package/dist/tool-contract-manifest.js +2 -2
  66. package/dist/tool-schemas.d.ts +91 -0
  67. package/dist/tool-schemas.js +20 -0
  68. package/docs/README-ja.md +4 -2
  69. package/docs/tool-reference.md +13 -7
  70. package/package.json +15 -3
@@ -60,6 +60,7 @@ export type FindMappingInput = {
60
60
  targetMapping: SourceMapping;
61
61
  sourcePriority?: MappingSourcePriority;
62
62
  projectPath?: string;
63
+ gradleUserHome?: string;
63
64
  disambiguation?: {
64
65
  ownerHint?: string;
65
66
  descriptorHint?: string;
@@ -73,6 +74,7 @@ export type EnsureMappingAvailableInput = {
73
74
  targetMapping: SourceMapping;
74
75
  sourcePriority?: MappingSourcePriority;
75
76
  projectPath?: string;
77
+ gradleUserHome?: string;
76
78
  };
77
79
  export type EnsureMappingAvailableOutput = {
78
80
  transformChain: string[];
@@ -88,6 +90,7 @@ export type ResolveMethodMappingExactInput = {
88
90
  targetMapping: SourceMapping;
89
91
  sourcePriority?: MappingSourcePriority;
90
92
  projectPath?: string;
93
+ gradleUserHome?: string;
91
94
  maxCandidates?: number;
92
95
  };
93
96
  export type ResolveMethodMappingExactOutput = SymbolResolutionOutput;
@@ -97,6 +100,7 @@ export type ClassApiMatrixInput = {
97
100
  className: string;
98
101
  classNameMapping: SourceMapping;
99
102
  sourcePriority?: MappingSourcePriority;
103
+ gradleUserHome?: string;
100
104
  includeKinds?: ClassApiMatrixKind[];
101
105
  maxRows?: number;
102
106
  };
@@ -134,6 +138,7 @@ export type SymbolExistenceInput = {
134
138
  descriptor?: string;
135
139
  sourceMapping: SourceMapping;
136
140
  sourcePriority?: MappingSourcePriority;
141
+ gradleUserHome?: string;
137
142
  nameMode?: "fqcn" | "auto";
138
143
  signatureMode?: "exact" | "name-only";
139
144
  maxCandidates?: number;
@@ -31,12 +31,17 @@ export declare class MappingService {
31
31
  private provenanceForPath;
32
32
  /**
33
33
  * Probe the mapping graph health for a given version.
34
- * Returns availability of mojang mappings, tiny mappings, and member remap paths.
34
+ *
35
+ * `tinyMappingsAvailable` reports whether Tiny is sufficient for the
36
+ * request: `true` when Tiny is not required (obfuscated/mojang) or is
37
+ * loaded, `false` only when intermediary/yarn was requested and Tiny is
38
+ * unavailable.
35
39
  */
36
40
  checkMappingHealth(input: {
37
41
  version: string;
38
42
  requestedMapping: SourceMapping;
39
43
  sourcePriority?: MappingSourcePriority;
44
+ gradleUserHome?: string;
40
45
  }): Promise<{
41
46
  mojangMappingsAvailable: boolean;
42
47
  tinyMappingsAvailable: boolean;
@@ -3,6 +3,7 @@ import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { buildSuggestedCall } from "./build-suggested-call.js";
5
5
  import { createError, ERROR_CODES } from "./errors.js";
6
+ import { resolveGradleUserHomePath } from "./gradle-paths.js";
6
7
  import { defaultDownloadPath, downloadToCache } from "./repo-downloader.js";
7
8
  import { collectMatchedJarEntriesAsUtf8 } from "./source-jar-reader.js";
8
9
  import { VersionService, isUnobfuscatedVersion } from "./version-service.js";
@@ -112,7 +113,7 @@ export class MappingService {
112
113
  warnings: []
113
114
  };
114
115
  }
115
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
116
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
116
117
  const path = namespacePath(graph, sourceMapping, targetMapping);
117
118
  if (!path) {
118
119
  return {
@@ -254,7 +255,7 @@ export class MappingService {
254
255
  warnings: []
255
256
  };
256
257
  }
257
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
258
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
258
259
  const path = namespacePath(graph, sourceMapping, targetMapping);
259
260
  if (!path) {
260
261
  throw createError({
@@ -338,7 +339,7 @@ export class MappingService {
338
339
  warnings: []
339
340
  };
340
341
  }
341
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
342
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
342
343
  const path = namespacePath(graph, sourceMapping, targetMapping);
343
344
  if (!path) {
344
345
  return {
@@ -448,7 +449,7 @@ export class MappingService {
448
449
  });
449
450
  }
450
451
  const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
451
- const graph = await this.loadGraph(version, priority, "full");
452
+ const graph = await this.loadGraph(version, priority, "full", undefined, input.gradleUserHome);
452
453
  const warnings = [...graph.warnings];
453
454
  const includeKinds = normalizeIncludedKinds(input.includeKinds);
454
455
  const pathCache = new Map();
@@ -669,7 +670,7 @@ export class MappingService {
669
670
  querySymbol
670
671
  };
671
672
  })();
672
- const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full");
673
+ const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full", undefined, input.gradleUserHome);
673
674
  const warnings = [...graph.warnings];
674
675
  const records = collectTargetRecords(graph, sourceMapping);
675
676
  if (records.length === 0) {
@@ -926,11 +927,16 @@ export class MappingService {
926
927
  }
927
928
  /**
928
929
  * Probe the mapping graph health for a given version.
929
- * Returns availability of mojang mappings, tiny mappings, and member remap paths.
930
+ *
931
+ * `tinyMappingsAvailable` reports whether Tiny is sufficient for the
932
+ * request: `true` when Tiny is not required (obfuscated/mojang) or is
933
+ * loaded, `false` only when intermediary/yarn was requested and Tiny is
934
+ * unavailable.
930
935
  */
931
936
  async checkMappingHealth(input) {
932
937
  const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
933
938
  const degradations = [];
939
+ const needsTinyMappings = input.requestedMapping === "intermediary" || input.requestedMapping === "yarn";
934
940
  if (isUnobfuscatedVersion(input.version)) {
935
941
  const requestFulfillable = input.requestedMapping === "obfuscated" || input.requestedMapping === "mojang";
936
942
  if (!requestFulfillable) {
@@ -938,19 +944,19 @@ export class MappingService {
938
944
  }
939
945
  return {
940
946
  mojangMappingsAvailable: true,
941
- tinyMappingsAvailable: false,
947
+ tinyMappingsAvailable: !needsTinyMappings,
942
948
  memberRemapAvailable: requestFulfillable,
943
949
  degradations
944
950
  };
945
951
  }
946
952
  let graph;
947
953
  try {
948
- graph = await this.loadGraph(input.version, priority, "full");
954
+ graph = await this.loadGraph(input.version, priority, needsTinyMappings ? "full" : "obfuscated-mojang-only", undefined, input.gradleUserHome);
949
955
  }
950
956
  catch {
951
957
  return {
952
958
  mojangMappingsAvailable: false,
953
- tinyMappingsAvailable: false,
959
+ tinyMappingsAvailable: !needsTinyMappings,
954
960
  memberRemapAvailable: false,
955
961
  degradations: ["Mapping graph could not be loaded."]
956
962
  };
@@ -967,7 +973,7 @@ export class MappingService {
967
973
  if (!mojangAvailable) {
968
974
  degradations.push("Mojang client mappings are not available for this version.");
969
975
  }
970
- if (!tinyAvailable) {
976
+ if (needsTinyMappings && !tinyAvailable) {
971
977
  degradations.push("No intermediary/yarn tiny mappings were found for this version.");
972
978
  }
973
979
  // Check if member remap path exists (requestedMapping → obfuscated)
@@ -984,14 +990,15 @@ export class MappingService {
984
990
  }
985
991
  return {
986
992
  mojangMappingsAvailable: mojangAvailable,
987
- tinyMappingsAvailable: tinyAvailable,
993
+ tinyMappingsAvailable: needsTinyMappings ? tinyAvailable : true,
988
994
  memberRemapAvailable,
989
995
  degradations
990
996
  };
991
997
  }
992
- async loadGraph(version, priority, mode, projectPath) {
998
+ async loadGraph(version, priority, mode, projectPath, gradleUserHome) {
993
999
  const effectiveProjectPath = effectiveLoomSearchProjectPath(projectPath);
994
- const cacheKey = `${version}|${priority}|${mode}|${effectiveProjectPath ?? ""}`;
1000
+ const effectiveGradleUserHome = resolveGradleUserHomePath(gradleUserHome);
1001
+ const cacheKey = `${version}|${priority}|${mode}|${effectiveProjectPath ?? ""}|${effectiveGradleUserHome}`;
995
1002
  const cached = this.graphCache.get(cacheKey);
996
1003
  if (cached) {
997
1004
  this.graphCache.delete(cacheKey);
@@ -1002,7 +1009,7 @@ export class MappingService {
1002
1009
  if (existingLock) {
1003
1010
  return existingLock;
1004
1011
  }
1005
- const buildPromise = this.buildGraph(version, priority, mode, effectiveProjectPath);
1012
+ const buildPromise = this.buildGraph(version, priority, mode, effectiveProjectPath, effectiveGradleUserHome);
1006
1013
  this.buildLocks.set(cacheKey, buildPromise);
1007
1014
  try {
1008
1015
  const built = await buildPromise;
@@ -1014,7 +1021,7 @@ export class MappingService {
1014
1021
  this.buildLocks.delete(cacheKey);
1015
1022
  }
1016
1023
  }
1017
- async buildGraph(version, priority, mode, projectPath) {
1024
+ async buildGraph(version, priority, mode, projectPath, gradleUserHome) {
1018
1025
  if (isUnobfuscatedVersion(version)) {
1019
1026
  return {
1020
1027
  version,
@@ -1047,7 +1054,7 @@ export class MappingService {
1047
1054
  const deferredTinyWarnings = [];
1048
1055
  for (const source of mappingSourceOrder(priority)) {
1049
1056
  const tinyLoad = source === "loom-cache"
1050
- ? await this.loadTinyPairsFromLoom(version, projectPath)
1057
+ ? await this.loadTinyPairsFromLoom(version, projectPath, gradleUserHome)
1051
1058
  : await this.loadTinyPairsFromMaven(version);
1052
1059
  if (tinyLoad.pairs.size === 0) {
1053
1060
  deferredTinyWarnings.push(...tinyLoad.warnings);
@@ -1090,8 +1097,8 @@ export class MappingService {
1090
1097
  async loadMojangPairs(version) {
1091
1098
  return loadMojangPairs(this.loaderDeps(), version);
1092
1099
  }
1093
- async loadTinyPairsFromLoom(version, projectPath) {
1094
- return loadTinyPairsFromLoom(version, projectPath);
1100
+ async loadTinyPairsFromLoom(version, projectPath, gradleUserHome) {
1101
+ return loadTinyPairsFromLoom(version, projectPath, gradleUserHome);
1095
1102
  }
1096
1103
  async loadTinyPairsFromMaven(version) {
1097
1104
  return loadTinyPairsFromMaven(this.loaderDeps(), version);
@@ -1147,6 +1154,7 @@ export class MappingService {
1147
1154
  input.targetMapping,
1148
1155
  input.sourcePriority ?? "",
1149
1156
  effectiveLoomSearchProjectPath(input.projectPath) ?? "",
1157
+ resolveGradleUserHomePath(input.gradleUserHome),
1150
1158
  effectiveSignatureMode,
1151
1159
  String(input.maxCandidates ?? ""),
1152
1160
  JSON.stringify(input.disambiguation ?? "")
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public barrel for the mixin validation engine. The implementation lives
3
3
  * under `src/mixin/`; this file preserves the historical entry point used
4
- * by source-service, entry tools, and tests.
4
+ * by source-service, top-level workflow tools, and tests.
5
5
  */
6
6
  export type { AccessTransformerValidationResult, AccessWidenerValidationResult, AggregatedWarningGroup, ConfidenceBreakdown, ConfidencePenalty, IssueCategory, IssueConfidence, MappingHealthReport, MixinStageBudgets, MixinValidationProvenance, MixinValidationResult, ResolutionPath, ResolvedMember, ResolvedTargetMembers, StructuredWarning, TargetOutcome, ValidationIssue, ValidationStatus, ValidationSummary } from "./mixin/types.js";
7
7
  export { loadMixinStageBudgets } from "./mixin/types.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public barrel for the mixin validation engine. The implementation lives
3
3
  * under `src/mixin/`; this file preserves the historical entry point used
4
- * by source-service, entry tools, and tests.
4
+ * by source-service, top-level workflow tools, and tests.
5
5
  */
6
6
  export { loadMixinStageBudgets } from "./mixin/types.js";
7
7
  export { accessLevelFromFlags, buildQuickSummary, computeConfidenceBreakdown, computeFalsePositiveRisk, computeValidationStatus, extractMethodDescriptor, extractMethodName, levenshteinDistance, refreshMixinValidationOutcome, suggestSimilar, summarizeResolvedMembers } from "./mixin/helpers.js";
@@ -56,6 +56,7 @@ export async function validateAccessWidener(svc, input) {
56
56
  version,
57
57
  awNamespace,
58
58
  projectPath: input.projectPath,
59
+ gradleUserHome: input.gradleUserHome,
59
60
  scope: input.scope,
60
61
  preferProjectVersion: input.preferProjectVersion
61
62
  });
@@ -85,7 +86,8 @@ export async function validateAccessWidener(svc, input) {
85
86
  sourceMapping: awNamespace,
86
87
  targetMapping: lookupMapping,
87
88
  sourcePriority: input.sourcePriority,
88
- projectPath: input.projectPath
89
+ projectPath: input.projectPath,
90
+ gradleUserHome: input.gradleUserHome
89
91
  });
90
92
  if (mapped.resolved && mapped.resolvedSymbol) {
91
93
  lookupFqn = mapped.resolvedSymbol.name;
@@ -110,9 +112,9 @@ export async function validateAccessWidener(svc, input) {
110
112
  let fields = sig.fields;
111
113
  if (needsLookupMapping) {
112
114
  const [ctorResult, methodResult, fieldResult] = await Promise.all([
113
- svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath),
114
- svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath),
115
- svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath)
115
+ svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
116
+ svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
117
+ svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome)
116
118
  ]);
117
119
  constructors = ctorResult.members;
118
120
  methods = methodResult.members;
@@ -163,6 +165,7 @@ export async function validateAccessTransformer(svc, input) {
163
165
  version,
164
166
  atNamespace,
165
167
  projectPath: input.projectPath,
168
+ gradleUserHome: input.gradleUserHome,
166
169
  scope: input.scope,
167
170
  preferProjectVersion: input.preferProjectVersion
168
171
  });
@@ -197,7 +200,8 @@ export async function validateAccessTransformer(svc, input) {
197
200
  sourceMapping: atNamespace,
198
201
  targetMapping: lookupMapping,
199
202
  sourcePriority: input.sourcePriority,
200
- projectPath: input.projectPath
203
+ projectPath: input.projectPath,
204
+ gradleUserHome: input.gradleUserHome
201
205
  });
202
206
  if (mapped.resolved && mapped.resolvedSymbol) {
203
207
  lookupFqn = mapped.resolvedSymbol.name;
@@ -223,9 +227,9 @@ export async function validateAccessTransformer(svc, input) {
223
227
  let fields = sig.fields;
224
228
  if (needsLookupMapping && isSourceMappingNamespace(atNamespace) && isSourceMappingNamespace(lookupMapping)) {
225
229
  const [ctorResult, methodResult, fieldResult] = await Promise.all([
226
- svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath),
227
- svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath),
228
- svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath)
230
+ svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
231
+ svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
232
+ svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome)
229
233
  ]);
230
234
  constructors = ctorResult.members;
231
235
  methods = methodResult.members;
@@ -1,5 +1,5 @@
1
1
  import type { SourceService } from "../source-service.js";
2
- import type { ArtifactContentsSummary, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
2
+ import type { ArtifactContentsSummary, ProbeMinecraftArtifactInput, ProbeMinecraftArtifactOutput, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
3
3
  import type { AccessTransformerNamespace, ArtifactProvenance, ArtifactScope, ArtifactTargetKind, ResolvedSourceArtifact, RuntimeValidationProvenance, SourceMapping } from "../types.js";
4
4
  import type { WorkspaceProjectLoader } from "../workspace-mapping-service.js";
5
5
  export type VersionSourceDiscovery = {
@@ -32,10 +32,13 @@ export declare function hasExactVersionToken(path: string, version: string): boo
32
32
  export declare function discoverVersionSourceJar(_svc: SourceService, input: {
33
33
  version: string;
34
34
  projectPath?: string;
35
+ gradleUserHome?: string;
35
36
  }): Promise<VersionSourceDiscovery>;
37
+ export declare function probeMinecraftArtifact(svc: SourceService, input: ProbeMinecraftArtifactInput): Promise<ProbeMinecraftArtifactOutput>;
36
38
  export declare function discoverAccessWidenerRuntimeCandidates(_svc: SourceService, input: {
37
39
  version: string;
38
40
  projectPath?: string;
41
+ gradleUserHome?: string;
39
42
  requestedScope: ArtifactScope;
40
43
  }): Promise<{
41
44
  searchedPaths: string[];
@@ -45,6 +48,7 @@ export declare function discoverAccessWidenerRuntimeCandidates(_svc: SourceServi
45
48
  export declare function discoverAccessTransformerRuntimeCandidates(_svc: SourceService, input: {
46
49
  version: string;
47
50
  projectPath?: string;
51
+ gradleUserHome?: string;
48
52
  requestedScope: ArtifactScope;
49
53
  atNamespace: AccessTransformerNamespace;
50
54
  loader: WorkspaceProjectLoader | "unknown";
@@ -57,6 +61,7 @@ export declare function resolveAccessWidenerRuntimeArtifact(svc: SourceService,
57
61
  version: string;
58
62
  awNamespace: SourceMapping;
59
63
  projectPath?: string;
64
+ gradleUserHome?: string;
60
65
  scope?: ArtifactScope;
61
66
  preferProjectVersion?: boolean;
62
67
  }): Promise<RuntimeValidationProvenance<SourceMapping>>;
@@ -68,6 +73,7 @@ export declare function resolveAccessTransformerRuntimeArtifact(svc: SourceServi
68
73
  version: string;
69
74
  atNamespace: AccessTransformerNamespace;
70
75
  projectPath?: string;
76
+ gradleUserHome?: string;
71
77
  scope?: ArtifactScope;
72
78
  preferProjectVersion?: boolean;
73
79
  }): Promise<RuntimeValidationProvenance<AccessTransformerNamespace>>;
@@ -7,8 +7,9 @@ import { log } from "../logger.js";
7
7
  import { applyMappingPipeline } from "../mapping-pipeline-service.js";
8
8
  import { parseCoordinate } from "../maven-resolver.js";
9
9
  import { resolveMojangTinyFile } from "../mojang-tiny-mapping-service.js";
10
+ import { artifactSignatureFromFile } from "../path-resolver.js";
10
11
  import { detectFabricLikeInputNamespace, listJavaEntries } from "../source-jar-reader.js";
11
- import { resolveSourceTarget as resolveSourceTargetInternal } from "../source-resolver.js";
12
+ import { artifactIdForJar, resolveSourceTarget as resolveSourceTargetInternal } from "../source-resolver.js";
12
13
  import { resolveTinyRemapperJar } from "../tiny-remapper-resolver.js";
13
14
  import { isUnobfuscatedVersion } from "../version-service.js";
14
15
  import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
@@ -137,7 +138,10 @@ function buildProvenance(input) {
137
138
  }
138
139
  export async function discoverVersionSourceJar(_svc, input) {
139
140
  const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
140
- const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
141
+ const searchRoots = buildVersionSourceSearchRoots({
142
+ projectPath: normalizedProjectPath,
143
+ gradleUserHome: input.gradleUserHome
144
+ });
141
145
  const searchedPaths = [];
142
146
  const candidates = [];
143
147
  const seen = new Set();
@@ -209,12 +213,112 @@ export async function discoverVersionSourceJar(_svc, input) {
209
213
  selectedHasMinecraftNamespace: selected?.hasMinecraftNamespace
210
214
  };
211
215
  }
216
+ export async function probeMinecraftArtifact(svc, input) {
217
+ let value = input.target.value.trim();
218
+ const warnings = [];
219
+ const requestedMapping = normalizeMapping(input.mapping);
220
+ if (input.preferProjectVersion && input.projectPath) {
221
+ const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(input.projectPath);
222
+ if (detected && detected !== value) {
223
+ warnings.push(`Overriding version "${value}" with project version "${detected}" from gradle.properties.`);
224
+ }
225
+ value = detected ?? value;
226
+ }
227
+ if (!value) {
228
+ throw createError({
229
+ code: ERROR_CODES.INVALID_INPUT,
230
+ message: "target.value must be non-empty.",
231
+ details: { target: input.target }
232
+ });
233
+ }
234
+ const versionJar = await svc.versionService.resolveVersionJar(value);
235
+ const resolvedVersion = versionJar.version;
236
+ const runtimeNamesUnobfuscated = isUnobfuscatedVersion(resolvedVersion);
237
+ warnings.push(`Resolved Minecraft ${versionJar.version} from ${versionJar.clientJarUrl}.`);
238
+ let effectiveMapping = requestedMapping;
239
+ if ((requestedMapping === "intermediary" || requestedMapping === "yarn") &&
240
+ runtimeNamesUnobfuscated) {
241
+ warnings.push(`Version ${resolvedVersion} is unobfuscated; ${requestedMapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
242
+ effectiveMapping = "obfuscated";
243
+ }
244
+ if ((effectiveMapping === "intermediary" || effectiveMapping === "yarn") &&
245
+ !runtimeNamesUnobfuscated) {
246
+ throw createError({
247
+ code: ERROR_CODES.MAPPING_NOT_APPLIED,
248
+ message: `Lightweight artifact probe cannot verify ${effectiveMapping} mapping availability without running the full resolver.`,
249
+ details: {
250
+ requestedMapping: effectiveMapping,
251
+ version: resolvedVersion,
252
+ nextAction: "Use a direct validation task for mapping-sensitive checks, or use mapping=obfuscated for the project-summary artifact probe."
253
+ }
254
+ });
255
+ }
256
+ if (effectiveMapping === "mojang" && !runtimeNamesUnobfuscated) {
257
+ // Match validate-mixin's resolve stage: omitted scope defaults to vanilla,
258
+ // avoiding a workspace-wide source-jar scan that validate-mixin would skip.
259
+ const effectiveScope = input.scope ?? "vanilla";
260
+ if (effectiveScope === "vanilla") {
261
+ throw createError({
262
+ code: ERROR_CODES.MAPPING_NOT_APPLIED,
263
+ message: "Lightweight artifact probe cannot verify mojang mapping with scope=vanilla on obfuscated runtime versions.",
264
+ details: {
265
+ requestedMapping: effectiveMapping,
266
+ version: resolvedVersion,
267
+ nextAction: "Retry with scope=merged and projectPath so the probe can use a Loom source jar, or use mapping=obfuscated."
268
+ }
269
+ });
270
+ }
271
+ const versionSourceDiscovery = await svc.discoverVersionSourceJar({
272
+ version: resolvedVersion,
273
+ projectPath: input.projectPath,
274
+ gradleUserHome: input.gradleUserHome
275
+ });
276
+ if (!versionSourceDiscovery.selectedSourceJarPath) {
277
+ throw createError({
278
+ code: ERROR_CODES.MAPPING_NOT_APPLIED,
279
+ message: "Lightweight artifact probe cannot verify mojang mapping without a source-backed Loom artifact.",
280
+ details: {
281
+ requestedMapping: effectiveMapping,
282
+ version: resolvedVersion,
283
+ searchedPaths: versionSourceDiscovery.searchedPaths,
284
+ candidateArtifacts: versionSourceDiscovery.candidateArtifacts,
285
+ nextAction: "Use mapping=obfuscated for project-summary, or run a direct validation task when full source resolution is required."
286
+ }
287
+ });
288
+ }
289
+ const selectedSourceJarPath = versionSourceDiscovery.selectedSourceJarPath;
290
+ const sourceSignature = artifactSignatureFromFile(selectedSourceJarPath).signature;
291
+ const artifactId = artifactIdForJar("jar", selectedSourceJarPath, sourceSignature);
292
+ warnings.push(`Resolved source-backed artifact from Loom cache candidate: ${selectedSourceJarPath}.`);
293
+ if (versionSourceDiscovery.selectedHasMinecraftNamespace === false) {
294
+ warnings.push(`Source coverage does not include net.minecraft for ${selectedSourceJarPath}; class lookups may fall back to the binary artifact.`);
295
+ }
296
+ if (!hasExactVersionToken(selectedSourceJarPath, value)) {
297
+ warnings.push(`Requested version "${value}" but resolved source jar does not contain exact version string: ${selectedSourceJarPath}`);
298
+ }
299
+ return {
300
+ artifactId,
301
+ mappingApplied: "mojang",
302
+ ...(warnings.length > 0 ? { warnings } : {})
303
+ };
304
+ }
305
+ const binarySignature = artifactSignatureFromFile(versionJar.jarPath).signature;
306
+ const artifactId = artifactIdForJar("jar", versionJar.jarPath, `${binarySignature}:decompile`);
307
+ return {
308
+ artifactId,
309
+ mappingApplied: effectiveMapping,
310
+ ...(warnings.length > 0 ? { warnings } : {})
311
+ };
312
+ }
212
313
  export async function discoverAccessWidenerRuntimeCandidates(_svc, input) {
213
314
  const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
214
315
  const normalizedProjectPathLower = normalizedProjectPath
215
316
  ? normalizePathStyle(normalizedProjectPath).toLowerCase()
216
317
  : undefined;
217
- const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
318
+ const searchRoots = buildVersionSourceSearchRoots({
319
+ projectPath: normalizedProjectPath,
320
+ gradleUserHome: input.gradleUserHome
321
+ });
218
322
  const searchedPaths = [];
219
323
  const candidates = [];
220
324
  const seen = new Set();
@@ -284,7 +388,10 @@ export async function discoverAccessTransformerRuntimeCandidates(_svc, input) {
284
388
  const normalizedProjectPathLower = normalizedProjectPath
285
389
  ? normalizePathStyle(normalizedProjectPath).toLowerCase()
286
390
  : undefined;
287
- const searchRoots = buildLoaderRuntimeSearchRoots(normalizedProjectPath);
391
+ const searchRoots = buildLoaderRuntimeSearchRoots({
392
+ projectPath: normalizedProjectPath,
393
+ gradleUserHome: input.gradleUserHome
394
+ });
288
395
  const searchedPaths = [];
289
396
  const candidates = [];
290
397
  const seen = new Set();
@@ -394,6 +501,7 @@ export async function resolveAccessWidenerRuntimeArtifact(svc, input) {
394
501
  const discovery = await svc.discoverAccessWidenerRuntimeCandidates({
395
502
  version,
396
503
  projectPath: normalizedProjectPath,
504
+ gradleUserHome: input.gradleUserHome,
397
505
  requestedScope
398
506
  });
399
507
  if (!discovery.selected) {
@@ -525,6 +633,7 @@ export async function resolveAccessTransformerRuntimeArtifact(svc, input) {
525
633
  const discovery = await svc.discoverAccessTransformerRuntimeCandidates({
526
634
  version,
527
635
  projectPath: normalizedProjectPath,
636
+ gradleUserHome: input.gradleUserHome,
528
637
  requestedScope,
529
638
  atNamespace: input.atNamespace,
530
639
  loader
@@ -693,7 +802,8 @@ async function computeBinaryRemapGate(svc, input) {
693
802
  const health = await svc.mappingService.checkMappingHealth({
694
803
  version: input.version,
695
804
  requestedMapping: "mojang",
696
- sourcePriority: input.sourcePriority
805
+ sourcePriority: input.sourcePriority,
806
+ gradleUserHome: input.gradleUserHome
697
807
  });
698
808
  mojangAvailable = health.mojangMappingsAvailable;
699
809
  }
@@ -929,7 +1039,8 @@ export async function resolveArtifact(svc, input) {
929
1039
  scope !== "vanilla") {
930
1040
  versionSourceDiscovery = await svc.discoverVersionSourceJar({
931
1041
  version: resolvedVersion,
932
- projectPath: input.projectPath
1042
+ projectPath: input.projectPath,
1043
+ gradleUserHome: input.gradleUserHome
933
1044
  });
934
1045
  if (versionSourceDiscovery.selectedSourceJarPath) {
935
1046
  resolvedTarget = {
@@ -948,6 +1059,7 @@ export async function resolveArtifact(svc, input) {
948
1059
  version: resolvedVersion,
949
1060
  targetKind: kind,
950
1061
  sourcePriority: input.sourcePriority,
1062
+ gradleUserHome: input.gradleUserHome,
951
1063
  forceBinaryRemapDisabled: dependencyOrigin
952
1064
  });
953
1065
  if (binaryRemapGate.warnings.length > 0) {
@@ -1040,7 +1152,8 @@ export async function resolveArtifact(svc, input) {
1040
1152
  version: resolved.version,
1041
1153
  sourceMapping: "obfuscated",
1042
1154
  targetMapping: effectiveMapping,
1043
- sourcePriority: input.sourcePriority
1155
+ sourcePriority: input.sourcePriority,
1156
+ gradleUserHome: input.gradleUserHome
1044
1157
  });
1045
1158
  additionalTransformChain.push(...mappingAvailability.transformChain);
1046
1159
  if (mappingAvailability.warnings.length > 0) {
@@ -20,6 +20,7 @@ export type RemapMembersInput = {
20
20
  mappingApplied: SourceMapping;
21
21
  requestedMapping: SourceMapping;
22
22
  sourcePriority: MappingSourcePriority | undefined;
23
+ gradleUserHome?: string;
23
24
  memberPattern: string | undefined;
24
25
  warnings: string[];
25
26
  };
@@ -3,7 +3,7 @@ export async function remapAndCountMembers(svc, input) {
3
3
  if (input.version == null) {
4
4
  return members;
5
5
  }
6
- const result = await svc.remapSignatureMembers(members, kind, input.version, input.mappingApplied, input.requestedMapping, input.sourcePriority, input.warnings);
6
+ const result = await svc.remapSignatureMembers(members, kind, input.version, input.mappingApplied, input.requestedMapping, input.sourcePriority, input.warnings, undefined, input.gradleUserHome);
7
7
  return result.members;
8
8
  };
9
9
  let constructors = await remap(input.signatureConstructors, "method");
@@ -9,6 +9,7 @@ export declare function resolveClassNameForLookup(svc: SourceService, input: {
9
9
  sourceMapping: SourceMapping;
10
10
  targetMapping: SourceMapping;
11
11
  sourcePriority: MappingSourcePriority | undefined;
12
+ gradleUserHome?: string;
12
13
  warnings: string[];
13
14
  context: string;
14
15
  }): Promise<string>;
@@ -108,7 +108,8 @@ export async function resolveClassNameForLookup(svc, input) {
108
108
  name: input.className,
109
109
  sourceMapping: input.sourceMapping,
110
110
  targetMapping: input.targetMapping,
111
- sourcePriority: input.sourcePriority
111
+ sourcePriority: input.sourcePriority,
112
+ gradleUserHome: input.gradleUserHome
112
113
  });
113
114
  if (mapped.resolved && mapped.resolvedSymbol) {
114
115
  return mapped.resolvedSymbol.name;
@@ -401,6 +402,7 @@ export async function getClassSource(svc, input) {
401
402
  sourcePriority: input.sourcePriority,
402
403
  allowDecompile: input.allowDecompile,
403
404
  projectPath: input.projectPath,
405
+ gradleUserHome: input.gradleUserHome,
404
406
  scope: input.scope,
405
407
  preferProjectVersion: input.preferProjectVersion,
406
408
  strictVersion: input.strictVersion
@@ -488,6 +490,7 @@ export async function getClassSource(svc, input) {
488
490
  sourceMapping: requestedMapping,
489
491
  targetMapping: activeMappingApplied,
490
492
  sourcePriority: input.sourcePriority,
493
+ gradleUserHome: input.gradleUserHome,
491
494
  warnings,
492
495
  context: "source lookup"
493
496
  });
@@ -499,6 +502,7 @@ export async function getClassSource(svc, input) {
499
502
  sourceMapping: requestedMapping,
500
503
  targetMapping: activeMappingApplied,
501
504
  sourcePriority: input.sourcePriority,
505
+ gradleUserHome: input.gradleUserHome,
502
506
  warnings,
503
507
  context: "source lookup"
504
508
  });
@@ -528,6 +532,7 @@ export async function getClassSource(svc, input) {
528
532
  sourceMapping: requestedMapping,
529
533
  targetMapping: activeMappingApplied,
530
534
  sourcePriority: input.sourcePriority,
535
+ gradleUserHome: input.gradleUserHome,
531
536
  warnings,
532
537
  context: "source lookup"
533
538
  });
@@ -659,6 +664,7 @@ export async function getClassMembers(svc, input) {
659
664
  sourcePriority: input.sourcePriority,
660
665
  allowDecompile: input.allowDecompile,
661
666
  projectPath: input.projectPath,
667
+ gradleUserHome: input.gradleUserHome,
662
668
  scope: input.scope,
663
669
  preferProjectVersion: input.preferProjectVersion,
664
670
  strictVersion: input.strictVersion
@@ -726,6 +732,7 @@ export async function getClassMembers(svc, input) {
726
732
  sourceMapping: requestedMapping,
727
733
  targetMapping: mappingApplied,
728
734
  sourcePriority: input.sourcePriority,
735
+ gradleUserHome: input.gradleUserHome,
729
736
  warnings,
730
737
  context: "binary lookup"
731
738
  });
@@ -775,6 +782,7 @@ export async function getClassMembers(svc, input) {
775
782
  mappingApplied,
776
783
  requestedMapping,
777
784
  sourcePriority: input.sourcePriority,
785
+ gradleUserHome: input.gradleUserHome,
778
786
  memberPattern,
779
787
  warnings
780
788
  });