@adhisang/minecraft-modding-mcp 3.2.0 → 4.1.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 (194) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +52 -32
  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 +59 -7
  7. package/dist/config.d.ts +10 -1
  8. package/dist/config.js +52 -1
  9. package/dist/entry-tools/analyze-symbol-service.d.ts +18 -18
  10. package/dist/entry-tools/analyze-symbol-service.js +13 -2
  11. package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
  12. package/dist/entry-tools/batch-class-members-service.js +97 -0
  13. package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
  14. package/dist/entry-tools/batch-class-source-service.js +100 -0
  15. package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
  16. package/dist/entry-tools/batch-mappings-service.js +66 -0
  17. package/dist/entry-tools/batch-runner.d.ts +72 -0
  18. package/dist/entry-tools/batch-runner.js +90 -0
  19. package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
  20. package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
  21. package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
  22. package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
  23. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
  24. package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
  25. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
  26. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
  27. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
  28. package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
  29. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
  30. package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
  31. package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
  32. package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
  33. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
  34. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
  35. package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
  36. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
  37. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
  38. package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
  39. package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
  40. package/dist/entry-tools/inspect-minecraft-service.d.ts +213 -328
  41. package/dist/entry-tools/inspect-minecraft-service.js +20 -1238
  42. package/dist/entry-tools/manage-cache-service.d.ts +16 -16
  43. package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
  44. package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
  45. package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
  46. package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
  47. package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
  48. package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
  49. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +97 -0
  50. package/dist/entry-tools/validate-project/cases/project-summary.js +346 -0
  51. package/dist/entry-tools/validate-project/internal.d.ts +135 -0
  52. package/dist/entry-tools/validate-project/internal.js +287 -0
  53. package/dist/entry-tools/validate-project-service.d.ts +63 -47
  54. package/dist/entry-tools/validate-project-service.js +12 -482
  55. package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
  56. package/dist/entry-tools/verify-mixin-target-service.js +323 -0
  57. package/dist/error-mapping.d.ts +40 -0
  58. package/dist/error-mapping.js +139 -0
  59. package/dist/errors.d.ts +6 -0
  60. package/dist/errors.js +6 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +170 -1314
  63. package/dist/lru-list.d.ts +31 -0
  64. package/dist/lru-list.js +102 -0
  65. package/dist/mapping/internal-types.d.ts +54 -0
  66. package/dist/mapping/internal-types.js +14 -0
  67. package/dist/mapping/loaders/mojang.d.ts +2 -0
  68. package/dist/mapping/loaders/mojang.js +64 -0
  69. package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
  70. package/dist/mapping/loaders/tiny-loom.js +73 -0
  71. package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
  72. package/dist/mapping/loaders/tiny-maven.js +104 -0
  73. package/dist/mapping/loaders/types.d.ts +14 -0
  74. package/dist/mapping/loaders/types.js +2 -0
  75. package/dist/mapping/lookup.d.ts +52 -0
  76. package/dist/mapping/lookup.js +496 -0
  77. package/dist/mapping/parsers/normalize.d.ts +10 -0
  78. package/dist/mapping/parsers/normalize.js +52 -0
  79. package/dist/mapping/parsers/proguard.d.ts +20 -0
  80. package/dist/mapping/parsers/proguard.js +138 -0
  81. package/dist/mapping/parsers/symbol-records.d.ts +27 -0
  82. package/dist/mapping/parsers/symbol-records.js +216 -0
  83. package/dist/mapping/parsers/tiny.d.ts +9 -0
  84. package/dist/mapping/parsers/tiny.js +96 -0
  85. package/dist/mapping/types.d.ts +147 -0
  86. package/dist/mapping/types.js +2 -0
  87. package/dist/mapping-pipeline-service.d.ts +10 -1
  88. package/dist/mapping-pipeline-service.js +16 -3
  89. package/dist/mapping-service.d.ts +15 -144
  90. package/dist/mapping-service.js +179 -1119
  91. package/dist/mixin/access-validators.d.ts +9 -0
  92. package/dist/mixin/access-validators.js +257 -0
  93. package/dist/mixin/annotation-validators.d.ts +5 -0
  94. package/dist/mixin/annotation-validators.js +162 -0
  95. package/dist/mixin/helpers.d.ts +28 -0
  96. package/dist/mixin/helpers.js +315 -0
  97. package/dist/mixin/parsed-validator.d.ts +8 -0
  98. package/dist/mixin/parsed-validator.js +337 -0
  99. package/dist/mixin/types.d.ts +208 -0
  100. package/dist/mixin/types.js +28 -0
  101. package/dist/mixin-validator.d.ts +9 -201
  102. package/dist/mixin-validator.js +8 -1005
  103. package/dist/observability.d.ts +18 -1
  104. package/dist/observability.js +44 -1
  105. package/dist/response-utils.d.ts +44 -10
  106. package/dist/response-utils.js +131 -17
  107. package/dist/source/access-validate.d.ts +4 -0
  108. package/dist/source/access-validate.js +254 -0
  109. package/dist/source/artifact-resolver.d.ts +110 -0
  110. package/dist/source/artifact-resolver.js +1174 -0
  111. package/dist/source/cache-metrics.d.ts +26 -0
  112. package/dist/source/cache-metrics.js +172 -0
  113. package/dist/source/class-source/members-builder.d.ts +34 -0
  114. package/dist/source/class-source/members-builder.js +46 -0
  115. package/dist/source/class-source/snippet-builder.d.ts +19 -0
  116. package/dist/source/class-source/snippet-builder.js +46 -0
  117. package/dist/source/class-source-helpers.d.ts +34 -0
  118. package/dist/source/class-source-helpers.js +140 -0
  119. package/dist/source/class-source.d.ts +42 -0
  120. package/dist/source/class-source.js +883 -0
  121. package/dist/source/descriptor-utils.d.ts +6 -0
  122. package/dist/source/descriptor-utils.js +37 -0
  123. package/dist/source/file-access.d.ts +4 -0
  124. package/dist/source/file-access.js +102 -0
  125. package/dist/source/indexer.d.ts +82 -0
  126. package/dist/source/indexer.js +505 -0
  127. package/dist/source/lifecycle/diff-utils.d.ts +9 -0
  128. package/dist/source/lifecycle/diff-utils.js +107 -0
  129. package/dist/source/lifecycle/diff.d.ts +2 -0
  130. package/dist/source/lifecycle/diff.js +265 -0
  131. package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
  132. package/dist/source/lifecycle/mapping-helpers.js +327 -0
  133. package/dist/source/lifecycle/runtime-check.d.ts +2 -0
  134. package/dist/source/lifecycle/runtime-check.js +142 -0
  135. package/dist/source/lifecycle/trace.d.ts +2 -0
  136. package/dist/source/lifecycle/trace.js +231 -0
  137. package/dist/source/lifecycle.d.ts +4 -0
  138. package/dist/source/lifecycle.js +5 -0
  139. package/dist/source/search.d.ts +51 -0
  140. package/dist/source/search.js +676 -0
  141. package/dist/source/shared-utils.d.ts +6 -0
  142. package/dist/source/shared-utils.js +55 -0
  143. package/dist/source/state.d.ts +21 -0
  144. package/dist/source/state.js +19 -0
  145. package/dist/source/symbol-resolver.d.ts +3 -0
  146. package/dist/source/symbol-resolver.js +212 -0
  147. package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
  148. package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
  149. package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
  150. package/dist/source/validate-mixin/pipeline/parse.js +10 -0
  151. package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
  152. package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
  153. package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
  154. package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
  155. package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
  156. package/dist/source/validate-mixin/pipeline-context.js +93 -0
  157. package/dist/source/validate-mixin.d.ts +22 -0
  158. package/dist/source/validate-mixin.js +799 -0
  159. package/dist/source/workspace-target.d.ts +18 -0
  160. package/dist/source/workspace-target.js +305 -0
  161. package/dist/source-resolver.d.ts +9 -1
  162. package/dist/source-resolver.js +14 -6
  163. package/dist/source-service.d.ts +178 -105
  164. package/dist/source-service.js +72 -5312
  165. package/dist/stage-emitter.d.ts +13 -0
  166. package/dist/stage-emitter.js +30 -0
  167. package/dist/stdio-supervisor.d.ts +61 -0
  168. package/dist/stdio-supervisor.js +326 -9
  169. package/dist/storage/artifacts-repo.d.ts +4 -1
  170. package/dist/storage/artifacts-repo.js +33 -5
  171. package/dist/storage/files-repo.d.ts +0 -2
  172. package/dist/storage/files-repo.js +0 -11
  173. package/dist/storage/migrations.d.ts +1 -1
  174. package/dist/storage/migrations.js +10 -2
  175. package/dist/storage/schema.d.ts +2 -0
  176. package/dist/storage/schema.js +25 -0
  177. package/dist/tool-contract-manifest.d.ts +1 -1
  178. package/dist/tool-contract-manifest.js +23 -6
  179. package/dist/tool-guidance.d.ts +82 -0
  180. package/dist/tool-guidance.js +734 -0
  181. package/dist/tool-schema-registry.d.ts +16 -0
  182. package/dist/tool-schema-registry.js +37 -0
  183. package/dist/tool-schemas.d.ts +3518 -0
  184. package/dist/tool-schemas.js +813 -0
  185. package/dist/types.d.ts +39 -0
  186. package/dist/version-service.js +7 -6
  187. package/dist/workspace-context-cache.d.ts +32 -0
  188. package/dist/workspace-context-cache.js +66 -0
  189. package/dist/workspace-mapping-service.d.ts +16 -0
  190. package/dist/workspace-mapping-service.js +173 -1
  191. package/docs/README-ja.md +414 -0
  192. package/docs/examples.md +483 -0
  193. package/docs/tool-reference.md +459 -0
  194. package/package.json +5 -2
@@ -0,0 +1,315 @@
1
+ export const TOOL_RESOLUTION_PATHS = [
2
+ "target-mapping-failed",
3
+ "member-remap-failed",
4
+ "source-signature-unavailable"
5
+ ];
6
+ const MAPPING_WARNING_RE = /(?:mapping|remap|fallback|could not map)/i;
7
+ const CONFIG_WARNING_RE = /(?:version|gradle|jar\b|properties|project)/i;
8
+ const PARSE_WARNING_RE = /(?:could not parse|parse\s+warning|missing method attribute)/i;
9
+ export function classifyStructuredWarning(message) {
10
+ return {
11
+ severity: MAPPING_WARNING_RE.test(message) ? "warning" : PARSE_WARNING_RE.test(message) ? "warning" : "info",
12
+ message,
13
+ category: MAPPING_WARNING_RE.test(message)
14
+ ? "mapping"
15
+ : PARSE_WARNING_RE.test(message)
16
+ ? "parse"
17
+ : CONFIG_WARNING_RE.test(message)
18
+ ? "configuration"
19
+ : "validation"
20
+ };
21
+ }
22
+ /* ------------------------------------------------------------------ */
23
+ /* Levenshtein distance */
24
+ /* ------------------------------------------------------------------ */
25
+ export function levenshteinDistance(a, b) {
26
+ const la = a.length;
27
+ const lb = b.length;
28
+ if (la === 0)
29
+ return lb;
30
+ if (lb === 0)
31
+ return la;
32
+ // Single-row DP
33
+ const prev = new Array(lb + 1);
34
+ for (let j = 0; j <= lb; j++)
35
+ prev[j] = j;
36
+ for (let i = 1; i <= la; i++) {
37
+ let diagPrev = prev[0];
38
+ prev[0] = i;
39
+ for (let j = 1; j <= lb; j++) {
40
+ const temp = prev[j];
41
+ if (a[i - 1] === b[j - 1]) {
42
+ prev[j] = diagPrev;
43
+ }
44
+ else {
45
+ prev[j] = 1 + Math.min(diagPrev, prev[j - 1], prev[j]);
46
+ }
47
+ diagPrev = temp;
48
+ }
49
+ }
50
+ return prev[lb];
51
+ }
52
+ export function suggestSimilar(name, candidates, maxDistance = 3, maxResults = 3) {
53
+ const normalizedName = name.toLowerCase();
54
+ const scored = [];
55
+ for (const candidate of candidates) {
56
+ const normalizedCandidate = candidate.toLowerCase();
57
+ if (Math.abs(normalizedName.length - normalizedCandidate.length) > maxDistance) {
58
+ continue;
59
+ }
60
+ const distance = levenshteinDistance(normalizedName, normalizedCandidate);
61
+ if (distance <= maxDistance && distance > 0) {
62
+ scored.push({ candidate, distance });
63
+ }
64
+ }
65
+ scored.sort((a, b) => a.distance - b.distance);
66
+ return scored.slice(0, maxResults).map((s) => s.candidate);
67
+ }
68
+ /* ------------------------------------------------------------------ */
69
+ /* Method reference helpers */
70
+ /* ------------------------------------------------------------------ */
71
+ /**
72
+ * Strip owner prefix (`Lowner;`) and JVM descriptor (`(...)V`) from a
73
+ * Mixin method reference, returning just the method name.
74
+ *
75
+ * Examples:
76
+ * "playerTouch(Lnet/minecraft/world/entity/player/Player;)V" → "playerTouch"
77
+ * "Lnet/minecraft/SomeClass;tick(I)V" → "tick"
78
+ * "<init>" → "<init>"
79
+ * "<init>()V" → "<init>"
80
+ * "tick" → "tick"
81
+ */
82
+ function stripOwnerPrefix(ref) {
83
+ if (!ref.startsWith("L"))
84
+ return ref;
85
+ const ownerEnd = ref.indexOf(";");
86
+ if (ownerEnd === -1)
87
+ return ref;
88
+ const parenIdx = ref.indexOf("(");
89
+ // Owner prefixes appear before the descriptor, e.g. Lpkg/Class;method(I)V.
90
+ // If ';' appears inside the descriptor, this is not an owner prefix.
91
+ if (parenIdx !== -1 && ownerEnd > parenIdx)
92
+ return ref;
93
+ return ref.substring(ownerEnd + 1);
94
+ }
95
+ export function extractMethodName(ref) {
96
+ let s = stripOwnerPrefix(ref);
97
+ // Remove descriptor: everything from '(' onwards
98
+ const parenIdx = s.indexOf("(");
99
+ if (parenIdx !== -1) {
100
+ s = s.substring(0, parenIdx);
101
+ }
102
+ return s;
103
+ }
104
+ /**
105
+ * Extract the JVM descriptor portion from a method reference, if present.
106
+ *
107
+ * Examples:
108
+ * "playerTouch(Lnet/minecraft/world/entity/player/Player;)V" → "(Lnet/minecraft/world/entity/player/Player;)V"
109
+ * "tick" → undefined
110
+ */
111
+ export function extractMethodDescriptor(ref) {
112
+ // After stripping optional owner prefix, find '('
113
+ const s = stripOwnerPrefix(ref);
114
+ const parenIdx = s.indexOf("(");
115
+ if (parenIdx !== -1) {
116
+ return s.substring(parenIdx);
117
+ }
118
+ return undefined;
119
+ }
120
+ /* ------------------------------------------------------------------ */
121
+ /* Member-set helpers */
122
+ /* ------------------------------------------------------------------ */
123
+ export function allMethodNames(members) {
124
+ return [
125
+ ...members.constructors.map((m) => m.name),
126
+ ...members.methods.map((m) => m.name)
127
+ ];
128
+ }
129
+ export function allFieldNames(members) {
130
+ return members.fields.map((m) => m.name);
131
+ }
132
+ export function accessLevelFromFlags(accessFlags) {
133
+ if (accessFlags == null) {
134
+ return undefined;
135
+ }
136
+ if ((accessFlags & 0x0001) !== 0) {
137
+ return "public";
138
+ }
139
+ if ((accessFlags & 0x0004) !== 0) {
140
+ return "protected";
141
+ }
142
+ if ((accessFlags & 0x0002) !== 0) {
143
+ return "private";
144
+ }
145
+ return "package-private";
146
+ }
147
+ /* ------------------------------------------------------------------ */
148
+ /* Confidence / risk scoring */
149
+ /* ------------------------------------------------------------------ */
150
+ export function computeFalsePositiveRisk(healthReport, resolutionPath, issueConfidence) {
151
+ if (!healthReport)
152
+ return undefined;
153
+ if (healthReport.overallHealthy === false) {
154
+ if (resolutionPath === "source-signature-unavailable" ||
155
+ resolutionPath === "target-mapping-failed" ||
156
+ resolutionPath === "member-remap-failed")
157
+ return "high";
158
+ if (issueConfidence === "uncertain")
159
+ return "medium";
160
+ return "medium";
161
+ }
162
+ if (healthReport.memberRemapAvailable === false) {
163
+ if (resolutionPath === "member-remap-failed")
164
+ return "high";
165
+ if (issueConfidence === "uncertain")
166
+ return "medium";
167
+ }
168
+ return undefined;
169
+ }
170
+ export function computeConfidenceBreakdown(healthReport, provenance, remapFailureCount, skippedMemberCount) {
171
+ const baseScore = 100;
172
+ const penalties = [];
173
+ let score = baseScore;
174
+ if (healthReport) {
175
+ if (!healthReport.overallHealthy) {
176
+ penalties.push({ reason: "mapping-health", points: 30 });
177
+ score -= 30;
178
+ }
179
+ if (!healthReport.tinyMappingsAvailable) {
180
+ penalties.push({ reason: "tiny-mappings-unavailable", points: 20 });
181
+ score -= 20;
182
+ }
183
+ if (!healthReport.memberRemapAvailable) {
184
+ penalties.push({ reason: "member-remap-unavailable", points: 15 });
185
+ score -= 15;
186
+ }
187
+ }
188
+ if (provenance?.scopeFallback) {
189
+ penalties.push({ reason: "scope-fallback", points: 10 });
190
+ score -= 10;
191
+ }
192
+ if (provenance && provenance.requestedMapping !== provenance.mappingApplied) {
193
+ penalties.push({ reason: "mapping-mismatch", points: 15 });
194
+ score -= 15;
195
+ }
196
+ if (skippedMemberCount > 0) {
197
+ penalties.push({ reason: "members-skipped", points: 25 });
198
+ score -= 25;
199
+ }
200
+ const remapPenalty = Math.min(remapFailureCount * 2, 20);
201
+ if (remapPenalty > 0) {
202
+ penalties.push({ reason: "remap-failures", points: remapPenalty });
203
+ score -= remapPenalty;
204
+ }
205
+ return {
206
+ baseScore,
207
+ score: Math.max(score, 0),
208
+ penalties
209
+ };
210
+ }
211
+ /* ------------------------------------------------------------------ */
212
+ /* Resolved-member summarisation */
213
+ /* ------------------------------------------------------------------ */
214
+ export function summarizeResolvedMembers(resolvedMembers) {
215
+ return {
216
+ membersValidated: resolvedMembers.filter((member) => member.status === "resolved").length,
217
+ membersSkipped: resolvedMembers.filter((member) => member.status === "skipped").length,
218
+ membersMissing: resolvedMembers.filter((member) => member.status === "not-found").length
219
+ };
220
+ }
221
+ export function computeValidationStatus(summary) {
222
+ if (summary.errors > 0 || summary.definiteErrors > 0) {
223
+ return "invalid";
224
+ }
225
+ if (summary.warnings > 0 ||
226
+ summary.membersSkipped > 0 ||
227
+ (summary.targetsDeferredBudget ?? 0) > 0 ||
228
+ summary.degradedReason !== undefined) {
229
+ return "partial";
230
+ }
231
+ return "full";
232
+ }
233
+ export function buildQuickSummary(status, summary, context) {
234
+ const base = status === "full"
235
+ ? `${summary.membersValidated} member(s) validated successfully.`
236
+ : `${summary.definiteErrors} error(s), ${summary.uncertainErrors} uncertain, ${summary.warnings} warning(s). ${summary.membersValidated} validated, ${summary.membersSkipped} member(s) skipped, ${summary.membersMissing} member(s) missing.`;
237
+ const notes = [];
238
+ const scopeFallback = context?.provenance?.scopeFallback;
239
+ if (scopeFallback) {
240
+ notes.push(`Scope fell back from "${scopeFallback.requested}" to "${scopeFallback.applied}" (${scopeFallback.reason}).`);
241
+ }
242
+ const healthReport = context?.healthReport;
243
+ if (healthReport && !healthReport.overallHealthy) {
244
+ const degradations = healthReport.degradations.length > 0
245
+ ? healthReport.degradations.join("; ")
246
+ : "mapping infrastructure degraded";
247
+ notes.push(`Mapping health degraded: ${degradations}.`);
248
+ }
249
+ if (summary.degradedReason === "stage-budget-pre-target") {
250
+ notes.push("Budget exhausted before any target processed — increase budget or split mixinConfigPath.");
251
+ }
252
+ else if ((summary.targetsDeferredBudget ?? 0) > 0) {
253
+ notes.push(`${summary.targetsDeferredBudget} target(s) deferred by stage budget — narrow mixinConfigPath or split the run.`);
254
+ }
255
+ return notes.length > 0 ? `${base} ${notes.join(" ")}` : base;
256
+ }
257
+ export function addSkippedMembers(parsed, resolvedMembers) {
258
+ for (const inj of parsed.injections) {
259
+ resolvedMembers.push({
260
+ annotation: `@${inj.annotation}`,
261
+ name: extractMethodName(inj.method),
262
+ line: inj.line,
263
+ status: "skipped"
264
+ });
265
+ }
266
+ for (const shadow of parsed.shadows) {
267
+ resolvedMembers.push({
268
+ annotation: "@Shadow",
269
+ name: shadow.name,
270
+ line: shadow.line,
271
+ status: "skipped"
272
+ });
273
+ }
274
+ for (const accessor of parsed.accessors) {
275
+ resolvedMembers.push({
276
+ annotation: `@${accessor.annotation}`,
277
+ name: accessor.targetName,
278
+ line: accessor.line,
279
+ status: "skipped"
280
+ });
281
+ }
282
+ }
283
+ export function refreshMixinValidationOutcome(result) {
284
+ const memberSummary = result.resolvedMembers
285
+ ? summarizeResolvedMembers(result.resolvedMembers)
286
+ : {
287
+ membersValidated: result.summary.membersValidated,
288
+ membersSkipped: result.summary.membersSkipped,
289
+ membersMissing: result.summary.membersMissing
290
+ };
291
+ // Preserve budget signals across refresh; otherwise pre-target partial
292
+ // paths regress to "full" on a second recompute.
293
+ const preservedBudget = {
294
+ targetsDeferredBudget: result.summary.targetsDeferredBudget,
295
+ degradedReason: result.summary.degradedReason
296
+ };
297
+ result.summary = {
298
+ ...result.summary,
299
+ ...memberSummary,
300
+ ...(preservedBudget.targetsDeferredBudget !== undefined
301
+ ? { targetsDeferredBudget: preservedBudget.targetsDeferredBudget }
302
+ : {}),
303
+ ...(preservedBudget.degradedReason !== undefined
304
+ ? { degradedReason: preservedBudget.degradedReason }
305
+ : {})
306
+ };
307
+ result.validationStatus = computeValidationStatus(result.summary);
308
+ result.valid = result.summary.definiteErrors === 0;
309
+ result.quickSummary = buildQuickSummary(result.validationStatus, result.summary, {
310
+ provenance: result.provenance,
311
+ healthReport: result.toolHealth
312
+ });
313
+ return result;
314
+ }
315
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedMixin } from "../mixin-parser.js";
2
+ import type { IssueConfidence, MappingHealthReport, MixinValidationProvenance, MixinValidationResult, ResolvedTargetMembers } from "./types.js";
3
+ export declare function validateParsedMixin(parsed: ParsedMixin, targetMembers: Map<string, ResolvedTargetMembers>, warnings: string[], provenance?: MixinValidationProvenance, confidence?: IssueConfidence, mappingFailedTargets?: Set<string>, explain?: boolean, remapFailedMembers?: Map<string, Set<string>>, signatureFailedTargets?: Set<string>, suggestedCallContext?: {
4
+ scope?: string;
5
+ sourcePriority?: string;
6
+ projectPath?: string;
7
+ mapping?: string;
8
+ }, warningMode?: "full" | "aggregated", healthReport?: MappingHealthReport, symbolExistsButSignatureFailed?: Set<string>, deferredBudgetTargets?: Set<string>): MixinValidationResult;
@@ -0,0 +1,337 @@
1
+ import { buildSuggestedCall } from "../build-suggested-call.js";
2
+ import { TOOL_RESOLUTION_PATHS, addSkippedMembers, buildQuickSummary, classifyStructuredWarning, computeConfidenceBreakdown, computeValidationStatus, summarizeResolvedMembers } from "./helpers.js";
3
+ import { validateAccessor, validateInjection, validateShadow } from "./annotation-validators.js";
4
+ export function validateParsedMixin(parsed, targetMembers, warnings, provenance, confidence, mappingFailedTargets, explain, remapFailedMembers, signatureFailedTargets, suggestedCallContext, warningMode, healthReport, symbolExistsButSignatureFailed, deferredBudgetTargets) {
5
+ const issues = [];
6
+ const targetNames = parsed.targets.map((t) => t.className);
7
+ const confidenceReason = confidence === "uncertain"
8
+ ? `Mapping fallback: requested "${provenance?.requestedMapping}" but applied "${provenance?.mappingApplied}".`
9
+ : confidence === "likely"
10
+ ? "Some members could not be remapped."
11
+ : undefined;
12
+ const resolvedMembers = [];
13
+ // Check target classes exist
14
+ for (const target of parsed.targets) {
15
+ if (!targetMembers.has(target.className)) {
16
+ if (mappingFailedTargets?.has(target.className)) {
17
+ // Mapping failure — report as warning with distinct kind
18
+ issues.push({
19
+ severity: "warning",
20
+ kind: "target-mapping-failed",
21
+ annotation: "@Mixin",
22
+ target: target.className,
23
+ message: `Could not map target class "${target.className}" to obfuscated namespace; class may still exist under a different mapping.`,
24
+ confidence: "uncertain",
25
+ confidenceReason: `Mapping from "${provenance?.requestedMapping}" to obfuscated failed for this class.`,
26
+ category: "mapping",
27
+ resolutionPath: "target-mapping-failed",
28
+ falsePositiveRisk: healthReport?.overallHealthy === false ? "high" : "medium"
29
+ });
30
+ }
31
+ else if (symbolExistsButSignatureFailed?.has(target.className)) {
32
+ // Symbol exists in mapping graph but getSignature failed — tool limitation, not code issue
33
+ issues.push({
34
+ severity: "warning",
35
+ kind: "validation-incomplete",
36
+ annotation: "@Mixin",
37
+ target: target.className,
38
+ message: `Target class "${target.className}" exists in mapping data but could not be loaded from game jar (tool limitation). Members not validated.`,
39
+ confidence: "uncertain",
40
+ confidenceReason: "Class exists in mapping graph but bytecode signature extraction failed.",
41
+ category: "resolution",
42
+ resolutionPath: "source-signature-unavailable",
43
+ issueOrigin: "tool_issue",
44
+ falsePositiveRisk: "high"
45
+ });
46
+ addSkippedMembers(parsed, resolvedMembers);
47
+ }
48
+ else if (deferredBudgetTargets?.has(target.className)) {
49
+ // Deferred by stage budget; the targetOutcomes entry already records
50
+ // this, so skip member validation rather than emit a not-found error.
51
+ addSkippedMembers(parsed, resolvedMembers);
52
+ }
53
+ else if (signatureFailedTargets?.has(target.className)) {
54
+ issues.push({
55
+ severity: "warning",
56
+ kind: "validation-incomplete",
57
+ annotation: "@Mixin",
58
+ target: target.className,
59
+ message: `Target class "${target.className}" could not load enough target metadata for reliable validation. Members were not validated.`,
60
+ confidence: "uncertain",
61
+ confidenceReason: "Target bytecode could not be loaded and fallback existence checks were unavailable.",
62
+ category: "resolution",
63
+ resolutionPath: "source-signature-unavailable",
64
+ issueOrigin: "tool_issue",
65
+ falsePositiveRisk: "high"
66
+ });
67
+ addSkippedMembers(parsed, resolvedMembers);
68
+ }
69
+ else {
70
+ issues.push({
71
+ severity: "error",
72
+ kind: "target-not-found",
73
+ annotation: "@Mixin",
74
+ target: target.className,
75
+ message: `Target class "${target.className}" not found in game jar.`,
76
+ confidence,
77
+ confidenceReason,
78
+ category: "validation",
79
+ resolutionPath: "target-class-missing"
80
+ });
81
+ }
82
+ }
83
+ }
84
+ // Only validate members against targets that were resolved
85
+ const resolvedTargetNames = targetNames.filter((t) => targetMembers.has(t));
86
+ for (const inj of parsed.injections) {
87
+ validateInjection(inj, targetMembers, resolvedTargetNames, issues, resolvedMembers, confidence, confidenceReason, remapFailedMembers, signatureFailedTargets, healthReport);
88
+ }
89
+ for (const shadow of parsed.shadows) {
90
+ validateShadow(shadow, targetMembers, resolvedTargetNames, issues, resolvedMembers, confidence, confidenceReason, remapFailedMembers, signatureFailedTargets, healthReport);
91
+ }
92
+ for (const accessor of parsed.accessors) {
93
+ validateAccessor(accessor, targetMembers, resolvedTargetNames, issues, resolvedMembers, confidence, confidenceReason, remapFailedMembers, signatureFailedTargets, healthReport);
94
+ }
95
+ // Add parse warnings — escalate @Accessor/@Invoker/@Shadow parse failures to issues
96
+ for (const pw of parsed.parseWarnings) {
97
+ if (/@(Accessor|Invoker|Shadow)\b/.test(pw)) {
98
+ const annotation = pw.includes("@Accessor") ? "@Accessor"
99
+ : pw.includes("@Invoker") ? "@Invoker" : "@Shadow";
100
+ issues.push({
101
+ severity: "warning",
102
+ kind: "unknown-annotation",
103
+ annotation,
104
+ target: parsed.className,
105
+ message: pw,
106
+ confidence: "uncertain",
107
+ confidenceReason: "Parser could not extract member declaration; the annotation may be valid.",
108
+ category: "parse",
109
+ issueOrigin: "parser_limitation",
110
+ falsePositiveRisk: "high"
111
+ });
112
+ }
113
+ else {
114
+ warnings.push(pw);
115
+ }
116
+ }
117
+ // Contradiction detection: if some same-annotation members resolved OK but parse failed for others, note it
118
+ const resolvedAnnotations = new Set();
119
+ for (const member of resolvedMembers) {
120
+ if (member.status === "resolved") {
121
+ resolvedAnnotations.add(member.annotation);
122
+ }
123
+ }
124
+ let errorCount = 0;
125
+ let warningCount = 0;
126
+ let definiteErrors = 0;
127
+ let uncertainErrors = 0;
128
+ let resolutionErrors = 0;
129
+ let parseWarningCount = 0;
130
+ for (const issue of issues) {
131
+ if (issue.category === "parse" && resolvedAnnotations.has(issue.annotation)) {
132
+ issue.message += " (Note: other members with the same annotation resolved successfully.)";
133
+ }
134
+ if (!issue.category) {
135
+ issue.category = issue.resolutionPath ? "resolution" : "validation";
136
+ }
137
+ if (!issue.issueOrigin) {
138
+ if (issue.category === "parse") {
139
+ issue.issueOrigin = "parser_limitation";
140
+ }
141
+ else {
142
+ issue.issueOrigin = issue.resolutionPath && TOOL_RESOLUTION_PATHS.includes(issue.resolutionPath)
143
+ ? "tool_issue"
144
+ : "code_issue";
145
+ }
146
+ }
147
+ if (issue.severity === "error") {
148
+ errorCount++;
149
+ if (issue.confidence === "uncertain") {
150
+ uncertainErrors++;
151
+ }
152
+ else {
153
+ definiteErrors++;
154
+ }
155
+ }
156
+ else {
157
+ warningCount++;
158
+ }
159
+ if (issue.resolutionPath != null) {
160
+ resolutionErrors++;
161
+ }
162
+ if (issue.category === "parse") {
163
+ parseWarningCount++;
164
+ }
165
+ }
166
+ // Enrich issues with explanations and suggested calls when explain=true
167
+ if (explain) {
168
+ const version = provenance?.version;
169
+ const mapping = provenance?.requestedMapping;
170
+ const symbolLookupContext = {};
171
+ if (suggestedCallContext?.sourcePriority) {
172
+ symbolLookupContext.sourcePriority = suggestedCallContext.sourcePriority;
173
+ }
174
+ const classSourceContext = {};
175
+ if (suggestedCallContext?.scope)
176
+ classSourceContext.scope = suggestedCallContext.scope;
177
+ if (suggestedCallContext?.sourcePriority)
178
+ classSourceContext.sourcePriority = suggestedCallContext.sourcePriority;
179
+ if (suggestedCallContext?.projectPath)
180
+ classSourceContext.projectPath = suggestedCallContext.projectPath;
181
+ if (suggestedCallContext?.mapping)
182
+ classSourceContext.mapping = suggestedCallContext.mapping;
183
+ const assignSuggested = (issue, tool, params) => {
184
+ // Result-level suggestions go through the same schema gate as
185
+ // error-side suggestedCall payloads; a payload that does not parse
186
+ // stays unset rather than reaching the agent in broken form.
187
+ const gated = buildSuggestedCall({ tool, params });
188
+ if (gated.suggestedCall) {
189
+ issue.suggestedCall = gated.suggestedCall;
190
+ }
191
+ };
192
+ for (const issue of issues) {
193
+ switch (issue.kind) {
194
+ case "target-not-found":
195
+ issue.explanation = `The class "${issue.target}" was not found in the game jar. It may be misspelled, from a different version, or use a different mapping namespace.`;
196
+ if (version && mapping) {
197
+ assignSuggested(issue, "check-symbol-exists", {
198
+ kind: "class",
199
+ name: issue.target,
200
+ version,
201
+ sourceMapping: mapping,
202
+ nameMode: "auto",
203
+ ...symbolLookupContext
204
+ });
205
+ }
206
+ break;
207
+ case "validation-incomplete":
208
+ issue.explanation = `Target metadata for "${issue.target}" could not be loaded reliably, so validation was only partial. This usually indicates a tool or environment limitation rather than a confirmed code error.`;
209
+ if (version) {
210
+ assignSuggested(issue, "get-class-source", {
211
+ className: issue.target,
212
+ target: { type: "resolve", kind: "version", value: version },
213
+ ...(mapping ? { mapping } : {}),
214
+ mode: "metadata",
215
+ ...classSourceContext
216
+ });
217
+ }
218
+ break;
219
+ case "target-mapping-failed":
220
+ issue.explanation = `Mapping lookup failed for "${issue.target}". The class may exist under a different name in the target namespace.`;
221
+ if (version && mapping) {
222
+ assignSuggested(issue, "check-symbol-exists", {
223
+ kind: "class",
224
+ name: issue.target,
225
+ version,
226
+ sourceMapping: mapping,
227
+ nameMode: "auto",
228
+ ...symbolLookupContext
229
+ });
230
+ }
231
+ break;
232
+ case "method-not-found": {
233
+ const parts = issue.target.split("#");
234
+ const className = parts[0] ?? issue.target;
235
+ issue.explanation = `The method was not found in the target class. It may be named differently in the current mapping, or might not exist in this version.`;
236
+ if (version) {
237
+ assignSuggested(issue, "get-class-source", {
238
+ className,
239
+ target: { type: "resolve", kind: "version", value: version },
240
+ ...(mapping ? { mapping } : {}),
241
+ mode: "metadata",
242
+ ...classSourceContext
243
+ });
244
+ }
245
+ break;
246
+ }
247
+ case "field-not-found": {
248
+ const parts = issue.target.split("#");
249
+ const ownerName = parts[0] ?? issue.target;
250
+ const fieldName = parts[1] ?? issue.target;
251
+ issue.explanation = `The field "${fieldName}" was not found in the target class. Verify the field name matches the expected mapping namespace.`;
252
+ if (version && mapping) {
253
+ assignSuggested(issue, "check-symbol-exists", {
254
+ kind: "field",
255
+ owner: ownerName,
256
+ name: fieldName,
257
+ version,
258
+ sourceMapping: mapping,
259
+ ...symbolLookupContext
260
+ });
261
+ }
262
+ break;
263
+ }
264
+ }
265
+ }
266
+ }
267
+ const structuredWarnings = warnings.map(classifyStructuredWarning);
268
+ // Warning aggregation mode
269
+ let aggregatedWarnings;
270
+ let outputWarnings = warnings;
271
+ let outputStructuredWarnings = structuredWarnings.length > 0 ? structuredWarnings : undefined;
272
+ if (warningMode === "aggregated" && structuredWarnings.length > 0) {
273
+ const groupMap = new Map();
274
+ for (const sw of structuredWarnings) {
275
+ const cat = sw.category ?? "validation";
276
+ const existing = groupMap.get(cat);
277
+ if (existing) {
278
+ existing.count++;
279
+ if (existing.samples.length < 2) {
280
+ existing.samples.push(sw.message);
281
+ }
282
+ }
283
+ else {
284
+ groupMap.set(cat, { count: 1, samples: [sw.message] });
285
+ }
286
+ }
287
+ aggregatedWarnings = [...groupMap.entries()].map(([category, { count, samples }]) => ({
288
+ category,
289
+ count,
290
+ samples
291
+ }));
292
+ outputWarnings = [];
293
+ outputStructuredWarnings = undefined;
294
+ }
295
+ // Compute confidence score
296
+ const remapFailureCount = provenance?.remapFailures ?? 0;
297
+ const memberSummary = summarizeResolvedMembers(resolvedMembers);
298
+ const confidenceBreakdown = healthReport
299
+ ? computeConfidenceBreakdown(healthReport, provenance, remapFailureCount, memberSummary.membersSkipped)
300
+ : undefined;
301
+ const confidenceScore = confidenceBreakdown?.score;
302
+ const total = parsed.injections.length + parsed.shadows.length + parsed.accessors.length;
303
+ const summary = {
304
+ injections: parsed.injections.length,
305
+ shadows: parsed.shadows.length,
306
+ accessors: parsed.accessors.length,
307
+ total,
308
+ ...memberSummary,
309
+ errors: errorCount,
310
+ warnings: warningCount,
311
+ definiteErrors,
312
+ uncertainErrors,
313
+ resolutionErrors,
314
+ parseWarnings: parseWarningCount
315
+ };
316
+ const validationStatus = computeValidationStatus(summary);
317
+ const quickSummary = buildQuickSummary(validationStatus, summary, { provenance, healthReport });
318
+ return {
319
+ className: parsed.className,
320
+ targets: targetNames,
321
+ priority: parsed.priority,
322
+ valid: definiteErrors === 0,
323
+ validationStatus,
324
+ issues,
325
+ summary,
326
+ provenance,
327
+ warnings: outputWarnings,
328
+ structuredWarnings: outputStructuredWarnings,
329
+ aggregatedWarnings,
330
+ resolvedMembers: resolvedMembers.length > 0 ? resolvedMembers : undefined,
331
+ toolHealth: healthReport,
332
+ confidenceScore,
333
+ confidenceBreakdown,
334
+ quickSummary
335
+ };
336
+ }
337
+ //# sourceMappingURL=parsed-validator.js.map