@adhisang/minecraft-modding-mcp 4.0.0 → 4.1.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 (176) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +40 -23
  3. package/dist/build-suggested-call.d.ts +29 -0
  4. package/dist/build-suggested-call.js +58 -0
  5. package/dist/cache-registry.d.ts +3 -1
  6. package/dist/cache-registry.js +50 -6
  7. package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
  8. package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
  9. package/dist/entry-tools/batch-class-members-service.js +97 -0
  10. package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
  11. package/dist/entry-tools/batch-class-source-service.js +100 -0
  12. package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
  13. package/dist/entry-tools/batch-mappings-service.js +66 -0
  14. package/dist/entry-tools/batch-runner.d.ts +72 -0
  15. package/dist/entry-tools/batch-runner.js +90 -0
  16. package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
  17. package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
  18. package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
  19. package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
  20. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
  21. package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
  22. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
  23. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
  24. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
  25. package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
  26. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
  27. package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
  28. package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
  29. package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
  30. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
  31. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
  32. package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
  33. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
  34. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
  35. package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
  36. package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
  37. package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
  38. package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
  39. package/dist/entry-tools/manage-cache-service.d.ts +16 -16
  40. package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
  41. package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
  42. package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
  43. package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
  44. package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
  45. package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
  46. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +102 -0
  47. package/dist/entry-tools/validate-project/cases/project-summary.js +415 -0
  48. package/dist/entry-tools/validate-project/internal.d.ts +142 -0
  49. package/dist/entry-tools/validate-project/internal.js +303 -0
  50. package/dist/entry-tools/validate-project-service.d.ts +67 -47
  51. package/dist/entry-tools/validate-project-service.js +13 -563
  52. package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
  53. package/dist/entry-tools/verify-mixin-target-service.js +323 -0
  54. package/dist/error-mapping.d.ts +40 -0
  55. package/dist/error-mapping.js +139 -0
  56. package/dist/errors.d.ts +6 -0
  57. package/dist/errors.js +6 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +147 -1354
  60. package/dist/mapping/internal-types.d.ts +54 -0
  61. package/dist/mapping/internal-types.js +14 -0
  62. package/dist/mapping/loaders/mojang.d.ts +2 -0
  63. package/dist/mapping/loaders/mojang.js +64 -0
  64. package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
  65. package/dist/mapping/loaders/tiny-loom.js +73 -0
  66. package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
  67. package/dist/mapping/loaders/tiny-maven.js +104 -0
  68. package/dist/mapping/loaders/types.d.ts +14 -0
  69. package/dist/mapping/loaders/types.js +2 -0
  70. package/dist/mapping/lookup.d.ts +52 -0
  71. package/dist/mapping/lookup.js +496 -0
  72. package/dist/mapping/parsers/normalize.d.ts +10 -0
  73. package/dist/mapping/parsers/normalize.js +52 -0
  74. package/dist/mapping/parsers/proguard.d.ts +20 -0
  75. package/dist/mapping/parsers/proguard.js +138 -0
  76. package/dist/mapping/parsers/symbol-records.d.ts +27 -0
  77. package/dist/mapping/parsers/symbol-records.js +216 -0
  78. package/dist/mapping/parsers/tiny.d.ts +9 -0
  79. package/dist/mapping/parsers/tiny.js +96 -0
  80. package/dist/mapping/types.d.ts +147 -0
  81. package/dist/mapping/types.js +2 -0
  82. package/dist/mapping-pipeline-service.js +3 -2
  83. package/dist/mapping-service.d.ts +8 -145
  84. package/dist/mapping-service.js +30 -1207
  85. package/dist/mixin/access-validators.d.ts +9 -0
  86. package/dist/mixin/access-validators.js +257 -0
  87. package/dist/mixin/annotation-validators.d.ts +5 -0
  88. package/dist/mixin/annotation-validators.js +162 -0
  89. package/dist/mixin/helpers.d.ts +28 -0
  90. package/dist/mixin/helpers.js +315 -0
  91. package/dist/mixin/parsed-validator.d.ts +8 -0
  92. package/dist/mixin/parsed-validator.js +337 -0
  93. package/dist/mixin/types.d.ts +208 -0
  94. package/dist/mixin/types.js +28 -0
  95. package/dist/mixin-validator.d.ts +9 -201
  96. package/dist/mixin-validator.js +8 -1020
  97. package/dist/source/access-validate.d.ts +4 -0
  98. package/dist/source/access-validate.js +254 -0
  99. package/dist/source/artifact-resolver.d.ts +111 -0
  100. package/dist/source/artifact-resolver.js +1271 -0
  101. package/dist/source/cache-metrics.d.ts +26 -0
  102. package/dist/source/cache-metrics.js +172 -0
  103. package/dist/source/class-source/members-builder.d.ts +34 -0
  104. package/dist/source/class-source/members-builder.js +46 -0
  105. package/dist/source/class-source/snippet-builder.d.ts +19 -0
  106. package/dist/source/class-source/snippet-builder.js +46 -0
  107. package/dist/source/class-source-helpers.d.ts +34 -0
  108. package/dist/source/class-source-helpers.js +140 -0
  109. package/dist/source/class-source.d.ts +42 -0
  110. package/dist/source/class-source.js +883 -0
  111. package/dist/source/descriptor-utils.d.ts +6 -0
  112. package/dist/source/descriptor-utils.js +37 -0
  113. package/dist/source/file-access.d.ts +4 -0
  114. package/dist/source/file-access.js +102 -0
  115. package/dist/source/indexer.d.ts +82 -0
  116. package/dist/source/indexer.js +522 -0
  117. package/dist/source/lifecycle/diff-utils.d.ts +9 -0
  118. package/dist/source/lifecycle/diff-utils.js +107 -0
  119. package/dist/source/lifecycle/diff.d.ts +2 -0
  120. package/dist/source/lifecycle/diff.js +265 -0
  121. package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
  122. package/dist/source/lifecycle/mapping-helpers.js +327 -0
  123. package/dist/source/lifecycle/runtime-check.d.ts +2 -0
  124. package/dist/source/lifecycle/runtime-check.js +142 -0
  125. package/dist/source/lifecycle/trace.d.ts +2 -0
  126. package/dist/source/lifecycle/trace.js +231 -0
  127. package/dist/source/lifecycle.d.ts +4 -0
  128. package/dist/source/lifecycle.js +5 -0
  129. package/dist/source/search.d.ts +51 -0
  130. package/dist/source/search.js +676 -0
  131. package/dist/source/shared-utils.d.ts +6 -0
  132. package/dist/source/shared-utils.js +55 -0
  133. package/dist/source/state.d.ts +26 -0
  134. package/dist/source/state.js +24 -0
  135. package/dist/source/symbol-resolver.d.ts +3 -0
  136. package/dist/source/symbol-resolver.js +212 -0
  137. package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
  138. package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
  139. package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
  140. package/dist/source/validate-mixin/pipeline/parse.js +10 -0
  141. package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
  142. package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
  143. package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
  144. package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
  145. package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
  146. package/dist/source/validate-mixin/pipeline-context.js +93 -0
  147. package/dist/source/validate-mixin.d.ts +22 -0
  148. package/dist/source/validate-mixin.js +799 -0
  149. package/dist/source/workspace-target.d.ts +18 -0
  150. package/dist/source/workspace-target.js +305 -0
  151. package/dist/source-resolver.d.ts +1 -0
  152. package/dist/source-resolver.js +1 -1
  153. package/dist/source-service.d.ts +164 -170
  154. package/dist/source-service.js +70 -6116
  155. package/dist/stage-emitter.d.ts +13 -0
  156. package/dist/stage-emitter.js +30 -0
  157. package/dist/stdio-supervisor.d.ts +61 -0
  158. package/dist/stdio-supervisor.js +326 -9
  159. package/dist/tool-contract-manifest.d.ts +1 -1
  160. package/dist/tool-contract-manifest.js +23 -6
  161. package/dist/tool-guidance.d.ts +82 -0
  162. package/dist/tool-guidance.js +734 -0
  163. package/dist/tool-schema-registry.d.ts +16 -0
  164. package/dist/tool-schema-registry.js +37 -0
  165. package/dist/tool-schemas.d.ts +3518 -0
  166. package/dist/tool-schemas.js +813 -0
  167. package/dist/types.d.ts +36 -0
  168. package/dist/version-service.js +7 -6
  169. package/dist/workspace-context-cache.d.ts +32 -0
  170. package/dist/workspace-context-cache.js +66 -0
  171. package/dist/workspace-mapping-service.d.ts +16 -0
  172. package/dist/workspace-mapping-service.js +173 -1
  173. package/docs/README-ja.md +416 -0
  174. package/docs/examples.md +483 -0
  175. package/docs/tool-reference.md +462 -0
  176. package/package.json +17 -4
@@ -0,0 +1,883 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { isAbsolute, resolve as resolvePath } from "node:path";
3
+ import { buildSuggestedCall } from "../build-suggested-call.js";
4
+ import { ERROR_CODES, createError, isAppError } from "../errors.js";
5
+ import * as artifactResolver from "./artifact-resolver.js";
6
+ import * as classSourceHelpers from "./class-source-helpers.js";
7
+ import { buildClassSourceSnippet } from "./class-source/snippet-builder.js";
8
+ import { remapAndCountMembers, sliceMembersWithLimit } from "./class-source/members-builder.js";
9
+ import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
10
+ const MEMBERS_STATUS_LEGACY = process.env.MEMBERS_STATUS_LEGACY === "1";
11
+ function normalizeStrictPositiveInt(value, field) {
12
+ if (value == null) {
13
+ return undefined;
14
+ }
15
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
16
+ throw createError({
17
+ code: ERROR_CODES.INVALID_INPUT,
18
+ message: `${field} must be a positive integer.`,
19
+ details: { field, value }
20
+ });
21
+ }
22
+ return value;
23
+ }
24
+ function normalizeMemberAccess(access) {
25
+ if (access == null) {
26
+ return "public";
27
+ }
28
+ if (access === "public" || access === "all") {
29
+ return access;
30
+ }
31
+ throw createError({
32
+ code: ERROR_CODES.INVALID_INPUT,
33
+ message: `access must be "public" or "all".`,
34
+ details: { access }
35
+ });
36
+ }
37
+ function buildResolveArtifactParams(target, extra = {}) {
38
+ return {
39
+ target: {
40
+ kind: target.kind,
41
+ value: target.value
42
+ },
43
+ ...extra
44
+ };
45
+ }
46
+ function looksLikeDeobfuscatedClassName(value) {
47
+ const trimmed = value.trim();
48
+ if (!trimmed) {
49
+ return false;
50
+ }
51
+ if (trimmed.startsWith("net.minecraft.") || trimmed.startsWith("com.mojang.")) {
52
+ return true;
53
+ }
54
+ const simpleName = trimmed.split(/[.$]/).at(-1) ?? trimmed;
55
+ return /^[A-Z][A-Za-z0-9_$]{2,}$/.test(simpleName);
56
+ }
57
+ function obfuscatedNamespaceHint(className) {
58
+ return `Artifact is indexed in obfuscated runtime names. Deobfuscated names like "${className}" usually require mapping="mojang" or a find-mapping lookup to obfuscated names.`;
59
+ }
60
+ function hasPartialNetMinecraftCoverage(qualityFlags) {
61
+ return qualityFlags.includes("partial-source-no-net-minecraft");
62
+ }
63
+ function classNameToClassPath(className) {
64
+ const normalized = normalizePathStyle(className.trim()).replace(/\//g, ".");
65
+ const segments = normalized.split(".").filter((segment) => segment.length > 0);
66
+ if (segments.length === 0) {
67
+ return "";
68
+ }
69
+ const firstTypeSegment = segments.findIndex((segment) => /^[A-Z_$]/.test(segment));
70
+ if (firstTypeSegment < 0) {
71
+ return segments.join("/");
72
+ }
73
+ const packagePath = segments.slice(0, firstTypeSegment).join("/");
74
+ const typePath = segments.slice(firstTypeSegment).join("$");
75
+ return packagePath ? `${packagePath}/${typePath}` : typePath;
76
+ }
77
+ export function resolveClassFilePath(svc, artifactId, className) {
78
+ const normalizedClassName = className.trim();
79
+ const classPath = classNameToClassPath(normalizedClassName);
80
+ if (!classPath) {
81
+ return undefined;
82
+ }
83
+ const candidates = new Set([`${classPath}.java`]);
84
+ const innerIndex = classPath.indexOf("$");
85
+ if (innerIndex > 0) {
86
+ candidates.add(`${classPath.slice(0, innerIndex)}.java`);
87
+ }
88
+ const simpleName = normalizedClassName.split(/[.$]/).at(-1);
89
+ if (!simpleName) {
90
+ return undefined;
91
+ }
92
+ const lastSlash = classPath.lastIndexOf("/");
93
+ const expectedPrefix = lastSlash < 0 ? "" : classPath.slice(0, lastSlash + 1);
94
+ return svc.filesRepo.findBestClassLookupPath(artifactId, [...candidates], normalizedClassName, simpleName, expectedPrefix);
95
+ }
96
+ export async function resolveClassNameForLookup(svc, input) {
97
+ if (input.sourceMapping === input.targetMapping) {
98
+ return input.className;
99
+ }
100
+ if (!input.version) {
101
+ input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context} because version is unavailable.`);
102
+ return input.className;
103
+ }
104
+ try {
105
+ const mapped = await svc.mappingService.findMapping({
106
+ version: input.version,
107
+ kind: "class",
108
+ name: input.className,
109
+ sourceMapping: input.sourceMapping,
110
+ targetMapping: input.targetMapping,
111
+ sourcePriority: input.sourcePriority
112
+ });
113
+ if (mapped.resolved && mapped.resolvedSymbol) {
114
+ return mapped.resolvedSymbol.name;
115
+ }
116
+ input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context}.`);
117
+ }
118
+ catch {
119
+ input.warnings.push(`Mapping lookup failed for class "${input.className}" while preparing ${input.context} in ${input.targetMapping}.`);
120
+ }
121
+ return input.className;
122
+ }
123
+ export function buildFallbackProvenance(svc, input) {
124
+ const artifact = svc.getArtifact(input.artifactId);
125
+ const fallbackTarget = artifact.version
126
+ ? { kind: "version", value: artifact.version }
127
+ : artifact.coordinate
128
+ ? { kind: "coordinate", value: artifact.coordinate }
129
+ : { kind: "jar", value: artifact.sourceJarPath ?? artifact.binaryJarPath ?? input.artifactId };
130
+ const transformChain = artifact.provenance?.transformChain && artifact.provenance.transformChain.length > 0
131
+ ? artifact.provenance.transformChain
132
+ : [`mapping:${input.requestedMapping}->${input.mappingApplied}`];
133
+ return {
134
+ target: fallbackTarget,
135
+ resolvedAt: artifact.updatedAt,
136
+ resolvedFrom: {
137
+ origin: artifact.origin,
138
+ sourceJarPath: artifact.sourceJarPath,
139
+ binaryJarPath: artifact.binaryJarPath,
140
+ coordinate: artifact.coordinate,
141
+ version: artifact.version,
142
+ repoUrl: artifact.repoUrl
143
+ },
144
+ transformChain
145
+ };
146
+ }
147
+ export function buildClassSourceNotFoundError(_svc, input) {
148
+ const simpleName = input.className.split(/[.$]/).at(-1) ?? input.className;
149
+ const details = {
150
+ artifactId: input.artifactId,
151
+ className: input.className,
152
+ mapping: input.mappingApplied,
153
+ qualityFlags: input.qualityFlags,
154
+ ...(input.lookupClassName !== input.className ? { lookupClassName: input.lookupClassName } : {}),
155
+ ...(input.filePath ? { filePath: input.filePath } : {}),
156
+ ...(input.scope ? { scope: input.scope } : {}),
157
+ ...(input.targetKind ? { targetKind: input.targetKind } : {}),
158
+ ...(input.targetValue ? { targetValue: input.targetValue } : {}),
159
+ ...(input.attemptedBinaryFallback ? { binaryFallbackAttempted: true } : {})
160
+ };
161
+ let nextAction = `Use find-class to resolve the correct fully-qualified name for "${simpleName}".`;
162
+ let suggestionSpec = {
163
+ tool: "find-class",
164
+ params: { className: simpleName, artifactId: input.artifactId }
165
+ };
166
+ if (input.targetKind === "version" && input.scope && input.scope !== "merged" && !input.projectPath) {
167
+ nextAction +=
168
+ ` If the class exists in a modded environment, retry with scope: "merged" and projectPath pointing to your mod project.`;
169
+ }
170
+ else if (input.targetKind === "version" && input.scope && input.scope !== "merged" && input.projectPath) {
171
+ nextAction += ` The class may exist in merged sources; retry with scope: "merged".`;
172
+ }
173
+ if (hasPartialNetMinecraftCoverage(input.qualityFlags)) {
174
+ nextAction =
175
+ `Resolved source coverage does not include net.minecraft for "${input.className}",` +
176
+ (input.attemptedBinaryFallback
177
+ ? " and binary fallback did not produce source for that class."
178
+ : " and a binary fallback has not produced source for that class.") +
179
+ " Use get-class-api-matrix or find-mapping instead of find-class for vanilla API discovery.";
180
+ if (input.version) {
181
+ suggestionSpec = {
182
+ tool: "get-class-api-matrix",
183
+ params: {
184
+ version: input.version,
185
+ className: input.className,
186
+ classNameMapping: input.requestedMapping
187
+ }
188
+ };
189
+ }
190
+ else {
191
+ suggestionSpec = {
192
+ tool: "find-class",
193
+ params: { className: simpleName, artifactId: input.artifactId }
194
+ };
195
+ }
196
+ }
197
+ if (input.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(input.className)) {
198
+ nextAction += ` ${obfuscatedNamespaceHint(input.className)}`;
199
+ }
200
+ details.nextAction = nextAction;
201
+ Object.assign(details, buildSuggestedCall(suggestionSpec));
202
+ return createError({
203
+ code: ERROR_CODES.CLASS_NOT_FOUND,
204
+ message: `Source for class "${input.className}" was not found.`,
205
+ details
206
+ });
207
+ }
208
+ export function buildDecompiledFallback(svc, artifactId, lookupClassName, memberPattern, maxMembers) {
209
+ const filePath = resolveClassFilePath(svc, artifactId, lookupClassName);
210
+ if (!filePath) {
211
+ return undefined;
212
+ }
213
+ const row = svc.filesRepo.getFileContent(artifactId, filePath);
214
+ if (!row) {
215
+ return undefined;
216
+ }
217
+ const extracted = classSourceHelpers.extractDecompiledMembers(lookupClassName, filePath, row.content);
218
+ const filterByPattern = (list) => {
219
+ if (!memberPattern) {
220
+ return list;
221
+ }
222
+ const lower = memberPattern.toLowerCase();
223
+ return list.filter((entry) => entry.name.toLowerCase().includes(lower));
224
+ };
225
+ let constructors = filterByPattern(extracted.constructors);
226
+ let fields = filterByPattern(extracted.fields);
227
+ let methods = filterByPattern(extracted.methods);
228
+ const totalBefore = constructors.length + fields.length + methods.length;
229
+ if (totalBefore === 0) {
230
+ return undefined;
231
+ }
232
+ let remaining = maxMembers;
233
+ const takeWithinLimit = (list) => {
234
+ if (remaining <= 0) {
235
+ return [];
236
+ }
237
+ const slice = list.slice(0, remaining);
238
+ remaining -= slice.length;
239
+ return slice;
240
+ };
241
+ constructors = takeWithinLimit(constructors);
242
+ fields = takeWithinLimit(fields);
243
+ methods = takeWithinLimit(methods);
244
+ return {
245
+ fallback: {
246
+ constructors,
247
+ fields,
248
+ methods,
249
+ origin: "source-extracted"
250
+ },
251
+ counts: {
252
+ constructors: constructors.length,
253
+ fields: fields.length,
254
+ methods: methods.length,
255
+ total: constructors.length + fields.length + methods.length
256
+ }
257
+ };
258
+ }
259
+ export function findClass(svc, input) {
260
+ const className = input.className.trim();
261
+ if (!className) {
262
+ throw createError({
263
+ code: ERROR_CODES.INVALID_INPUT,
264
+ message: "className must be non-empty."
265
+ });
266
+ }
267
+ const inputArtifactId = input.artifactId.trim();
268
+ if (!inputArtifactId) {
269
+ throw createError({
270
+ code: ERROR_CODES.INVALID_INPUT,
271
+ message: "artifactId must be non-empty."
272
+ });
273
+ }
274
+ const artifact = svc.getArtifact(inputArtifactId);
275
+ const artifactId = artifact.artifactId;
276
+ const limit = Math.max(1, Math.min(input.limit ?? 20, 200));
277
+ const warnings = [];
278
+ const isQualified = className.includes(".");
279
+ if (isQualified) {
280
+ const classPath = className.replace(/\./g, "/");
281
+ const result = svc.symbolsRepo.findScopedSymbols({
282
+ artifactId,
283
+ query: className.split(".").at(-1) ?? className,
284
+ match: "exact",
285
+ limit: 5000
286
+ });
287
+ const matches = result.items
288
+ .filter((row) => {
289
+ const isTypeSymbol = row.symbolKind === "class" || row.symbolKind === "interface" ||
290
+ row.symbolKind === "enum" || row.symbolKind === "record";
291
+ if (!isTypeSymbol)
292
+ return false;
293
+ const rowQualified = row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", ".");
294
+ return rowQualified === className || row.filePath === `${classPath}.java`;
295
+ })
296
+ .map((row) => ({
297
+ qualifiedName: row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", "."),
298
+ filePath: row.filePath,
299
+ line: row.line,
300
+ symbolKind: row.symbolKind
301
+ }))
302
+ .slice(0, limit);
303
+ const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
304
+ const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
305
+ ? []
306
+ : matches;
307
+ if (filteredMatches.length === 0 && partialVanillaLookup) {
308
+ warnings.push(`Artifact source coverage is partial and excludes net.minecraft; returning non-vanilla matches for "${className}" would be misleading. Use get-class-source/get-class-members for binary fallback or get-class-api-matrix for mapped API inspection.`);
309
+ }
310
+ if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
311
+ warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
312
+ }
313
+ return { matches: filteredMatches, total: filteredMatches.length, warnings };
314
+ }
315
+ const result = svc.symbolsRepo.findScopedSymbols({
316
+ artifactId,
317
+ query: className,
318
+ match: "exact",
319
+ limit: limit * 5
320
+ });
321
+ const matches = [];
322
+ for (const row of result.items) {
323
+ if (matches.length >= limit)
324
+ break;
325
+ const isTypeSymbol = row.symbolKind === "class" || row.symbolKind === "interface" ||
326
+ row.symbolKind === "enum" || row.symbolKind === "record";
327
+ if (!isTypeSymbol)
328
+ continue;
329
+ matches.push({
330
+ qualifiedName: row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", "."),
331
+ filePath: row.filePath,
332
+ line: row.line,
333
+ symbolKind: row.symbolKind
334
+ });
335
+ }
336
+ const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
337
+ const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
338
+ ? []
339
+ : matches;
340
+ if (filteredMatches.length === 0 && partialVanillaLookup) {
341
+ warnings.push(`Artifact source coverage is partial and excludes net.minecraft; returning non-vanilla matches for "${className}" would be misleading. Use get-class-source/get-class-members for binary fallback or get-class-api-matrix for mapped API inspection.`);
342
+ }
343
+ if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
344
+ warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
345
+ }
346
+ return { matches: filteredMatches, total: filteredMatches.length, warnings };
347
+ }
348
+ export async function getClassSource(svc, input) {
349
+ const className = input.className.trim();
350
+ if (!className) {
351
+ throw createError({
352
+ code: ERROR_CODES.INVALID_INPUT,
353
+ message: "className must be non-empty."
354
+ });
355
+ }
356
+ const mode = input.mode ?? "metadata";
357
+ const startLine = normalizeStrictPositiveInt(input.startLine, "startLine");
358
+ const endLine = normalizeStrictPositiveInt(input.endLine, "endLine");
359
+ let maxLines = normalizeStrictPositiveInt(input.maxLines, "maxLines");
360
+ const maxChars = normalizeStrictPositiveInt(input.maxChars, "maxChars");
361
+ const outputFile = normalizeOptionalString(input.outputFile);
362
+ if (mode === "snippet" && startLine == null && endLine == null && maxLines == null) {
363
+ maxLines = 200;
364
+ }
365
+ if (startLine != null && endLine != null && startLine > endLine) {
366
+ throw createError({
367
+ code: ERROR_CODES.INVALID_LINE_RANGE,
368
+ message: `Invalid line range: startLine (${startLine}) is greater than endLine (${endLine}).`,
369
+ details: { startLine, endLine }
370
+ });
371
+ }
372
+ const normalizedArtifactId = normalizeOptionalString(input.artifactId);
373
+ if (normalizedArtifactId && input.target) {
374
+ throw createError({
375
+ code: ERROR_CODES.INVALID_INPUT,
376
+ message: "artifactId and target are mutually exclusive.",
377
+ details: { artifactId: normalizedArtifactId, target: input.target }
378
+ });
379
+ }
380
+ let artifactId = normalizedArtifactId;
381
+ let origin = "local-jar";
382
+ let warnings = [];
383
+ let requestedMapping = normalizeMapping(input.mapping);
384
+ let mappingApplied = requestedMapping;
385
+ let provenance;
386
+ let qualityFlags = [];
387
+ let sourceJarPath;
388
+ let binaryJarPath;
389
+ let version;
390
+ let coordinate;
391
+ if (!artifactId) {
392
+ if (!input.target) {
393
+ throw createError({
394
+ code: ERROR_CODES.INVALID_INPUT,
395
+ message: "Either artifactId or target must be provided."
396
+ });
397
+ }
398
+ const resolved = await svc.resolveArtifact({
399
+ target: input.target,
400
+ mapping: input.mapping,
401
+ sourcePriority: input.sourcePriority,
402
+ allowDecompile: input.allowDecompile,
403
+ projectPath: input.projectPath,
404
+ scope: input.scope,
405
+ preferProjectVersion: input.preferProjectVersion,
406
+ strictVersion: input.strictVersion
407
+ });
408
+ artifactId = resolved.artifactId;
409
+ origin = resolved.origin;
410
+ warnings = [...resolved.warnings];
411
+ requestedMapping = resolved.requestedMapping;
412
+ mappingApplied = resolved.mappingApplied;
413
+ provenance = resolved.provenance;
414
+ qualityFlags = [...resolved.qualityFlags];
415
+ sourceJarPath = resolved.resolvedSourceJarPath;
416
+ binaryJarPath = resolved.binaryJarPath;
417
+ version = resolved.version;
418
+ coordinate = resolved.coordinate;
419
+ }
420
+ else {
421
+ const artifact = svc.getArtifact(artifactId);
422
+ artifactId = artifact.artifactId;
423
+ origin = artifact.origin;
424
+ requestedMapping = artifact.requestedMapping ?? requestedMapping;
425
+ mappingApplied = artifact.mappingApplied ?? requestedMapping;
426
+ provenance = artifact.provenance;
427
+ qualityFlags = artifact.qualityFlags;
428
+ sourceJarPath = artifact.sourceJarPath;
429
+ binaryJarPath = artifact.binaryJarPath;
430
+ version = artifact.version;
431
+ coordinate = artifact.coordinate;
432
+ }
433
+ version = await artifactResolver.resolveVersionContext(svc, {
434
+ version,
435
+ provenance,
436
+ coordinate,
437
+ projectPath: input.projectPath,
438
+ preferProjectVersion: input.preferProjectVersion,
439
+ warnings
440
+ });
441
+ let activeArtifactId = artifactId;
442
+ let activeOrigin = origin;
443
+ let activeProvenance = provenance;
444
+ let activeQualityFlags = [...qualityFlags];
445
+ let activeMappingApplied = mappingApplied;
446
+ let activeSourceJarPath = sourceJarPath;
447
+ let attemptedBinaryFallback = false;
448
+ const tryBinaryFallback = async () => {
449
+ if (attemptedBinaryFallback) {
450
+ return false;
451
+ }
452
+ attemptedBinaryFallback = true;
453
+ const normalizedBinaryJarPath = normalizeOptionalString(binaryJarPath);
454
+ if (!normalizedBinaryJarPath) {
455
+ return false;
456
+ }
457
+ if (activeSourceJarPath &&
458
+ normalizePathStyle(activeSourceJarPath) === normalizePathStyle(normalizedBinaryJarPath)) {
459
+ return false;
460
+ }
461
+ const fallbackResolved = await svc.resolveBinaryFallbackArtifact({
462
+ binaryJarPath: normalizedBinaryJarPath,
463
+ version,
464
+ coordinate,
465
+ requestedMapping,
466
+ mappingApplied,
467
+ provenance: activeProvenance,
468
+ qualityFlags: activeQualityFlags
469
+ });
470
+ if (!fallbackResolved || fallbackResolved.artifactId === activeArtifactId) {
471
+ return false;
472
+ }
473
+ activeArtifactId = fallbackResolved.artifactId;
474
+ activeOrigin = fallbackResolved.origin;
475
+ activeMappingApplied = fallbackResolved.mappingApplied ?? activeMappingApplied;
476
+ activeProvenance = fallbackResolved.provenance ?? activeProvenance;
477
+ activeQualityFlags = dedupeQualityFlags([...(fallbackResolved.qualityFlags ?? []), "binary-fallback"]);
478
+ activeSourceJarPath = fallbackResolved.sourceJarPath;
479
+ warnings.push(`Falling back to binary artifact "${normalizedBinaryJarPath}" because source coverage for "${className}" was incomplete.`);
480
+ if (activeMappingApplied !== requestedMapping) {
481
+ warnings.push(`Fallback source text is indexed in ${activeMappingApplied} names; returned source is not remapped to ${requestedMapping}.`);
482
+ }
483
+ return true;
484
+ };
485
+ let activeLookupClassName = await svc.resolveClassNameForLookup({
486
+ className,
487
+ version,
488
+ sourceMapping: requestedMapping,
489
+ targetMapping: activeMappingApplied,
490
+ sourcePriority: input.sourcePriority,
491
+ warnings,
492
+ context: "source lookup"
493
+ });
494
+ let filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName);
495
+ if (!filePath && (await tryBinaryFallback())) {
496
+ activeLookupClassName = await svc.resolveClassNameForLookup({
497
+ className,
498
+ version,
499
+ sourceMapping: requestedMapping,
500
+ targetMapping: activeMappingApplied,
501
+ sourcePriority: input.sourcePriority,
502
+ warnings,
503
+ context: "source lookup"
504
+ });
505
+ filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName);
506
+ }
507
+ if (!filePath) {
508
+ throw buildClassSourceNotFoundError(svc, {
509
+ artifactId: activeArtifactId,
510
+ className,
511
+ lookupClassName: activeLookupClassName,
512
+ mappingApplied: activeMappingApplied,
513
+ requestedMapping,
514
+ qualityFlags: activeQualityFlags,
515
+ attemptedBinaryFallback,
516
+ targetKind: input.target?.kind,
517
+ targetValue: input.target && "value" in input.target ? input.target.value : undefined,
518
+ scope: input.scope,
519
+ projectPath: input.projectPath,
520
+ version
521
+ });
522
+ }
523
+ let row = svc.filesRepo.getFileContent(activeArtifactId, filePath);
524
+ if (!row && (await tryBinaryFallback())) {
525
+ activeLookupClassName = await svc.resolveClassNameForLookup({
526
+ className,
527
+ version,
528
+ sourceMapping: requestedMapping,
529
+ targetMapping: activeMappingApplied,
530
+ sourcePriority: input.sourcePriority,
531
+ warnings,
532
+ context: "source lookup"
533
+ });
534
+ filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName) ?? filePath;
535
+ row = svc.filesRepo.getFileContent(activeArtifactId, filePath);
536
+ }
537
+ if (!row) {
538
+ throw buildClassSourceNotFoundError(svc, {
539
+ artifactId: activeArtifactId,
540
+ className,
541
+ lookupClassName: activeLookupClassName,
542
+ mappingApplied: activeMappingApplied,
543
+ requestedMapping,
544
+ qualityFlags: activeQualityFlags,
545
+ attemptedBinaryFallback,
546
+ filePath,
547
+ targetKind: input.target?.kind,
548
+ targetValue: input.target && "value" in input.target ? input.target.value : undefined,
549
+ scope: input.scope,
550
+ projectPath: input.projectPath,
551
+ version
552
+ });
553
+ }
554
+ const snippet = buildClassSourceSnippet({
555
+ filePath,
556
+ content: row.content,
557
+ mode,
558
+ startLine,
559
+ endLine,
560
+ maxLines,
561
+ maxChars
562
+ });
563
+ let sourceText = snippet.sourceText;
564
+ const totalLines = snippet.totalLines;
565
+ const returnedStart = snippet.returnedStart;
566
+ const returnedEnd = snippet.returnedEnd;
567
+ const truncated = snippet.truncated;
568
+ const charsTruncated = snippet.charsTruncated;
569
+ let resolvedOutputFile;
570
+ if (outputFile) {
571
+ const outputPath = isAbsolute(outputFile)
572
+ ? outputFile
573
+ : resolvePath(outputFile);
574
+ await writeFile(outputPath, sourceText, "utf8");
575
+ resolvedOutputFile = outputPath;
576
+ sourceText = `[Written to ${outputPath}]`;
577
+ }
578
+ const normalizedProvenance = activeProvenance ??
579
+ buildFallbackProvenance(svc, {
580
+ artifactId: activeArtifactId,
581
+ origin: activeOrigin,
582
+ requestedMapping,
583
+ mappingApplied: activeMappingApplied
584
+ });
585
+ return {
586
+ className,
587
+ mode,
588
+ sourceText,
589
+ totalLines,
590
+ returnedRange: {
591
+ start: returnedStart,
592
+ end: returnedEnd
593
+ },
594
+ truncated,
595
+ ...(charsTruncated ? { charsTruncated } : {}),
596
+ origin: activeOrigin,
597
+ artifactId: activeArtifactId,
598
+ requestedMapping,
599
+ mappingApplied: activeMappingApplied,
600
+ returnedNamespace: activeMappingApplied,
601
+ provenance: normalizedProvenance,
602
+ qualityFlags: activeQualityFlags,
603
+ artifactContents: svc.buildArtifactContentsSummary({
604
+ origin: activeOrigin,
605
+ sourceJarPath: activeSourceJarPath,
606
+ isDecompiled: activeOrigin === "decompiled",
607
+ qualityFlags: activeQualityFlags
608
+ }),
609
+ ...(resolvedOutputFile ? { outputFile: resolvedOutputFile } : {}),
610
+ warnings
611
+ };
612
+ }
613
+ export async function getClassMembers(svc, input) {
614
+ const className = input.className.trim();
615
+ if (!className) {
616
+ throw createError({
617
+ code: ERROR_CODES.INVALID_INPUT,
618
+ message: "className must be non-empty."
619
+ });
620
+ }
621
+ let requestedMapping = normalizeMapping(input.mapping);
622
+ const access = normalizeMemberAccess(input.access);
623
+ const includeSynthetic = input.includeSynthetic ?? false;
624
+ const includeInherited = input.includeInherited ?? false;
625
+ const memberPattern = normalizeOptionalString(input.memberPattern);
626
+ const parsedMaxMembers = normalizeStrictPositiveInt(input.maxMembers, "maxMembers");
627
+ const maxMembers = parsedMaxMembers == null ? 500 : Math.min(parsedMaxMembers, 5000);
628
+ const normalizedArtifactId = normalizeOptionalString(input.artifactId);
629
+ if (normalizedArtifactId && input.target) {
630
+ throw createError({
631
+ code: ERROR_CODES.INVALID_INPUT,
632
+ message: "artifactId and target are mutually exclusive.",
633
+ details: { artifactId: normalizedArtifactId, target: input.target }
634
+ });
635
+ }
636
+ let artifactId = normalizedArtifactId;
637
+ let origin = "local-jar";
638
+ let warnings = [];
639
+ let mappingApplied = requestedMapping;
640
+ let provenance;
641
+ let qualityFlags = [];
642
+ let binaryJarPath;
643
+ let sourceJarPath;
644
+ let coordinate;
645
+ if (parsedMaxMembers != null && parsedMaxMembers > 5000) {
646
+ warnings.push(`maxMembers was clamped to 5000 from ${parsedMaxMembers}.`);
647
+ }
648
+ let version;
649
+ if (!artifactId) {
650
+ if (!input.target) {
651
+ throw createError({
652
+ code: ERROR_CODES.INVALID_INPUT,
653
+ message: "Either artifactId or target must be provided."
654
+ });
655
+ }
656
+ const resolved = await svc.resolveArtifact({
657
+ target: input.target,
658
+ mapping: input.mapping,
659
+ sourcePriority: input.sourcePriority,
660
+ allowDecompile: input.allowDecompile,
661
+ projectPath: input.projectPath,
662
+ scope: input.scope,
663
+ preferProjectVersion: input.preferProjectVersion,
664
+ strictVersion: input.strictVersion
665
+ });
666
+ artifactId = resolved.artifactId;
667
+ origin = resolved.origin;
668
+ warnings.push(...resolved.warnings);
669
+ requestedMapping = resolved.requestedMapping;
670
+ mappingApplied = resolved.mappingApplied;
671
+ provenance = resolved.provenance;
672
+ qualityFlags = [...resolved.qualityFlags];
673
+ binaryJarPath = resolved.binaryJarPath;
674
+ sourceJarPath = resolved.resolvedSourceJarPath;
675
+ version = resolved.version;
676
+ coordinate = resolved.coordinate;
677
+ }
678
+ else {
679
+ const artifact = svc.getArtifact(artifactId);
680
+ artifactId = artifact.artifactId;
681
+ origin = artifact.origin;
682
+ mappingApplied = artifact.mappingApplied ?? requestedMapping;
683
+ provenance = artifact.provenance;
684
+ qualityFlags = artifact.qualityFlags;
685
+ binaryJarPath = artifact.binaryJarPath;
686
+ sourceJarPath = artifact.sourceJarPath;
687
+ version = artifact.version;
688
+ coordinate = artifact.coordinate;
689
+ }
690
+ version = await artifactResolver.resolveVersionContext(svc, {
691
+ version,
692
+ provenance,
693
+ coordinate,
694
+ projectPath: input.projectPath,
695
+ preferProjectVersion: input.preferProjectVersion,
696
+ warnings
697
+ });
698
+ if (requestedMapping !== "obfuscated" && !version) {
699
+ throw createError({
700
+ code: ERROR_CODES.MAPPING_NOT_APPLIED,
701
+ message: `Non-obfuscated mapping "${requestedMapping}" requires a version, but none was resolved.`,
702
+ details: {
703
+ mapping: requestedMapping,
704
+ nextAction: "Resolve with target: { kind: \"version\", value: ... } or specify a versioned coordinate.",
705
+ ...buildSuggestedCall({
706
+ tool: "resolve-artifact",
707
+ params: buildResolveArtifactParams({ kind: "version", value: "latest" })
708
+ })
709
+ }
710
+ });
711
+ }
712
+ if (!binaryJarPath) {
713
+ throw createError({
714
+ code: ERROR_CODES.CONTEXT_UNRESOLVED,
715
+ message: `Class members require a binary jar, but artifact "${artifactId}" has no binaryJarPath.`,
716
+ details: {
717
+ artifactId,
718
+ className,
719
+ nextAction: "Resolve with target: { kind: \"jar\" | \"version\", value: ... } or use an artifact that has a binary jar."
720
+ }
721
+ });
722
+ }
723
+ const lookupClassName = await svc.resolveClassNameForLookup({
724
+ className,
725
+ version,
726
+ sourceMapping: requestedMapping,
727
+ targetMapping: mappingApplied,
728
+ sourcePriority: input.sourcePriority,
729
+ warnings,
730
+ context: "binary lookup"
731
+ });
732
+ let signatureContext;
733
+ let signatureConstructors;
734
+ let signatureFields;
735
+ let signatureMethods;
736
+ let binaryExtractionFailed = false;
737
+ let binaryExtractionFailureReason;
738
+ try {
739
+ const signature = await svc.explorerService.getSignature({
740
+ fqn: lookupClassName,
741
+ jarPath: binaryJarPath,
742
+ access,
743
+ includeSynthetic,
744
+ includeInherited,
745
+ memberPattern: requestedMapping === mappingApplied ? memberPattern : undefined
746
+ });
747
+ warnings.push(...signature.warnings);
748
+ signatureContext = signature.context;
749
+ signatureConstructors = signature.constructors;
750
+ signatureFields = signature.fields;
751
+ signatureMethods = signature.methods;
752
+ }
753
+ catch (error) {
754
+ if (isAppError(error) && error.code === ERROR_CODES.CLASS_NOT_FOUND) {
755
+ throw error;
756
+ }
757
+ binaryExtractionFailed = true;
758
+ binaryExtractionFailureReason = error instanceof Error ? error.message : String(error);
759
+ signatureContext = {
760
+ minecraftVersion: version ?? "unknown",
761
+ mappingType: "unknown",
762
+ mappingNamespace: mappingApplied === "intermediary" ? "obfuscated" : mappingApplied,
763
+ jarHash: "",
764
+ generatedAt: new Date().toISOString()
765
+ };
766
+ signatureConstructors = [];
767
+ signatureFields = [];
768
+ signatureMethods = [];
769
+ }
770
+ const remapped = await remapAndCountMembers(svc, {
771
+ signatureConstructors,
772
+ signatureFields,
773
+ signatureMethods,
774
+ version,
775
+ mappingApplied,
776
+ requestedMapping,
777
+ sourcePriority: input.sourcePriority,
778
+ memberPattern,
779
+ warnings
780
+ });
781
+ const counts = remapped.counts;
782
+ const sliced = sliceMembersWithLimit(remapped, counts.total, maxMembers, warnings);
783
+ const constructors = sliced.constructors;
784
+ const fields = sliced.fields;
785
+ const methods = sliced.methods;
786
+ const truncated = sliced.truncated;
787
+ const normalizedProvenance = provenance ??
788
+ buildFallbackProvenance(svc, {
789
+ artifactId,
790
+ origin,
791
+ requestedMapping,
792
+ mappingApplied
793
+ });
794
+ let decompiledFallback;
795
+ let decompiledMemberCounts;
796
+ let fallbackQualityFlags = qualityFlags;
797
+ if (counts.total === 0) {
798
+ const namespaceMismatch = requestedMapping !== mappingApplied;
799
+ const fallbackPattern = namespaceMismatch ? undefined : memberPattern;
800
+ const sourceFallback = buildDecompiledFallback(svc, artifactId, lookupClassName, fallbackPattern, maxMembers);
801
+ if (sourceFallback) {
802
+ decompiledFallback = sourceFallback.fallback;
803
+ decompiledMemberCounts = sourceFallback.counts;
804
+ fallbackQualityFlags = dedupeQualityFlags([
805
+ ...qualityFlags,
806
+ "members-from-decompiled-source"
807
+ ]);
808
+ const namespaceNote = namespaceMismatch
809
+ ? ` Member names are in ${mappingApplied} (artifact namespace); the request asked for ${requestedMapping}.`
810
+ : "";
811
+ warnings.push("Bytecode member enumeration returned zero; populated decompiledFallback from decompiled source. "
812
+ + "Descriptors and access modifiers are unavailable — use get-class-source for full details."
813
+ + namespaceNote);
814
+ if (namespaceMismatch && memberPattern) {
815
+ warnings.push(`memberPattern="${memberPattern}" was not applied to decompiledFallback because the artifact namespace (${mappingApplied}) differs from the requested namespace (${requestedMapping}); filter the response client-side after mapping.`);
816
+ }
817
+ }
818
+ }
819
+ let statusFields = {};
820
+ if (!MEMBERS_STATUS_LEGACY) {
821
+ let status;
822
+ let unavailableReason;
823
+ let suggestedCall;
824
+ if (counts.total > 0) {
825
+ status = "ok";
826
+ }
827
+ else if (decompiledFallback) {
828
+ status = "partial";
829
+ }
830
+ else if (binaryExtractionFailed) {
831
+ status = "members_unavailable";
832
+ unavailableReason =
833
+ binaryExtractionFailureReason
834
+ ?? `binary extraction failed for "${className}".`;
835
+ suggestedCall = buildSuggestedCall({
836
+ tool: "get-class-source",
837
+ params: {
838
+ target: { type: "artifact", artifactId },
839
+ className,
840
+ mode: "snippet",
841
+ mapping: requestedMapping
842
+ }
843
+ }).suggestedCall;
844
+ }
845
+ else {
846
+ status = "ok";
847
+ }
848
+ statusFields = {
849
+ status,
850
+ ...(unavailableReason ? { unavailableReason } : {}),
851
+ ...(suggestedCall ? { suggestedCall } : {})
852
+ };
853
+ }
854
+ return {
855
+ className,
856
+ members: {
857
+ constructors,
858
+ fields,
859
+ methods
860
+ },
861
+ counts,
862
+ truncated,
863
+ context: signatureContext,
864
+ origin,
865
+ artifactId,
866
+ requestedMapping,
867
+ mappingApplied,
868
+ returnedNamespace: requestedMapping,
869
+ provenance: normalizedProvenance,
870
+ qualityFlags: fallbackQualityFlags,
871
+ artifactContents: svc.buildArtifactContentsSummary({
872
+ origin,
873
+ sourceJarPath,
874
+ isDecompiled: origin === "decompiled",
875
+ qualityFlags: fallbackQualityFlags
876
+ }),
877
+ ...(decompiledFallback ? { decompiledFallback } : {}),
878
+ ...(decompiledMemberCounts ? { decompiledMemberCounts } : {}),
879
+ ...statusFields,
880
+ warnings
881
+ };
882
+ }
883
+ //# sourceMappingURL=class-source.js.map