@adhisang/minecraft-modding-mcp 4.2.1 → 6.0.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 (144) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +5 -4
  3. package/dist/access-transformer-parser.d.ts +2 -1
  4. package/dist/access-transformer-parser.js +10 -1
  5. package/dist/access-widener-parser.d.ts +1 -0
  6. package/dist/access-widener-parser.js +6 -3
  7. package/dist/build-suggested-call.js +31 -10
  8. package/dist/cache-registry.d.ts +8 -0
  9. package/dist/cache-registry.js +50 -32
  10. package/dist/config.d.ts +1 -0
  11. package/dist/config.js +4 -0
  12. package/dist/decompiler/vineflower.js +8 -2
  13. package/dist/entry-tools/analyze-mod-service.d.ts +24 -24
  14. package/dist/entry-tools/analyze-mod-service.js +2 -1
  15. package/dist/entry-tools/analyze-symbol-service.d.ts +57 -22
  16. package/dist/entry-tools/analyze-symbol-service.js +139 -34
  17. package/dist/entry-tools/batch-class-members-service.d.ts +3 -1
  18. package/dist/entry-tools/batch-class-members-service.js +6 -6
  19. package/dist/entry-tools/batch-class-source-service.d.ts +3 -1
  20. package/dist/entry-tools/batch-class-source-service.js +5 -6
  21. package/dist/entry-tools/batch-mappings-service.d.ts +3 -1
  22. package/dist/entry-tools/batch-mappings-service.js +4 -5
  23. package/dist/entry-tools/batch-symbol-exists-service.d.ts +3 -1
  24. package/dist/entry-tools/batch-symbol-exists-service.js +4 -5
  25. package/dist/entry-tools/compare-minecraft-service.d.ts +10 -10
  26. package/dist/entry-tools/compare-minecraft-service.js +1 -1
  27. package/dist/entry-tools/entry-tool-schema.d.ts +1 -1
  28. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +1 -3
  29. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +4 -3
  30. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +0 -5
  31. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +0 -1
  32. package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -2
  33. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +0 -1
  34. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +8 -0
  35. package/dist/entry-tools/inspect-minecraft/handlers/search.js +13 -2
  36. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +1 -1
  37. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +3 -2
  38. package/dist/entry-tools/inspect-minecraft/internal.d.ts +90 -89
  39. package/dist/entry-tools/inspect-minecraft/internal.js +32 -14
  40. package/dist/entry-tools/inspect-minecraft-service.d.ts +218 -218
  41. package/dist/entry-tools/inspect-minecraft-service.js +1 -1
  42. package/dist/entry-tools/manage-cache-service.d.ts +4 -4
  43. package/dist/entry-tools/response-contract.d.ts +3 -1
  44. package/dist/entry-tools/response-contract.js +3 -2
  45. package/dist/entry-tools/validate-project/cases/mixin.js +2 -1
  46. package/dist/entry-tools/validate-project/cases/project-summary.js +5 -3
  47. package/dist/entry-tools/validate-project-service.d.ts +42 -36
  48. package/dist/entry-tools/validate-project-service.js +3 -1
  49. package/dist/entry-tools/verify-mixin-target-service.d.ts +30 -0
  50. package/dist/entry-tools/verify-mixin-target-service.js +120 -29
  51. package/dist/error-mapping.d.ts +51 -0
  52. package/dist/error-mapping.js +157 -2
  53. package/dist/index.js +147 -76
  54. package/dist/java-process.js +1 -1
  55. package/dist/json-rpc-framing.d.ts +4 -0
  56. package/dist/json-rpc-framing.js +23 -1
  57. package/dist/mapping/internal-types.d.ts +24 -0
  58. package/dist/mapping/loaders/tiny-maven.js +46 -20
  59. package/dist/mapping/lookup.d.ts +13 -2
  60. package/dist/mapping/lookup.js +60 -2
  61. package/dist/mapping/types.d.ts +6 -0
  62. package/dist/mapping-service.d.ts +26 -2
  63. package/dist/mapping-service.js +212 -55
  64. package/dist/mcp-helpers.d.ts +1 -0
  65. package/dist/mcp-helpers.js +25 -3
  66. package/dist/minecraft-explorer-service.js +24 -11
  67. package/dist/mixin/access-validators.js +33 -1
  68. package/dist/mixin/annotation-validators.js +73 -9
  69. package/dist/mixin/parsed-validator.js +3 -2
  70. package/dist/mixin-parser.js +119 -7
  71. package/dist/mod-decompile-service.d.ts +2 -0
  72. package/dist/mod-decompile-service.js +21 -3
  73. package/dist/mod-remap-service.d.ts +20 -1
  74. package/dist/mod-remap-service.js +101 -45
  75. package/dist/mojang-tiny-mapping-service.js +5 -2
  76. package/dist/nbt/java-nbt-codec.js +46 -16
  77. package/dist/nbt/json-patch.js +51 -12
  78. package/dist/nbt/typed-json.d.ts +1 -0
  79. package/dist/nbt/typed-json.js +12 -2
  80. package/dist/nbt/types.d.ts +2 -2
  81. package/dist/observability.d.ts +6 -0
  82. package/dist/observability.js +10 -0
  83. package/dist/page-cursor.d.ts +17 -0
  84. package/dist/page-cursor.js +0 -0
  85. package/dist/path-converter.js +10 -3
  86. package/dist/registry-service.d.ts +2 -0
  87. package/dist/registry-service.js +41 -2
  88. package/dist/repo-downloader.js +4 -3
  89. package/dist/resources.js +45 -9
  90. package/dist/response-utils.d.ts +29 -4
  91. package/dist/response-utils.js +95 -5
  92. package/dist/source/access-validate.js +12 -2
  93. package/dist/source/artifact-resolver.js +2 -2
  94. package/dist/source/class-source/members-builder.d.ts +44 -1
  95. package/dist/source/class-source/members-builder.js +51 -4
  96. package/dist/source/class-source/snippet-builder.d.ts +13 -0
  97. package/dist/source/class-source/snippet-builder.js +54 -4
  98. package/dist/source/class-source-helpers.d.ts +4 -4
  99. package/dist/source/class-source-helpers.js +77 -20
  100. package/dist/source/class-source.d.ts +19 -0
  101. package/dist/source/class-source.js +144 -40
  102. package/dist/source/file-access.js +3 -2
  103. package/dist/source/indexer.js +29 -9
  104. package/dist/source/lifecycle/mapping-helpers.js +28 -3
  105. package/dist/source/lifecycle/runtime-check.js +20 -6
  106. package/dist/source/search.d.ts +1 -1
  107. package/dist/source/search.js +117 -30
  108. package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
  109. package/dist/source/validate-mixin.d.ts +2 -1
  110. package/dist/source/validate-mixin.js +45 -4
  111. package/dist/source/workspace-target.js +17 -10
  112. package/dist/source-jar-reader.d.ts +54 -1
  113. package/dist/source-jar-reader.js +121 -10
  114. package/dist/source-resolver.js +2 -2
  115. package/dist/source-service.d.ts +28 -7
  116. package/dist/source-service.js +9 -1
  117. package/dist/stdio-supervisor.d.ts +2 -2
  118. package/dist/stdio-supervisor.js +29 -9
  119. package/dist/storage/db.js +24 -1
  120. package/dist/storage/files-repo.d.ts +15 -0
  121. package/dist/storage/files-repo.js +67 -37
  122. package/dist/storage/symbols-repo.d.ts +15 -0
  123. package/dist/storage/symbols-repo.js +53 -19
  124. package/dist/symbols/symbol-extractor.js +35 -6
  125. package/dist/text-truncate.d.ts +7 -0
  126. package/dist/text-truncate.js +21 -0
  127. package/dist/tiny-remapper-resolver.js +10 -0
  128. package/dist/tool-contract-manifest.js +2 -2
  129. package/dist/tool-execution-gate.js +12 -1
  130. package/dist/tool-guidance.d.ts +6 -2
  131. package/dist/tool-guidance.js +63 -13
  132. package/dist/tool-schemas.d.ts +561 -496
  133. package/dist/tool-schemas.js +175 -139
  134. package/dist/types.d.ts +1 -0
  135. package/dist/version-diff-service.js +5 -0
  136. package/dist/version-service.d.ts +3 -0
  137. package/dist/version-service.js +18 -2
  138. package/dist/warning-details.d.ts +30 -0
  139. package/dist/warning-details.js +80 -0
  140. package/dist/workspace-mapping-service.js +9 -0
  141. package/docs/README-ja.md +2 -2
  142. package/docs/examples.md +26 -2
  143. package/docs/tool-reference.md +22 -17
  144. package/package.json +1 -1
package/dist/resources.js CHANGED
@@ -30,7 +30,7 @@ export function registerResources(server, sourceService) {
30
30
  }
31
31
  catch (e) {
32
32
  if (isAppError(e))
33
- return errorResource(uri.href, { message: e.message, code: e.code });
33
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
34
34
  throw e;
35
35
  }
36
36
  });
@@ -41,7 +41,7 @@ export function registerResources(server, sourceService) {
41
41
  }
42
42
  catch (e) {
43
43
  if (isAppError(e))
44
- return errorResource(uri.href, { message: e.message, code: e.code });
44
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
45
45
  throw e;
46
46
  }
47
47
  });
@@ -50,13 +50,31 @@ export function registerResources(server, sourceService) {
50
50
  try {
51
51
  const result = await sourceService.getClassSource({
52
52
  artifactId: params.artifactId,
53
- className: decodeTemplateParam(params, "className")
53
+ className: decodeTemplateParam(params, "className"),
54
+ // A "text/x-java" source resource must return real Java, not the default
55
+ // metadata outline; match the sibling class-source-json resource.
56
+ mode: "full"
54
57
  });
55
58
  return textResource(uri.href, result.sourceText);
56
59
  }
57
60
  catch (e) {
58
61
  if (isAppError(e))
59
- return errorResource(uri.href, { message: e.message, code: e.code });
62
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
63
+ throw e;
64
+ }
65
+ });
66
+ server.resource("class-source-json", new ResourceTemplate("mc://source-json/{artifactId}/{className}", { list: undefined }), { description: "JSON envelope of a class's full source plus metadata (artifactId, mappingApplied, totalLines, returnedRange, provenance, warnings) — the structured alternative to the raw-text class-source resource, easier to cite and continue.", mimeType: "application/json" }, async (uri, params) => {
67
+ try {
68
+ const result = await sourceService.getClassSource({
69
+ artifactId: params.artifactId,
70
+ className: decodeTemplateParam(params, "className"),
71
+ mode: "full"
72
+ });
73
+ return objectResource(uri.href, result);
74
+ }
75
+ catch (e) {
76
+ if (isAppError(e))
77
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
60
78
  throw e;
61
79
  }
62
80
  });
@@ -70,15 +88,33 @@ export function registerResources(server, sourceService) {
70
88
  }
71
89
  catch (e) {
72
90
  if (isAppError(e))
73
- return errorResource(uri.href, { message: e.message, code: e.code });
91
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
92
+ throw e;
93
+ }
94
+ });
95
+ server.resource("find-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{name}", { list: undefined }), { description: "Look up a CLASS mapping between two naming namespaces. This URI carries no owner, so field/method lookups (which need an owner) must use the find-member-mapping resource or the find-mapping tool.", mimeType: "application/json" }, async (uri, params) => {
96
+ try {
97
+ const result = await sourceService.findMapping({
98
+ version: params.version,
99
+ kind: params.kind,
100
+ name: decodeTemplateParam(params, "name"),
101
+ sourceMapping: params.sourceMapping,
102
+ targetMapping: params.targetMapping
103
+ });
104
+ return objectResource(uri.href, result);
105
+ }
106
+ catch (e) {
107
+ if (isAppError(e))
108
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
74
109
  throw e;
75
110
  }
76
111
  });
77
- server.resource("find-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{name}", { list: undefined }), { description: "Look up a mapping for a class, field, or method between two naming namespaces.", mimeType: "application/json" }, async (uri, params) => {
112
+ server.resource("find-member-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{owner}/{name}", { list: undefined }), { description: "Look up a FIELD or METHOD mapping between two naming namespaces, including the owner class the member belongs to (required for member lookups). For exact method overload resolution, use the find-mapping tool with a descriptor.", mimeType: "application/json" }, async (uri, params) => {
78
113
  try {
79
114
  const result = await sourceService.findMapping({
80
115
  version: params.version,
81
116
  kind: params.kind,
117
+ owner: decodeTemplateParam(params, "owner"),
82
118
  name: decodeTemplateParam(params, "name"),
83
119
  sourceMapping: params.sourceMapping,
84
120
  targetMapping: params.targetMapping
@@ -87,7 +123,7 @@ export function registerResources(server, sourceService) {
87
123
  }
88
124
  catch (e) {
89
125
  if (isAppError(e))
90
- return errorResource(uri.href, { message: e.message, code: e.code });
126
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
91
127
  throw e;
92
128
  }
93
129
  });
@@ -101,7 +137,7 @@ export function registerResources(server, sourceService) {
101
137
  }
102
138
  catch (e) {
103
139
  if (isAppError(e))
104
- return errorResource(uri.href, { message: e.message, code: e.code });
140
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
105
141
  throw e;
106
142
  }
107
143
  });
@@ -112,7 +148,7 @@ export function registerResources(server, sourceService) {
112
148
  }
113
149
  catch (e) {
114
150
  if (isAppError(e))
115
- return errorResource(uri.href, { message: e.message, code: e.code });
151
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
116
152
  throw e;
117
153
  }
118
154
  });
@@ -46,14 +46,29 @@ export declare const TOOL_PRESERVE_PAYLOAD_KEYS: Record<string, ReadonlySet<stri
46
46
  * payload fields do not leak through as explicit nulls.
47
47
  */
48
48
  export declare function compactResponse(obj: Record<string, unknown>, preserveKeys?: ReadonlySet<string>): Record<string, unknown>;
49
- /** resolve-artifact compact: omit debug/diagnostic fields. */
50
- export declare function compactArtifactResponse(obj: Record<string, unknown>): Record<string, unknown>;
49
+ /**
50
+ * resolve-artifact compact: omit debug/diagnostic fields. When `include` is provided,
51
+ * any field protected by a present include group is kept (re-added).
52
+ */
53
+ export declare function compactArtifactResponse(obj: Record<string, unknown>, include?: ReadonlySet<string>): Record<string, unknown>;
51
54
  /** get-class-source compact: drop provenance, artifactContents, qualityFlags. */
52
55
  export declare function compactSourceResponse(obj: Record<string, unknown>): Record<string, unknown>;
53
56
  /** get-class-members compact: drop provenance, artifactContents, qualityFlags, context. */
54
57
  export declare function compactMembersResponse(obj: Record<string, unknown>): Record<string, unknown>;
55
- /** Light compact projection: drop the artifactContents summary only. */
56
- export declare function compactLightResponse(obj: Record<string, unknown>): Record<string, unknown>;
58
+ /**
59
+ * Default diagnostic strip for get-class-source / get-class-members. Drops the
60
+ * three diagnostic fields (provenance, artifactContents, qualityFlags) that the
61
+ * common path never needs, applied unless the caller passes includeProvenance.
62
+ * Unlike compactMembersResponse this KEEPS members' `context` — only compact:true
63
+ * drops context. Reuses SOURCE_COMPACT_OMIT_KEYS (exactly those three keys).
64
+ */
65
+ export declare function stripSourceDiagnostics(obj: Record<string, unknown>): Record<string, unknown>;
66
+ export declare function stripMembersDiagnostics(obj: Record<string, unknown>): Record<string, unknown>;
67
+ /**
68
+ * Light compact projection: drop the artifactContents summary only. When `include`
69
+ * contains "artifact", the summary is kept (re-added).
70
+ */
71
+ export declare function compactLightResponse(obj: Record<string, unknown>, include?: ReadonlySet<string>): Record<string, unknown>;
57
72
  /**
58
73
  * Mapping tool compact: project candidates for size reduction.
59
74
  *
@@ -67,3 +82,13 @@ export declare function compactLightResponse(obj: Record<string, unknown>): Reco
67
82
  * seeing.
68
83
  */
69
84
  export declare function compactMappingResponse(obj: Record<string, unknown>): Record<string, unknown>;
85
+ export type ResponseDetailLevel = "summary" | "standard" | "full";
86
+ /** Expert + batch tools that accept the detail/include response contract. */
87
+ export declare const DETAIL_ENABLED_TOOL_NAMES: Set<string>;
88
+ /** Per-tool default detail level, chosen so default output is byte-identical to the old compact defaults. */
89
+ export declare const DEFAULT_DETAIL_BY_TOOL: Record<string, ResponseDetailLevel>;
90
+ /**
91
+ * Project an expert/batch tool result for the requested detail level + include set.
92
+ * Reuses the existing compact omit-sets; see the block comment above for the mapping.
93
+ */
94
+ export declare function projectByDetail(tool: string, result: Record<string, unknown>, detail: ResponseDetailLevel, include: ReadonlySet<string>): Record<string, unknown>;
@@ -126,9 +126,32 @@ const ARTIFACT_COMPACT_OMIT_KEYS = new Set([
126
126
  "repoUrl",
127
127
  "resolvedSourceJarPath"
128
128
  ]);
129
- /** resolve-artifact compact: omit debug/diagnostic fields. */
130
- export function compactArtifactResponse(obj) {
131
- return projectOmitKeys(obj, ARTIFACT_COMPACT_OMIT_KEYS);
129
+ /**
130
+ * include-group => the resolve-artifact fields that group re-adds (un-omits) when present.
131
+ * Used by the detail/include projection so callers can opt specific diagnostics back in.
132
+ */
133
+ const ARTIFACT_INCLUDE_PROTECTS = {
134
+ provenance: ["provenance"],
135
+ artifact: ["artifactContents"],
136
+ samples: ["sampleEntries"],
137
+ candidates: ["adjacentSourceCandidates"],
138
+ paths: ["binaryJarPath", "resolvedSourceJarPath", "coordinate", "repoUrl"]
139
+ };
140
+ /**
141
+ * resolve-artifact compact: omit debug/diagnostic fields. When `include` is provided,
142
+ * any field protected by a present include group is kept (re-added).
143
+ */
144
+ export function compactArtifactResponse(obj, include) {
145
+ if (!include || include.size === 0) {
146
+ return projectOmitKeys(obj, ARTIFACT_COMPACT_OMIT_KEYS);
147
+ }
148
+ const omit = new Set(ARTIFACT_COMPACT_OMIT_KEYS);
149
+ for (const group of include) {
150
+ for (const protectedKey of ARTIFACT_INCLUDE_PROTECTS[group] ?? []) {
151
+ omit.delete(protectedKey);
152
+ }
153
+ }
154
+ return projectOmitKeys(obj, omit);
132
155
  }
133
156
  /** Fields to omit from get-class-source in compact mode. */
134
157
  const SOURCE_COMPACT_OMIT_KEYS = new Set([
@@ -151,12 +174,31 @@ const MEMBERS_COMPACT_OMIT_KEYS = new Set([
151
174
  export function compactMembersResponse(obj) {
152
175
  return projectOmitKeys(obj, MEMBERS_COMPACT_OMIT_KEYS);
153
176
  }
177
+ /**
178
+ * Default diagnostic strip for get-class-source / get-class-members. Drops the
179
+ * three diagnostic fields (provenance, artifactContents, qualityFlags) that the
180
+ * common path never needs, applied unless the caller passes includeProvenance.
181
+ * Unlike compactMembersResponse this KEEPS members' `context` — only compact:true
182
+ * drops context. Reuses SOURCE_COMPACT_OMIT_KEYS (exactly those three keys).
183
+ */
184
+ export function stripSourceDiagnostics(obj) {
185
+ return projectOmitKeys(obj, SOURCE_COMPACT_OMIT_KEYS);
186
+ }
187
+ export function stripMembersDiagnostics(obj) {
188
+ return projectOmitKeys(obj, SOURCE_COMPACT_OMIT_KEYS);
189
+ }
154
190
  /** Fields to omit from search-class-source / list-artifact-files in compact mode. */
155
191
  const LIGHT_COMPACT_OMIT_KEYS = new Set([
156
192
  "artifactContents"
157
193
  ]);
158
- /** Light compact projection: drop the artifactContents summary only. */
159
- export function compactLightResponse(obj) {
194
+ /**
195
+ * Light compact projection: drop the artifactContents summary only. When `include`
196
+ * contains "artifact", the summary is kept (re-added).
197
+ */
198
+ export function compactLightResponse(obj, include) {
199
+ if (include?.has("artifact")) {
200
+ return obj;
201
+ }
160
202
  return projectOmitKeys(obj, LIGHT_COMPACT_OMIT_KEYS);
161
203
  }
162
204
  /** Max number of unresolved candidates that get full metadata in compact mode. */
@@ -224,4 +266,52 @@ export function compactMappingResponse(obj) {
224
266
  }
225
267
  return projected;
226
268
  }
269
+ /** Expert + batch tools that accept the detail/include response contract. */
270
+ export const DETAIL_ENABLED_TOOL_NAMES = COMPACT_ENABLED_TOOL_NAMES;
271
+ /** Per-tool default detail level, chosen so default output is byte-identical to the old compact defaults. */
272
+ export const DEFAULT_DETAIL_BY_TOOL = {
273
+ "resolve-artifact": "summary",
274
+ "find-mapping": "summary",
275
+ "resolve-method-mapping-exact": "summary",
276
+ "resolve-workspace-symbol": "summary",
277
+ "check-symbol-exists": "summary",
278
+ "get-class-source": "standard",
279
+ "get-class-members": "standard",
280
+ "search-class-source": "standard",
281
+ "list-artifact-files": "standard"
282
+ };
283
+ /** Extra keys omitted from get-class-members at detail=summary (beyond the diagnostic strip). */
284
+ const MEMBERS_SUMMARY_EXTRA_OMIT = new Set(["context"]);
285
+ /**
286
+ * Project an expert/batch tool result for the requested detail level + include set.
287
+ * Reuses the existing compact omit-sets; see the block comment above for the mapping.
288
+ */
289
+ export function projectByDetail(tool, result, detail, include) {
290
+ let out = result;
291
+ // Phase-4 diagnostic strip for source/members, now detail/include-aware:
292
+ // kept at detail=full or when include opts provenance back in.
293
+ const keepDiagnostics = detail === "full" || include.has("provenance");
294
+ if (!keepDiagnostics) {
295
+ if (tool === "get-class-source")
296
+ out = stripSourceDiagnostics(out);
297
+ if (tool === "get-class-members")
298
+ out = stripMembersDiagnostics(out);
299
+ }
300
+ if (detail === "summary") {
301
+ if (tool === "resolve-artifact") {
302
+ out = compactArtifactResponse(out, include);
303
+ }
304
+ if (COMPACT_MAPPING_TOOL_NAMES.has(tool) && !include.has("candidates")) {
305
+ out = compactMappingResponse(out);
306
+ }
307
+ if (tool === "get-class-members") {
308
+ out = projectOmitKeys(out, MEMBERS_SUMMARY_EXTRA_OMIT);
309
+ }
310
+ if (COMPACT_LIGHT_TOOL_NAMES.has(tool)) {
311
+ out = compactLightResponse(out, include);
312
+ }
313
+ out = compactResponse(out, TOOL_PRESERVE_PAYLOAD_KEYS[tool]);
314
+ }
315
+ return out;
316
+ }
227
317
  //# sourceMappingURL=response-utils.js.map
@@ -11,6 +11,11 @@ function normalizeAccessWidenerNamespace(namespace) {
11
11
  if (normalized === "named") {
12
12
  return "yarn";
13
13
  }
14
+ // Fabric/Loom call the obfuscated namespace "official"; treat it as obfuscated
15
+ // (matches the tiny-parser alias) instead of silently assuming intermediary.
16
+ if (normalized === "official") {
17
+ return "obfuscated";
18
+ }
14
19
  if (normalized === "obfuscated" ||
15
20
  normalized === "mojang" ||
16
21
  normalized === "intermediary" ||
@@ -39,8 +44,13 @@ export async function validateAccessWidener(svc, input) {
39
44
  const headerNamespaceRaw = normalizeOptionalString(parsed.namespace);
40
45
  const overrideMapping = input.mapping ? normalizeMapping(input.mapping) : undefined;
41
46
  const headerNamespace = normalizeAccessWidenerNamespace(headerNamespaceRaw);
42
- if (!headerNamespace && headerNamespaceRaw && !overrideMapping) {
43
- warnings.push(`Unsupported access widener namespace "${headerNamespaceRaw}". Assuming intermediary.`);
47
+ if (!headerNamespace && !overrideMapping) {
48
+ // No usable namespace resolved and no override: surface the intermediary
49
+ // assumption so it is never silent (previously only warned when a header
50
+ // namespace was present but unsupported).
51
+ warnings.push(headerNamespaceRaw
52
+ ? `Unsupported access widener namespace "${headerNamespaceRaw}". Assuming intermediary.`
53
+ : `Access widener namespace not declared; assuming intermediary. Pass mapping=… to set it explicitly.`);
44
54
  }
45
55
  const awNamespace = overrideMapping ?? headerNamespace ?? "intermediary";
46
56
  if (overrideMapping && headerNamespace && overrideMapping !== headerNamespace) {
@@ -54,7 +54,7 @@ export function hasExactVersionToken(path, version) {
54
54
  // Avoid prefix false-positives like "1.21.1" matching "1.21.10".
55
55
  const cached = VERSION_TOKEN_REGEX_CACHE.get(normalizedVersion);
56
56
  const pattern = cached
57
- ?? rememberCachedRegex(VERSION_TOKEN_REGEX_CACHE, normalizedVersion, new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}([^0-9a-z]|$)`, "i"));
57
+ ?? rememberCachedRegex(VERSION_TOKEN_REGEX_CACHE, normalizedVersion, new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}(?![0-9a-z]|\\.[0-9])`, "i"));
58
58
  return pattern.test(normalizedPath);
59
59
  }
60
60
  function inferMergedRuntimeNamespaceHint(path) {
@@ -1228,7 +1228,7 @@ export async function resolveArtifact(svc, input) {
1228
1228
  resolved.artifactAlias = artifactAlias;
1229
1229
  await svc.ingestIfNeeded(resolved);
1230
1230
  let sampleEntries;
1231
- if (input.compact === false && resolved.sourceJarPath) {
1231
+ if (input.includeSampleEntries && resolved.sourceJarPath) {
1232
1232
  try {
1233
1233
  const javaEntries = await listJavaEntries(resolved.sourceJarPath);
1234
1234
  const MAX_SAMPLE = 10;
@@ -31,5 +31,48 @@ export type MemberSliceResult = {
31
31
  methods: SignatureMember[];
32
32
  truncated: boolean;
33
33
  returnedTotal: number;
34
+ /** Absolute offset to resume from when more members remain after this page. */
35
+ nextOffset?: number;
34
36
  };
35
- export declare function sliceMembersWithLimit(remapped: Omit<RemappedMembers, "counts">, totalCount: number, maxMembers: number, warnings: string[]): MemberSliceResult;
37
+ export declare function sliceMembersWithLimit(remapped: Omit<RemappedMembers, "counts">, totalCount: number, maxMembers: number, warnings: string[], offset?: number): MemberSliceResult;
38
+ /** Slim per-member wire shape: only what an agent reading Java needs. */
39
+ export type WireMember = {
40
+ name: string;
41
+ javaSignature: string;
42
+ /**
43
+ * JVM descriptor. Always present on methods/constructors (overload
44
+ * disambiguation for @At/@Shadow matching); omitted on FIELD members by
45
+ * default (the single type is already in javaSignature) unless
46
+ * keepFieldDescriptors is set.
47
+ */
48
+ jvmDescriptor?: string;
49
+ /** Present only when members span multiple owners (includeInherited). */
50
+ ownerFqn?: string;
51
+ /** Present only when true. */
52
+ isSynthetic?: boolean;
53
+ };
54
+ export type WireMembersBlock = {
55
+ /** Hoisted owner when every member shares one owner (the common, non-inherited case). */
56
+ ownerFqn?: string;
57
+ constructors: WireMember[];
58
+ fields: WireMember[];
59
+ methods: WireMember[];
60
+ };
61
+ /**
62
+ * Project the internal six-field SignatureMember arrays to the slim wire shape.
63
+ * `ownerFqn` is hoisted to the block level when all members share a single owner
64
+ * (so it is not repeated per member); when members span multiple owners — the
65
+ * includeInherited case — it stays per member and the block-level field is omitted.
66
+ * `accessFlags` is dropped (javaSignature already encodes the modifiers);
67
+ * `isSynthetic` is emitted only when true. `jvmDescriptor` is always kept on
68
+ * methods/constructors (overload disambiguation) and, on FIELD members, only when
69
+ * keepFieldDescriptors is set (the field's single type is already in javaSignature).
70
+ *
71
+ * The owner anchor is derived from the members themselves (not from the looked-up
72
+ * class name) so it is correct in the requested namespace regardless of remapping.
73
+ */
74
+ export declare function projectMembersForWire(slice: {
75
+ constructors: SignatureMember[];
76
+ fields: SignatureMember[];
77
+ methods: SignatureMember[];
78
+ }, includeInherited: boolean, keepFieldDescriptors?: boolean): WireMembersBlock;
@@ -23,13 +23,22 @@ export async function remapAndCountMembers(svc, input) {
23
23
  };
24
24
  return { constructors, fields, methods, counts };
25
25
  }
26
- export function sliceMembersWithLimit(remapped, totalCount, maxMembers, warnings) {
26
+ export function sliceMembersWithLimit(remapped, totalCount, maxMembers, warnings, offset = 0) {
27
+ // Members form a single flat sequence in [constructors, fields, methods]
28
+ // order; offset skips that many from the start before taking maxMembers.
29
+ let toSkip = Math.max(0, offset);
27
30
  let remaining = maxMembers;
28
31
  const takeWithinLimit = (members) => {
32
+ if (toSkip >= members.length) {
33
+ toSkip -= members.length;
34
+ return [];
35
+ }
36
+ const afterSkip = toSkip > 0 ? members.slice(toSkip) : members;
37
+ toSkip = 0;
29
38
  if (remaining <= 0) {
30
39
  return [];
31
40
  }
32
- const slice = members.slice(0, remaining);
41
+ const slice = afterSkip.slice(0, remaining);
33
42
  remaining -= slice.length;
34
43
  return slice;
35
44
  };
@@ -37,10 +46,48 @@ export function sliceMembersWithLimit(remapped, totalCount, maxMembers, warnings
37
46
  const fields = takeWithinLimit(remapped.fields);
38
47
  const methods = takeWithinLimit(remapped.methods);
39
48
  const returnedTotal = constructors.length + fields.length + methods.length;
40
- const truncated = returnedTotal < totalCount;
49
+ const consumed = Math.min(Math.max(0, offset), totalCount) + returnedTotal;
50
+ const truncated = consumed < totalCount;
41
51
  if (truncated) {
42
52
  warnings.push(`Member list was truncated to ${returnedTotal} entries (from ${totalCount}).`);
43
53
  }
44
- return { constructors, fields, methods, truncated, returnedTotal };
54
+ return {
55
+ constructors,
56
+ fields,
57
+ methods,
58
+ truncated,
59
+ returnedTotal,
60
+ ...(truncated ? { nextOffset: consumed } : {})
61
+ };
62
+ }
63
+ /**
64
+ * Project the internal six-field SignatureMember arrays to the slim wire shape.
65
+ * `ownerFqn` is hoisted to the block level when all members share a single owner
66
+ * (so it is not repeated per member); when members span multiple owners — the
67
+ * includeInherited case — it stays per member and the block-level field is omitted.
68
+ * `accessFlags` is dropped (javaSignature already encodes the modifiers);
69
+ * `isSynthetic` is emitted only when true. `jvmDescriptor` is always kept on
70
+ * methods/constructors (overload disambiguation) and, on FIELD members, only when
71
+ * keepFieldDescriptors is set (the field's single type is already in javaSignature).
72
+ *
73
+ * The owner anchor is derived from the members themselves (not from the looked-up
74
+ * class name) so it is correct in the requested namespace regardless of remapping.
75
+ */
76
+ export function projectMembersForWire(slice, includeInherited, keepFieldDescriptors = false) {
77
+ const owners = new Set([...slice.constructors, ...slice.fields, ...slice.methods].map((m) => m.ownerFqn));
78
+ const hoistOwner = !includeInherited && owners.size === 1;
79
+ const toWire = (m, keepDescriptor) => ({
80
+ name: m.name,
81
+ javaSignature: m.javaSignature,
82
+ ...(keepDescriptor ? { jvmDescriptor: m.jvmDescriptor } : {}),
83
+ ...(hoistOwner ? {} : { ownerFqn: m.ownerFqn }),
84
+ ...(m.isSynthetic ? { isSynthetic: true } : {})
85
+ });
86
+ return {
87
+ ...(hoistOwner ? { ownerFqn: [...owners][0] } : {}),
88
+ constructors: slice.constructors.map((m) => toWire(m, true)),
89
+ fields: slice.fields.map((m) => toWire(m, keepFieldDescriptors)),
90
+ methods: slice.methods.map((m) => toWire(m, true))
91
+ };
45
92
  }
46
93
  //# sourceMappingURL=members-builder.js.map
@@ -15,5 +15,18 @@ export type SnippetBuildResult = {
15
15
  returnedEnd: number;
16
16
  truncated: boolean;
17
17
  charsTruncated: boolean;
18
+ /**
19
+ * First line the caller has NOT yet fully received, when more source remains.
20
+ * Safe to pass back as `startLine` to continue reading. Undefined when the
21
+ * response already reached the end of the file.
22
+ */
23
+ nextStartLine?: number;
24
+ /**
25
+ * True when the requested window begins past the end of the file. The returned
26
+ * window is empty (returnedEnd < returnedStart) rather than clamped into the
27
+ * last real line, so paginated readers can detect overshoot instead of silently
28
+ * re-reading the final line.
29
+ */
30
+ outOfRange?: boolean;
18
31
  };
19
32
  export declare function buildClassSourceSnippet(input: SnippetBuildInput): SnippetBuildResult;
@@ -1,6 +1,13 @@
1
1
  import * as classSourceHelpers from "../class-source-helpers.js";
2
+ import { sliceToMaxCharsSafe } from "../../text-truncate.js";
2
3
  export function buildClassSourceSnippet(input) {
3
- const lines = input.content.split(/\r?\n/);
4
+ const rawLines = input.content.split(/\r?\n/);
5
+ // A trailing newline produces a final empty element that is not a real source
6
+ // line. Drop a single one so totalLines and line addressing reflect the actual
7
+ // source (decompiled Java almost always ends with a trailing newline).
8
+ const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === ""
9
+ ? rawLines.slice(0, -1)
10
+ : rawLines;
4
11
  const totalLines = lines.length;
5
12
  let sourceText;
6
13
  let returnedStart;
@@ -15,7 +22,20 @@ export function buildClassSourceSnippet(input) {
15
22
  else {
16
23
  const requestedStart = input.startLine ?? 1;
17
24
  const requestedEnd = input.endLine ?? totalLines;
18
- const normalizedStart = Math.min(Math.max(1, requestedStart), Math.max(totalLines, 1));
25
+ if (requestedStart > totalLines) {
26
+ // The window begins past EOF: return an empty selection instead of clamping
27
+ // into the last real line, so paginated readers can detect the overshoot.
28
+ return {
29
+ sourceText: "",
30
+ totalLines,
31
+ returnedStart: requestedStart,
32
+ returnedEnd: requestedStart - 1,
33
+ truncated: true,
34
+ charsTruncated: false,
35
+ outOfRange: true
36
+ };
37
+ }
38
+ const normalizedStart = Math.max(1, requestedStart);
19
39
  const normalizedEnd = Math.min(Math.max(normalizedStart, requestedEnd), Math.max(totalLines, 1));
20
40
  let selectedLines = lines.slice(normalizedStart - 1, normalizedEnd);
21
41
  const clippedByRange = normalizedStart !== requestedStart || normalizedEnd !== requestedEnd;
@@ -30,9 +50,38 @@ export function buildClassSourceSnippet(input) {
30
50
  truncated = clippedByRange || clippedByMax;
31
51
  }
32
52
  if (input.maxChars != null && sourceText.length > input.maxChars) {
33
- sourceText = sourceText.slice(0, input.maxChars);
53
+ sourceText = sliceToMaxCharsSafe(sourceText, input.maxChars);
34
54
  charsTruncated = true;
35
55
  truncated = true;
56
+ if (input.mode !== "metadata") {
57
+ const survivingNewlines = (sourceText.match(/\n/g) ?? []).length;
58
+ returnedEnd = sourceText.endsWith("\n")
59
+ ? returnedStart + survivingNewlines - 1
60
+ : returnedStart + survivingNewlines;
61
+ }
62
+ }
63
+ let nextStartLine;
64
+ // Metadata mode returns a synthesized outline, not a line window into the
65
+ // source, so its returnedStart/returnedEnd are not line-addressable and a
66
+ // char cut must not yield a (bogus) line continuation.
67
+ if (truncated && input.mode !== "metadata") {
68
+ if (charsTruncated) {
69
+ // A mid-line character cut may leave the final returned line partial.
70
+ // Count only the complete (newline-terminated) lines and resume from the
71
+ // first line not fully returned, re-reading any partial line in full.
72
+ // Require forward progress: when the cut lands inside the first returned
73
+ // line (no complete line returned), resuming at the same startLine with
74
+ // the same maxChars would loop, so emit no continuation — the caller must
75
+ // raise maxChars instead.
76
+ const completeLines = (sourceText.match(/\n/g) ?? []).length;
77
+ const resume = returnedStart + completeLines;
78
+ if (resume > returnedStart && resume <= totalLines) {
79
+ nextStartLine = resume;
80
+ }
81
+ }
82
+ else if (returnedEnd < totalLines) {
83
+ nextStartLine = returnedEnd + 1;
84
+ }
36
85
  }
37
86
  return {
38
87
  sourceText,
@@ -40,7 +89,8 @@ export function buildClassSourceSnippet(input) {
40
89
  returnedStart,
41
90
  returnedEnd,
42
91
  truncated,
43
- charsTruncated
92
+ charsTruncated,
93
+ ...(nextStartLine != null ? { nextStartLine } : {})
44
94
  };
45
95
  }
46
96
  //# sourceMappingURL=snippet-builder.js.map
@@ -9,16 +9,16 @@ export declare function extractDecompiledMembers(className: string, filePath: st
9
9
  fields: DecompiledMember[];
10
10
  methods: DecompiledMember[];
11
11
  };
12
- export declare function computeLineBraceDepths(lines: string[]): number[];
12
+ export declare function computeLineBraceDepths(lines: string[], strippedLines?: string[]): number[];
13
13
  export declare function computeBraceRange(lines: string[], symbols: Array<{
14
14
  symbolKind: string;
15
15
  symbolName: string;
16
16
  line: number;
17
- }>, simpleName: string): {
17
+ }>, simpleName: string, strippedLines?: string[]): {
18
18
  declarationLine: number;
19
19
  endLine: number;
20
20
  } | undefined;
21
- export declare function scanBraceRange(lines: string[], declarationLine: number): {
21
+ export declare function scanBraceRange(lines: string[], declarationLine: number, strippedLines?: string[]): {
22
22
  declarationLine: number;
23
23
  endLine: number;
24
24
  };
@@ -28,7 +28,7 @@ export declare function computeNestedTypeRanges(lines: string[], symbols: Array<
28
28
  }>, outerBody: {
29
29
  declarationLine: number;
30
30
  endLine: number;
31
- }): Array<{
31
+ }, strippedLines?: string[]): Array<{
32
32
  declarationLine: number;
33
33
  endLine: number;
34
34
  }>;