@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
package/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.2.1] - 2026-05-23
9
+
10
+ ### Fixed
11
+ - Mojang binary-remap cache writes recover from corrupt `<cacheDir>/remapped/<artifactId>.jar` directories instead of surfacing raw `ENOTEMPTY`, and `manage-cache` can list and delete those corrupt `binary-remap` entries by `selector.artifactId`.
12
+ - `isAppError(value)` now narrows to `AppError` via `instanceof AppError` instead of `Error & { code: string }`. Plain `Error` objects that happen to carry a `code` field no longer pass the guard, so unrelated errors can no longer leak through `AppError`-specific error handling.
13
+ - Java NBT `TAG_String` now encodes/decodes via MUTF-8 to match Mojang's `DataInputStream.readUTF` / `DataOutputStream.writeUTF` contract. Supplementary characters (emoji, supplementary CJK) and the NUL character (`U+0000`) now round-trip correctly. ASCII and common BMP byte sequences are byte-identical to the previous UTF-8 output, so existing NBT payloads are unaffected.
14
+
15
+ ## [4.2.0] - 2026-05-17
16
+
17
+ ### Added
18
+ - Cache-backed source, mapping, validation, batch, and workflow tools now accept `gradleUserHome` so callers can use Loom caches from a non-default Gradle User Home instead of the MCP process default.
19
+
8
20
  ## [4.1.1] - 2026-05-10
9
21
 
10
22
  ### Documentation
@@ -160,8 +172,8 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2
160
172
  ## [3.0.0] - 2026-03-09
161
173
 
162
174
  ### Added
163
- - v3 entry tools: `inspect-minecraft`, `analyze-symbol`, `compare-minecraft`, `analyze-mod`, `validate-project`, and `manage-cache` now provide summary-first starting points for the main Minecraft, symbol, mod, validation, and cache workflows while keeping expert tools available for follow-up work.
164
- - The new v3 entry tools share `detail` / `include` response shaping and always return `result.summary` inside the standard `{ result?, error?, meta }` envelope, reducing default payload size and making next actions explicit.
175
+ - Top-level workflow tools: `inspect-minecraft`, `analyze-symbol`, `compare-minecraft`, `analyze-mod`, `validate-project`, and `manage-cache` now provide summary-first starting points for the main Minecraft, symbol, mod, validation, and cache workflows while keeping expert tools available for follow-up work.
176
+ - The new top-level workflow tools share `detail` / `include` response shaping and always return `result.summary` inside the standard `{ result?, error?, meta }` envelope, reducing default payload size and making next actions explicit.
165
177
  - `analyze-mod` now exposes `executionMode="preview" | "apply"` for safe remap planning and execution, and `manage-cache` now exposes the same preview/apply model for cache deletion, pruning, and rebuild workflows.
166
178
 
167
179
  ### Fixed
package/README.md CHANGED
@@ -134,6 +134,8 @@ Pass environment variables to override defaults:
134
134
  }
135
135
  ```
136
136
 
137
+ When a build used a non-default Gradle User Home, pass `gradleUserHome` on cache-backed source, mapping, validation, and workflow calls. The server treats it as a per-call Gradle User Home and searches `<gradleUserHome>/loom-cache` and `<gradleUserHome>/caches/fabric-loom` before the MCP process default. It does not accept arbitrary Loom cache directories.
138
+
137
139
  ## Start Here
138
140
 
139
141
  These six top-level workflow tools cover the common paths and return summary-first results. They are the best default starting points for agents and MCP clients.
@@ -161,6 +163,8 @@ These notes cover high-frequency decisions during onboarding. For the full pitfa
161
163
  - `validate-project task="project-summary"` propagates `preferProjectVersion=true` across discovered Mixin, Access Widener, and Access Transformer checks. If no version can be resolved from the request or `gradle.properties`, the summary returns recovery guidance instead of guessing.
162
164
  - `validate-mixin` and `validate-project` keep `mapping-health` lightweight for `obfuscated` and `mojang` validation, avoiding full Tiny mapping graph loads unless `intermediary` or `yarn` namespaces are requested.
163
165
  - `validate-project task="project-summary"` uses a lightweight artifact probe for `tasks["minecraft.artifact.resolved"]`; it does not decompile Minecraft or rebuild the source index just to report per-probe status. Set `VALIDATE_PROJECT_TASKS_OFF=1` to omit the additive `tasks` field.
166
+ - If a workspace was built with `GRADLE_USER_HOME=/tmp/...` or another isolated Gradle home, pass that path as `gradleUserHome` so source, mapping, runtime, and project validation lookups use the same Loom cache instead of stale caches under the MCP process home.
167
+ - `manage-cache` reports corrupt Mojang binary-remap cache directories under `cacheKinds: ["binary-remap"]` with `status: "corrupt"`, and can delete them by `selector.artifactId` in preview/apply workflows.
164
168
 
165
169
  ### Inspect Minecraft source from a version
166
170
 
@@ -251,7 +255,7 @@ Start with these top-level workflow tools unless you already know the exact spec
251
255
 
252
256
  ### Top-Level Workflow Tools
253
257
 
254
- <!-- BEGIN GENERATED TOOL TABLE: v3-entry-tools -->
258
+ <!-- BEGIN GENERATED TOOL TABLE: top-level-workflow-tools -->
255
259
  | Tool | Purpose |
256
260
  | --- | --- |
257
261
  | `inspect-minecraft` | Inspect versions, artifacts, classes, files, source text, and workspace-aware lookup flows |
@@ -260,7 +264,7 @@ Start with these top-level workflow tools unless you already know the exact spec
260
264
  | `analyze-mod` | Summarize mod metadata, decompile and search mod code, inspect class source, and preview or apply remaps |
261
265
  | `validate-project` | Summarize workspaces and run direct Mixin, Access Widener, or Access Transformer validation |
262
266
  | `manage-cache` | List, verify, and preview or apply cache cleanup and rebuild operations |
263
- <!-- END GENERATED TOOL TABLE: v3-entry-tools -->
267
+ <!-- END GENERATED TOOL TABLE: top-level-workflow-tools -->
264
268
 
265
269
  ### Source Exploration
266
270
 
@@ -442,6 +442,9 @@ function workspaceCacheEntries(workspaceCache) {
442
442
  }));
443
443
  }
444
444
  async function fileBackedEntries(config, cacheKind) {
445
+ if (cacheKind === "binary-remap") {
446
+ return binaryRemapEntries(config);
447
+ }
445
448
  const root = kindRoot(config, cacheKind);
446
449
  const files = await listFilesRecursive(root);
447
450
  const entries = [];
@@ -466,17 +469,91 @@ async function fileBackedEntries(config, cacheKind) {
466
469
  inUse: filePath.endsWith(".lock") ||
467
470
  filePath.endsWith(".wal") ||
468
471
  filePath.endsWith(".journal"),
469
- ...(cacheKind === "downloads" || cacheKind === "mod-remap" || cacheKind === "binary-remap"
472
+ ...(cacheKind === "downloads" || cacheKind === "mod-remap"
470
473
  ? { jarPath: filePath }
471
- : {}),
472
- ...(cacheKind === "binary-remap"
473
- ? { artifactId: normalizedEntryId.replace(/\.jar$/i, "") }
474
474
  : {})
475
475
  }
476
476
  });
477
477
  }
478
478
  return entries;
479
479
  }
480
+ /**
481
+ * Binary-remap cache entries are keyed by the final artifact id even when the
482
+ * on-disk entry is a corrupt final directory or a leftover temp path.
483
+ */
484
+ function parseBinaryRemapEntryName(name) {
485
+ const legacyTempMatch = /^(.+)\.jar\.tmp\..+$/.exec(name);
486
+ if (legacyTempMatch?.[1]) {
487
+ return { artifactId: legacyTempMatch[1], corrupt: true };
488
+ }
489
+ const tempMatch = /^(.+)\.tmp\.\d+\.\d+\.[A-Za-z0-9_-]+\.jar$/.exec(name);
490
+ if (tempMatch?.[1]) {
491
+ return { artifactId: tempMatch[1], corrupt: true };
492
+ }
493
+ const finalJarMatch = /^(.+)\.jar$/.exec(name);
494
+ if (finalJarMatch?.[1]) {
495
+ return { artifactId: finalJarMatch[1], corrupt: false };
496
+ }
497
+ return undefined;
498
+ }
499
+ async function binaryRemapEntries(config) {
500
+ const root = kindRoot(config, "binary-remap");
501
+ if (!existsSync(root)) {
502
+ return [];
503
+ }
504
+ const entries = [];
505
+ // Do not recurse here: a corrupt `<artifactId>.jar` directory must remain one
506
+ // selectable cache entry so `selector.artifactId` can remove it recursively.
507
+ const dirents = await readdir(root, { withFileTypes: true });
508
+ for (const entry of dirents.sort((left, right) => left.name.localeCompare(right.name))) {
509
+ if (!entry.isFile() && !entry.isDirectory()) {
510
+ continue;
511
+ }
512
+ const parsed = parseBinaryRemapEntryName(entry.name);
513
+ if (!parsed) {
514
+ continue;
515
+ }
516
+ const filePath = join(root, entry.name);
517
+ const fileStat = await stat(filePath);
518
+ const corrupt = parsed.corrupt || entry.isDirectory();
519
+ const sizeBytes = entry.isDirectory()
520
+ ? await directoryFileSizeBytes(filePath)
521
+ : fileStat.size;
522
+ entries.push({
523
+ cacheKind: "binary-remap",
524
+ entryId: entry.name,
525
+ path: filePath,
526
+ sizeBytes,
527
+ status: corrupt ? "corrupt" : "healthy",
528
+ meta: {
529
+ updatedAt: fileStat.mtime.toISOString(),
530
+ version: inferVersion(filePath, entry.name),
531
+ mapping: inferMapping(filePath, entry.name),
532
+ scope: inferScope(filePath, entry.name),
533
+ projectPath: inferProjectPath(filePath, config.pathRuntimeInfo),
534
+ partial: entry.isFile() && fileStat.size === 0,
535
+ corrupt,
536
+ inUse: false,
537
+ jarPath: filePath,
538
+ artifactId: parsed.artifactId
539
+ }
540
+ });
541
+ }
542
+ return entries;
543
+ }
544
+ async function directoryFileSizeBytes(root) {
545
+ const files = await listFilesRecursive(root);
546
+ let totalBytes = 0;
547
+ for (const filePath of files) {
548
+ try {
549
+ totalBytes += (await stat(filePath)).size;
550
+ }
551
+ catch {
552
+ // The entry may disappear during cleanup or a concurrent cache write.
553
+ }
554
+ }
555
+ return totalBytes;
556
+ }
480
557
  export function createCacheRegistry(config) {
481
558
  const workspaceCache = config.workspaceContextCache ?? getProcessWorkspaceContextCache();
482
559
  async function collectEntries(cacheKinds, selector) {
@@ -564,7 +641,9 @@ export function createCacheRegistry(config) {
564
641
  continue;
565
642
  }
566
643
  if (existsSync(entry.path)) {
567
- await rm(entry.path, { force: true });
644
+ // Only binary-remap inventory can return directories as entries;
645
+ // other file-backed kinds keep their existing file-only contract.
646
+ await rm(entry.path, { recursive: entry.cacheKind === "binary-remap", force: true });
568
647
  }
569
648
  }
570
649
  }
@@ -23,6 +23,7 @@ export declare const analyzeSymbolShape: {
23
23
  targetMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
24
24
  classNameMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
25
25
  projectPath: z.ZodOptional<z.ZodString>;
26
+ gradleUserHome: z.ZodOptional<z.ZodString>;
26
27
  signatureMode: z.ZodDefault<z.ZodEnum<["exact", "name-only"]>>;
27
28
  nameMode: z.ZodDefault<z.ZodEnum<["fqcn", "auto"]>>;
28
29
  includeKinds: z.ZodOptional<z.ZodArray<z.ZodEnum<["class", "field", "method"]>, "many">>;
@@ -54,6 +55,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
54
55
  targetMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
55
56
  classNameMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
56
57
  projectPath: z.ZodOptional<z.ZodString>;
58
+ gradleUserHome: z.ZodOptional<z.ZodString>;
57
59
  signatureMode: z.ZodDefault<z.ZodEnum<["exact", "name-only"]>>;
58
60
  nameMode: z.ZodDefault<z.ZodEnum<["fqcn", "auto"]>>;
59
61
  includeKinds: z.ZodOptional<z.ZodArray<z.ZodEnum<["class", "field", "method"]>, "many">>;
@@ -77,6 +79,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
77
79
  sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
78
80
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
79
81
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
82
+ gradleUserHome?: string | undefined;
80
83
  detail?: "full" | "summary" | "standard" | undefined;
81
84
  include?: string[] | undefined;
82
85
  includeKinds?: ("class" | "field" | "method")[] | undefined;
@@ -94,6 +97,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
94
97
  sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
95
98
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
96
99
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
100
+ gradleUserHome?: string | undefined;
97
101
  nameMode?: "auto" | "fqcn" | undefined;
98
102
  detail?: "full" | "summary" | "standard" | undefined;
99
103
  include?: string[] | undefined;
@@ -117,6 +121,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
117
121
  sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
118
122
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
119
123
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
124
+ gradleUserHome?: string | undefined;
120
125
  detail?: "full" | "summary" | "standard" | undefined;
121
126
  include?: string[] | undefined;
122
127
  includeKinds?: ("class" | "field" | "method")[] | undefined;
@@ -134,6 +139,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
134
139
  sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
135
140
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
136
141
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
142
+ gradleUserHome?: string | undefined;
137
143
  nameMode?: "auto" | "fqcn" | undefined;
138
144
  detail?: "full" | "summary" | "standard" | undefined;
139
145
  include?: string[] | undefined;
@@ -152,6 +158,7 @@ type AnalyzeSymbolDeps = {
152
158
  descriptor?: string;
153
159
  sourceMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
154
160
  sourcePriority?: "loom-first" | "maven-first";
161
+ gradleUserHome?: string;
155
162
  nameMode?: "fqcn" | "auto";
156
163
  signatureMode?: "exact" | "name-only";
157
164
  maxCandidates?: number;
@@ -164,6 +171,7 @@ type AnalyzeSymbolDeps = {
164
171
  descriptor?: string;
165
172
  sourceMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
166
173
  targetMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
174
+ gradleUserHome?: string;
167
175
  signatureMode?: "exact" | "name-only";
168
176
  maxCandidates?: number;
169
177
  }) => Promise<FindMappingOutput>;
@@ -174,12 +182,14 @@ type AnalyzeSymbolDeps = {
174
182
  descriptor: string;
175
183
  sourceMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
176
184
  targetMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
185
+ gradleUserHome?: string;
177
186
  maxCandidates?: number;
178
187
  }) => Promise<ResolveMethodMappingExactOutput>;
179
188
  traceSymbolLifecycle: (input: {
180
189
  symbol: string;
181
190
  descriptor?: string;
182
191
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn";
192
+ gradleUserHome?: string;
183
193
  toVersion?: string;
184
194
  maxVersions?: number;
185
195
  }) => Promise<TraceSymbolLifecycleOutput>;
@@ -191,6 +201,7 @@ type AnalyzeSymbolDeps = {
191
201
  owner?: string;
192
202
  descriptor?: string;
193
203
  sourceMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
204
+ gradleUserHome?: string;
194
205
  maxCandidates?: number;
195
206
  }) => Promise<ResolveWorkspaceSymbolOutput>;
196
207
  getClassApiMatrix: (input: {
@@ -198,6 +209,7 @@ type AnalyzeSymbolDeps = {
198
209
  className: string;
199
210
  classNameMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
200
211
  includeKinds?: ("class" | "field" | "method")[];
212
+ gradleUserHome?: string;
201
213
  maxRows?: number;
202
214
  }) => Promise<GetClassApiMatrixOutput>;
203
215
  };
@@ -30,6 +30,7 @@ export const analyzeSymbolShape = {
30
30
  targetMapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
31
31
  classNameMapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
32
32
  projectPath: nonEmptyString.optional(),
33
+ gradleUserHome: nonEmptyString.optional(),
33
34
  signatureMode: z.enum(["exact", "name-only"]).default("exact"),
34
35
  nameMode: z.enum(["fqcn", "auto"]).default("fqcn"),
35
36
  includeKinds: z.array(z.enum(["class", "field", "method"])).optional(),
@@ -107,6 +108,7 @@ export class AnalyzeSymbolService {
107
108
  owner: input.subject.owner,
108
109
  descriptor: input.subject.descriptor,
109
110
  sourceMapping: input.sourceMapping ?? "obfuscated",
111
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
110
112
  nameMode: input.nameMode,
111
113
  signatureMode: input.signatureMode,
112
114
  maxCandidates: input.maxCandidates
@@ -152,6 +154,7 @@ export class AnalyzeSymbolService {
152
154
  descriptor: input.subject.descriptor,
153
155
  sourceMapping: input.sourceMapping ?? "obfuscated",
154
156
  targetMapping: input.targetMapping ?? "mojang",
157
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
155
158
  signatureMode: input.signatureMode,
156
159
  maxCandidates: input.maxCandidates
157
160
  });
@@ -202,6 +205,7 @@ export class AnalyzeSymbolService {
202
205
  descriptor: input.subject.descriptor,
203
206
  sourceMapping: input.sourceMapping ?? "obfuscated",
204
207
  targetMapping: input.targetMapping ?? "mojang",
208
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
205
209
  maxCandidates: input.maxCandidates
206
210
  });
207
211
  return {
@@ -243,6 +247,7 @@ export class AnalyzeSymbolService {
243
247
  : input.subject.name,
244
248
  descriptor: input.subject.descriptor,
245
249
  mapping: input.sourceMapping,
250
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
246
251
  toVersion: input.version,
247
252
  maxVersions: 5
248
253
  });
@@ -286,6 +291,7 @@ export class AnalyzeSymbolService {
286
291
  owner: input.subject.owner,
287
292
  descriptor: input.subject.descriptor,
288
293
  sourceMapping: input.sourceMapping ?? "obfuscated",
294
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
289
295
  maxCandidates: input.maxCandidates
290
296
  });
291
297
  return {
@@ -328,6 +334,7 @@ export class AnalyzeSymbolService {
328
334
  className: input.subject.name,
329
335
  classNameMapping,
330
336
  includeKinds: input.includeKinds,
337
+ ...(input.gradleUserHome !== undefined ? { gradleUserHome: input.gradleUserHome } : {}),
331
338
  maxRows: input.maxRows
332
339
  });
333
340
  return {
@@ -19,6 +19,7 @@ export type BatchClassMembersInput = {
19
19
  sourcePriority?: MappingSourcePriority;
20
20
  allowDecompile?: boolean;
21
21
  projectPath?: string;
22
+ gradleUserHome?: string;
22
23
  scope?: ArtifactScope;
23
24
  preferProjectVersion?: boolean;
24
25
  strictVersion?: boolean;
@@ -21,6 +21,7 @@ export class BatchClassMembersService {
21
21
  sourcePriority: input.sourcePriority,
22
22
  allowDecompile: input.allowDecompile,
23
23
  projectPath: input.projectPath,
24
+ gradleUserHome: input.gradleUserHome,
24
25
  scope: input.scope,
25
26
  preferProjectVersion: input.preferProjectVersion,
26
27
  strictVersion: input.strictVersion
@@ -56,6 +57,7 @@ export class BatchClassMembersService {
56
57
  sourcePriority: input.sourcePriority,
57
58
  allowDecompile: input.allowDecompile,
58
59
  projectPath: input.projectPath,
60
+ gradleUserHome: input.gradleUserHome,
59
61
  scope: input.scope,
60
62
  preferProjectVersion: input.preferProjectVersion,
61
63
  strictVersion: input.strictVersion
@@ -17,6 +17,7 @@ export type BatchClassSourceInput = {
17
17
  sourcePriority?: MappingSourcePriority;
18
18
  allowDecompile?: boolean;
19
19
  projectPath?: string;
20
+ gradleUserHome?: string;
20
21
  scope?: ArtifactScope;
21
22
  preferProjectVersion?: boolean;
22
23
  strictVersion?: boolean;
@@ -21,6 +21,7 @@ export class BatchClassSourceService {
21
21
  sourcePriority: input.sourcePriority,
22
22
  allowDecompile: input.allowDecompile,
23
23
  projectPath: input.projectPath,
24
+ gradleUserHome: input.gradleUserHome,
24
25
  scope: input.scope,
25
26
  preferProjectVersion: input.preferProjectVersion,
26
27
  strictVersion: input.strictVersion
@@ -57,6 +58,7 @@ export class BatchClassSourceService {
57
58
  sourcePriority: input.sourcePriority,
58
59
  allowDecompile: input.allowDecompile,
59
60
  projectPath: input.projectPath,
61
+ gradleUserHome: input.gradleUserHome,
60
62
  scope: input.scope,
61
63
  preferProjectVersion: input.preferProjectVersion,
62
64
  strictVersion: input.strictVersion
@@ -23,6 +23,7 @@ export type BatchMappingsInput = {
23
23
  version: string;
24
24
  sourcePriority?: MappingSourcePriority;
25
25
  projectPath?: string;
26
+ gradleUserHome?: string;
26
27
  concurrency?: number;
27
28
  failFast?: boolean;
28
29
  compact?: boolean;
@@ -26,6 +26,7 @@ export class BatchMappingsService {
26
26
  targetMapping: entry.targetMapping,
27
27
  sourcePriority: input.sourcePriority,
28
28
  projectPath: input.projectPath,
29
+ gradleUserHome: input.gradleUserHome,
29
30
  signatureMode: entry.signatureMode,
30
31
  disambiguation: entry.disambiguation,
31
32
  maxCandidates: entry.maxCandidates
@@ -30,6 +30,7 @@ export type BatchSymbolExistsInput = {
30
30
  sourcePriority?: MappingSourcePriority;
31
31
  allowDecompile?: boolean;
32
32
  projectPath?: string;
33
+ gradleUserHome?: string;
33
34
  scope?: ArtifactScope;
34
35
  preferProjectVersion?: boolean;
35
36
  strictVersion?: boolean;
@@ -35,6 +35,7 @@ export class BatchSymbolExistsService {
35
35
  sourcePriority: input.sourcePriority,
36
36
  allowDecompile: input.allowDecompile,
37
37
  projectPath: input.projectPath,
38
+ gradleUserHome: input.gradleUserHome,
38
39
  scope: input.scope,
39
40
  preferProjectVersion: input.preferProjectVersion,
40
41
  strictVersion: input.strictVersion
@@ -71,6 +72,7 @@ export class BatchSymbolExistsService {
71
72
  descriptor: entry.descriptor,
72
73
  sourceMapping: sharedArtifact.sourceMapping,
73
74
  sourcePriority: input.sourcePriority,
75
+ gradleUserHome: input.gradleUserHome,
74
76
  nameMode: entry.nameMode,
75
77
  signatureMode: entry.signatureMode,
76
78
  maxCandidates: entry.maxCandidates,
@@ -26,12 +26,14 @@ export declare const compareMinecraftShape: {
26
26
  toVersion: z.ZodString;
27
27
  mapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
28
28
  sourcePriority: z.ZodOptional<z.ZodEnum<["loom-first", "maven-first"]>>;
29
+ gradleUserHome: z.ZodOptional<z.ZodString>;
29
30
  }, "strip", z.ZodTypeAny, {
30
31
  kind: "class";
31
32
  className: string;
32
33
  fromVersion: string;
33
34
  toVersion: string;
34
35
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
36
+ gradleUserHome?: string | undefined;
35
37
  sourcePriority?: "loom-first" | "maven-first" | undefined;
36
38
  }, {
37
39
  kind: "class";
@@ -39,6 +41,7 @@ export declare const compareMinecraftShape: {
39
41
  fromVersion: string;
40
42
  toVersion: string;
41
43
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
44
+ gradleUserHome?: string | undefined;
42
45
  sourcePriority?: "loom-first" | "maven-first" | undefined;
43
46
  }>, z.ZodObject<{
44
47
  kind: z.ZodLiteral<"registry">;
@@ -87,12 +90,14 @@ export declare const compareMinecraftSchema: z.ZodObject<{
87
90
  toVersion: z.ZodString;
88
91
  mapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
89
92
  sourcePriority: z.ZodOptional<z.ZodEnum<["loom-first", "maven-first"]>>;
93
+ gradleUserHome: z.ZodOptional<z.ZodString>;
90
94
  }, "strip", z.ZodTypeAny, {
91
95
  kind: "class";
92
96
  className: string;
93
97
  fromVersion: string;
94
98
  toVersion: string;
95
99
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
100
+ gradleUserHome?: string | undefined;
96
101
  sourcePriority?: "loom-first" | "maven-first" | undefined;
97
102
  }, {
98
103
  kind: "class";
@@ -100,6 +105,7 @@ export declare const compareMinecraftSchema: z.ZodObject<{
100
105
  fromVersion: string;
101
106
  toVersion: string;
102
107
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
108
+ gradleUserHome?: string | undefined;
103
109
  sourcePriority?: "loom-first" | "maven-first" | undefined;
104
110
  }>, z.ZodObject<{
105
111
  kind: z.ZodLiteral<"registry">;
@@ -136,6 +142,7 @@ export declare const compareMinecraftSchema: z.ZodObject<{
136
142
  fromVersion: string;
137
143
  toVersion: string;
138
144
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
145
+ gradleUserHome?: string | undefined;
139
146
  sourcePriority?: "loom-first" | "maven-first" | undefined;
140
147
  } | {
141
148
  kind: "registry";
@@ -161,6 +168,7 @@ export declare const compareMinecraftSchema: z.ZodObject<{
161
168
  fromVersion: string;
162
169
  toVersion: string;
163
170
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
171
+ gradleUserHome?: string | undefined;
164
172
  sourcePriority?: "loom-first" | "maven-first" | undefined;
165
173
  } | {
166
174
  kind: "registry";
@@ -191,6 +199,7 @@ type CompareMinecraftDeps = {
191
199
  toVersion: string;
192
200
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn";
193
201
  sourcePriority?: "loom-first" | "maven-first";
202
+ gradleUserHome?: string;
194
203
  includeFullDiff?: boolean;
195
204
  }) => Promise<DiffClassSignaturesOutput>;
196
205
  getRegistryData: (input: {
@@ -18,7 +18,8 @@ const subjectSchema = z.discriminatedUnion("kind", [
18
18
  fromVersion: nonEmptyString,
19
19
  toVersion: nonEmptyString,
20
20
  mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
21
- sourcePriority: z.enum(["loom-first", "maven-first"]).optional()
21
+ sourcePriority: z.enum(["loom-first", "maven-first"]).optional(),
22
+ gradleUserHome: nonEmptyString.optional()
22
23
  }),
23
24
  z.object({
24
25
  kind: z.literal("registry"),
@@ -175,6 +176,7 @@ export class CompareMinecraftService {
175
176
  toVersion: input.subject.toVersion,
176
177
  mapping: input.subject.mapping,
177
178
  sourcePriority: input.subject.sourcePriority,
179
+ ...(input.subject.gradleUserHome !== undefined ? { gradleUserHome: input.subject.gradleUserHome } : {}),
178
180
  includeFullDiff: input.includeFullDiff
179
181
  });
180
182
  const changedCount = output.summary.total.added +
@@ -12,6 +12,7 @@ export async function handleClassMembers(deps, subject, detail, include, limit)
12
12
  mapping: classSubject.mapping,
13
13
  scope: classSubject.scope,
14
14
  projectPath: classSubject.projectPath,
15
+ gradleUserHome: classSubject.gradleUserHome,
15
16
  preferProjectVersion: classSubject.preferProjectVersion,
16
17
  strictVersion: classSubject.strictVersion,
17
18
  maxMembers: limit
@@ -43,7 +43,8 @@ export async function handleClassOverview(deps, subject, detail, include) {
43
43
  const partialSourceFallback = subject.kind === "workspace" && hasPartialVanillaCoverage(artifact.artifact)
44
44
  ? await resolveBinaryBackedClass(deps, className, {
45
45
  version: artifact.version,
46
- mapping: classSubject.mapping
46
+ mapping: classSubject.mapping,
47
+ gradleUserHome: classSubject.gradleUserHome
47
48
  })
48
49
  : undefined;
49
50
  if (partialSourceFallback) {
@@ -53,6 +54,7 @@ export async function handleClassOverview(deps, subject, detail, include) {
53
54
  mapping: classSubject.mapping,
54
55
  scope: classSubject.scope,
55
56
  projectPath: classSubject.projectPath,
57
+ gradleUserHome: classSubject.gradleUserHome,
56
58
  preferProjectVersion: classSubject.preferProjectVersion,
57
59
  strictVersion: classSubject.strictVersion,
58
60
  mode: "metadata"
@@ -13,6 +13,7 @@ export async function handleClassSource(deps, subject, detail, include) {
13
13
  mapping: classSubject.mapping,
14
14
  scope: classSubject.scope,
15
15
  projectPath: classSubject.projectPath,
16
+ gradleUserHome: classSubject.gradleUserHome,
16
17
  preferProjectVersion: classSubject.preferProjectVersion,
17
18
  strictVersion: classSubject.strictVersion,
18
19
  mode: include.includes("source") || detail === "full" ? "snippet" : "metadata"
@@ -35,6 +35,7 @@ export async function handleListFiles(deps, subject, detail, include, limit, cur
35
35
  projectPath: subject.projectPath,
36
36
  mapping: subject.mapping,
37
37
  scope: subject.scope,
38
+ gradleUserHome: subject.gradleUserHome,
38
39
  preferProjectVersion: subject.preferProjectVersion,
39
40
  strictVersion: subject.strictVersion,
40
41
  focus: {
@@ -34,7 +34,8 @@ export async function handleSearch(deps, subject, detail, include, limit, cursor
34
34
  const binaryBackedClassHit = needsBinaryBackedClassHit
35
35
  ? await resolveBinaryBackedClass(deps, searchSubject.query, {
36
36
  version: artifact.version,
37
- mapping: subject.mapping
37
+ mapping: subject.mapping,
38
+ gradleUserHome: subject.gradleUserHome
38
39
  })
39
40
  : undefined;
40
41
  const binaryBackedHitRecord = binaryBackedClassHit == null