@adhisang/minecraft-modding-mcp 2.0.0 → 3.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 (57) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +139 -30
  3. package/dist/cache-registry.d.ts +95 -0
  4. package/dist/cache-registry.js +541 -0
  5. package/dist/cli.js +31 -4
  6. package/dist/compat-stdio-transport.d.ts +2 -7
  7. package/dist/compat-stdio-transport.js +12 -154
  8. package/dist/entry-tools/analyze-mod-service.d.ts +207 -0
  9. package/dist/entry-tools/analyze-mod-service.js +253 -0
  10. package/dist/entry-tools/analyze-symbol-service.d.ts +209 -0
  11. package/dist/entry-tools/analyze-symbol-service.js +304 -0
  12. package/dist/entry-tools/compare-minecraft-service.d.ts +210 -0
  13. package/dist/entry-tools/compare-minecraft-service.js +397 -0
  14. package/dist/entry-tools/entry-tool-schema.d.ts +6 -0
  15. package/dist/entry-tools/entry-tool-schema.js +10 -0
  16. package/dist/entry-tools/inspect-minecraft-service.d.ts +1953 -0
  17. package/dist/entry-tools/inspect-minecraft-service.js +876 -0
  18. package/dist/entry-tools/manage-cache-service.d.ts +130 -0
  19. package/dist/entry-tools/manage-cache-service.js +229 -0
  20. package/dist/entry-tools/request-normalizers.d.ts +10 -0
  21. package/dist/entry-tools/request-normalizers.js +36 -0
  22. package/dist/entry-tools/response-contract.d.ts +44 -0
  23. package/dist/entry-tools/response-contract.js +96 -0
  24. package/dist/entry-tools/validate-project-service.d.ts +543 -0
  25. package/dist/entry-tools/validate-project-service.js +381 -0
  26. package/dist/index.js +495 -42
  27. package/dist/json-rpc-framing.d.ts +22 -0
  28. package/dist/json-rpc-framing.js +168 -0
  29. package/dist/mapping-pipeline-service.js +9 -1
  30. package/dist/mapping-service.d.ts +9 -0
  31. package/dist/mapping-service.js +183 -60
  32. package/dist/minecraft-explorer-service.d.ts +0 -1
  33. package/dist/minecraft-explorer-service.js +119 -23
  34. package/dist/mixin-validator.d.ts +24 -2
  35. package/dist/mixin-validator.js +223 -98
  36. package/dist/mod-decompile-service.d.ts +5 -0
  37. package/dist/mod-decompile-service.js +40 -5
  38. package/dist/mod-remap-service.js +142 -30
  39. package/dist/path-resolver.js +41 -4
  40. package/dist/registry-service.d.ts +10 -1
  41. package/dist/registry-service.js +154 -22
  42. package/dist/search-hit-accumulator.js +23 -2
  43. package/dist/source-jar-reader.js +16 -2
  44. package/dist/source-resolver.js +6 -7
  45. package/dist/source-service.d.ts +42 -4
  46. package/dist/source-service.js +781 -127
  47. package/dist/stdio-supervisor.d.ts +46 -0
  48. package/dist/stdio-supervisor.js +349 -0
  49. package/dist/storage/files-repo.d.ts +3 -9
  50. package/dist/storage/files-repo.js +66 -43
  51. package/dist/symbols/symbol-extractor.js +6 -4
  52. package/dist/tool-execution-gate.d.ts +15 -0
  53. package/dist/tool-execution-gate.js +58 -0
  54. package/dist/version-diff-service.js +10 -5
  55. package/dist/version-service.js +7 -2
  56. package/dist/workspace-mapping-service.js +12 -0
  57. package/package.json +1 -1
@@ -0,0 +1,876 @@
1
+ import { z } from "zod";
2
+ import { createError, ERROR_CODES } from "../errors.js";
3
+ import { buildIncludeSchema, detailSchema, positiveIntSchema } from "./entry-tool-schema.js";
4
+ import { buildEntryToolResult, buildEntryToolMeta, createNextAction, createTruncationMeta } from "./response-contract.js";
5
+ import { capArray, nextActionsOrUndefined, resolveDetail, resolveInclude } from "./request-normalizers.js";
6
+ const INCLUDE_GROUPS = ["warnings", "provenance", "candidates", "members", "source", "files", "samples", "artifact", "timings"];
7
+ const TASKS = ["auto", "versions", "artifact", "class-overview", "class-source", "class-members", "search", "file", "list-files"];
8
+ const SUBJECT_KINDS = ["version", "artifact", "class", "file", "search", "workspace"];
9
+ const nonEmptyString = z.string().trim().min(1);
10
+ const resolveTargetSchema = z.object({
11
+ kind: z.enum(["version", "jar", "coordinate"]),
12
+ value: nonEmptyString
13
+ });
14
+ const artifactRefSchema = z.discriminatedUnion("type", [
15
+ z.object({
16
+ type: z.literal("resolved-id"),
17
+ artifactId: nonEmptyString
18
+ }),
19
+ z.object({
20
+ type: z.literal("resolve-target"),
21
+ target: resolveTargetSchema
22
+ })
23
+ ]);
24
+ const workspaceFocusSchema = z.discriminatedUnion("kind", [
25
+ z.object({
26
+ kind: z.literal("class"),
27
+ className: nonEmptyString,
28
+ artifact: artifactRefSchema.optional()
29
+ }),
30
+ z.object({
31
+ kind: z.literal("file"),
32
+ filePath: nonEmptyString,
33
+ artifact: artifactRefSchema.optional()
34
+ }),
35
+ z.object({
36
+ kind: z.literal("search"),
37
+ query: nonEmptyString,
38
+ artifact: artifactRefSchema.optional(),
39
+ intent: z.enum(["symbol", "text", "path"]).optional(),
40
+ match: z.enum(["exact", "prefix", "contains", "regex"]).optional(),
41
+ symbolKind: z.enum(["class", "interface", "enum", "record", "method", "field"]).optional(),
42
+ packagePrefix: nonEmptyString.optional(),
43
+ fileGlob: nonEmptyString.optional(),
44
+ queryMode: z.enum(["auto", "token", "literal"]).optional()
45
+ })
46
+ ]);
47
+ const subjectSchema = z.discriminatedUnion("kind", [
48
+ z.object({
49
+ kind: z.literal("version"),
50
+ version: nonEmptyString,
51
+ mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
52
+ scope: z.enum(["vanilla", "merged", "loader"]).optional(),
53
+ projectPath: nonEmptyString.optional(),
54
+ preferProjectVersion: z.boolean().optional(),
55
+ strictVersion: z.boolean().optional()
56
+ }),
57
+ z.object({
58
+ kind: z.literal("artifact"),
59
+ artifact: artifactRefSchema,
60
+ mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
61
+ scope: z.enum(["vanilla", "merged", "loader"]).optional(),
62
+ projectPath: nonEmptyString.optional(),
63
+ preferProjectVersion: z.boolean().optional(),
64
+ strictVersion: z.boolean().optional()
65
+ }),
66
+ z.object({
67
+ kind: z.literal("class"),
68
+ className: nonEmptyString,
69
+ artifact: artifactRefSchema.optional(),
70
+ mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
71
+ scope: z.enum(["vanilla", "merged", "loader"]).optional(),
72
+ projectPath: nonEmptyString.optional(),
73
+ preferProjectVersion: z.boolean().optional(),
74
+ strictVersion: z.boolean().optional()
75
+ }),
76
+ z.object({
77
+ kind: z.literal("file"),
78
+ filePath: nonEmptyString,
79
+ artifact: artifactRefSchema.optional()
80
+ }),
81
+ z.object({
82
+ kind: z.literal("search"),
83
+ query: nonEmptyString,
84
+ artifact: artifactRefSchema.optional(),
85
+ intent: z.enum(["symbol", "text", "path"]).optional(),
86
+ match: z.enum(["exact", "prefix", "contains", "regex"]).optional(),
87
+ symbolKind: z.enum(["class", "interface", "enum", "record", "method", "field"]).optional(),
88
+ packagePrefix: nonEmptyString.optional(),
89
+ fileGlob: nonEmptyString.optional(),
90
+ queryMode: z.enum(["auto", "token", "literal"]).optional()
91
+ }),
92
+ z.object({
93
+ kind: z.literal("workspace"),
94
+ projectPath: nonEmptyString,
95
+ mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
96
+ scope: z.enum(["vanilla", "merged", "loader"]).optional(),
97
+ preferProjectVersion: z.boolean().optional(),
98
+ strictVersion: z.boolean().optional(),
99
+ focus: workspaceFocusSchema.optional()
100
+ })
101
+ ]);
102
+ export const inspectMinecraftShape = {
103
+ task: z.enum(TASKS).optional(),
104
+ subject: subjectSchema.optional(),
105
+ includeSnapshots: z.boolean().optional(),
106
+ detail: detailSchema.optional(),
107
+ include: buildIncludeSchema(INCLUDE_GROUPS),
108
+ limit: positiveIntSchema.optional(),
109
+ cursor: nonEmptyString.optional()
110
+ };
111
+ export const inspectMinecraftSchema = z.object(inspectMinecraftShape).superRefine((value, ctx) => {
112
+ if (!value.subject && value.task !== "versions") {
113
+ ctx.addIssue({
114
+ code: z.ZodIssueCode.custom,
115
+ path: ["subject"],
116
+ message: "subject is required unless task=versions."
117
+ });
118
+ }
119
+ if (value.includeSnapshots !== undefined && value.task && value.task !== "versions") {
120
+ ctx.addIssue({
121
+ code: z.ZodIssueCode.custom,
122
+ path: ["includeSnapshots"],
123
+ message: "includeSnapshots is only supported for task=versions."
124
+ });
125
+ }
126
+ });
127
+ export class InspectMinecraftService {
128
+ deps;
129
+ constructor(deps) {
130
+ this.deps = deps;
131
+ }
132
+ async execute(input) {
133
+ const detail = resolveDetail(input.detail);
134
+ const include = resolveInclude(input.include);
135
+ const task = this.resolveTask(input.task, input.subject);
136
+ switch (task) {
137
+ case "versions":
138
+ return this.handleVersions(input, detail, include);
139
+ case "artifact":
140
+ return this.handleArtifact(input.subject, detail, include);
141
+ case "class-overview":
142
+ return this.handleClassOverview(input.subject, detail, include);
143
+ case "class-source":
144
+ return this.handleClassSource(input.subject, detail, include);
145
+ case "class-members":
146
+ return this.handleClassMembers(input.subject, detail, include, input.limit);
147
+ case "search":
148
+ return this.handleSearch(input.subject, detail, include, input.limit, input.cursor);
149
+ case "file":
150
+ return this.handleFile(input.subject, detail, include);
151
+ case "list-files":
152
+ return this.handleListFiles(input.subject, detail, include, input.limit, input.cursor);
153
+ default:
154
+ throw createError({
155
+ code: ERROR_CODES.INVALID_INPUT,
156
+ message: `Unsupported inspect-minecraft task "${task}".`
157
+ });
158
+ }
159
+ }
160
+ requireWorkspaceClassFocus(subject) {
161
+ if (subject.kind !== "workspace" || subject.focus?.kind !== "class") {
162
+ throw createError({
163
+ code: ERROR_CODES.INVALID_INPUT,
164
+ message: "Workspace focus must be kind=class for this task."
165
+ });
166
+ }
167
+ return subject.focus;
168
+ }
169
+ requireWorkspaceSearchFocus(subject) {
170
+ if (subject.kind !== "workspace" || subject.focus?.kind !== "search") {
171
+ throw createError({
172
+ code: ERROR_CODES.INVALID_INPUT,
173
+ message: "Workspace focus must be kind=search for this task."
174
+ });
175
+ }
176
+ return subject.focus;
177
+ }
178
+ requireWorkspaceFileFocus(subject) {
179
+ if (subject.kind !== "workspace" || subject.focus?.kind !== "file") {
180
+ throw createError({
181
+ code: ERROR_CODES.INVALID_INPUT,
182
+ message: "Workspace focus must be kind=file for this task."
183
+ });
184
+ }
185
+ return subject.focus;
186
+ }
187
+ buildClassSubject(subject) {
188
+ if (subject.kind === "class") {
189
+ return subject;
190
+ }
191
+ const workspaceFocus = this.requireWorkspaceClassFocus(subject);
192
+ return {
193
+ kind: "class",
194
+ className: workspaceFocus.className,
195
+ artifact: workspaceFocus.artifact,
196
+ projectPath: subject.projectPath,
197
+ mapping: subject.mapping,
198
+ scope: subject.scope,
199
+ preferProjectVersion: subject.preferProjectVersion,
200
+ strictVersion: subject.strictVersion
201
+ };
202
+ }
203
+ async resolveClassArtifactReference(subject, classSubject) {
204
+ if (subject.kind === "workspace") {
205
+ return this.resolveWorkspaceArtifactReference(subject, classSubject.artifact);
206
+ }
207
+ return this.resolveArtifactReference(classSubject);
208
+ }
209
+ async resolveWorkspaceArtifactReference(subject, artifactRef) {
210
+ if (!artifactRef) {
211
+ return this.resolveArtifactReference(subject);
212
+ }
213
+ if (artifactRef.type === "resolved-id") {
214
+ return {
215
+ artifactId: artifactRef.artifactId,
216
+ warnings: []
217
+ };
218
+ }
219
+ const artifact = await this.deps.resolveArtifact({
220
+ target: artifactRef.target,
221
+ mapping: subject.mapping,
222
+ scope: subject.scope,
223
+ projectPath: subject.projectPath,
224
+ preferProjectVersion: subject.preferProjectVersion,
225
+ strictVersion: subject.strictVersion
226
+ });
227
+ return {
228
+ artifactId: artifact.artifactId,
229
+ artifact,
230
+ warnings: [...artifact.warnings]
231
+ };
232
+ }
233
+ resolveTask(task, subject) {
234
+ if (task && task !== "auto") {
235
+ return task;
236
+ }
237
+ if (!subject) {
238
+ return "versions";
239
+ }
240
+ switch (subject.kind) {
241
+ case "version":
242
+ case "artifact":
243
+ return "artifact";
244
+ case "workspace":
245
+ switch (subject.focus?.kind) {
246
+ case "class":
247
+ return "class-overview";
248
+ case "search":
249
+ return "search";
250
+ case "file":
251
+ return "file";
252
+ default:
253
+ return "artifact";
254
+ }
255
+ case "class":
256
+ return "class-overview";
257
+ case "file":
258
+ return "file";
259
+ case "search":
260
+ return "search";
261
+ }
262
+ }
263
+ async resolveArtifactReference(subject) {
264
+ if (subject.kind === "artifact") {
265
+ return this.resolveArtifactRef(subject.artifact, subject);
266
+ }
267
+ if (subject.kind === "class" || subject.kind === "file" || subject.kind === "search") {
268
+ if (!subject.artifact) {
269
+ throw createError({
270
+ code: ERROR_CODES.INVALID_INPUT,
271
+ message: `${subject.kind} subject requires artifact context.`
272
+ });
273
+ }
274
+ return this.resolveArtifactRef(subject.artifact, subject);
275
+ }
276
+ if (subject.kind === "version") {
277
+ const artifact = await this.deps.resolveArtifact({
278
+ target: { kind: "version", value: subject.version },
279
+ mapping: subject.mapping,
280
+ scope: subject.scope,
281
+ projectPath: subject.projectPath,
282
+ preferProjectVersion: subject.preferProjectVersion,
283
+ strictVersion: subject.strictVersion
284
+ });
285
+ return {
286
+ artifactId: artifact.artifactId,
287
+ artifact,
288
+ version: subject.version,
289
+ warnings: [...artifact.warnings]
290
+ };
291
+ }
292
+ const version = await this.deps.detectProjectMinecraftVersion(subject.projectPath);
293
+ if (!version) {
294
+ return {
295
+ artifactId: "",
296
+ version: undefined,
297
+ warnings: [`Could not infer Minecraft version from ${subject.projectPath}.`]
298
+ };
299
+ }
300
+ const artifact = await this.deps.resolveArtifact({
301
+ target: { kind: "version", value: version },
302
+ mapping: subject.mapping,
303
+ scope: subject.scope,
304
+ projectPath: subject.projectPath,
305
+ preferProjectVersion: subject.preferProjectVersion ?? true,
306
+ strictVersion: subject.strictVersion
307
+ });
308
+ return {
309
+ artifactId: artifact.artifactId,
310
+ artifact,
311
+ version,
312
+ warnings: [...artifact.warnings]
313
+ };
314
+ }
315
+ async resolveArtifactRef(ref, subject) {
316
+ if (ref.type === "resolved-id") {
317
+ return {
318
+ artifactId: ref.artifactId,
319
+ warnings: []
320
+ };
321
+ }
322
+ const artifact = await this.deps.resolveArtifact({
323
+ target: ref.target,
324
+ mapping: "mapping" in subject ? subject.mapping : undefined,
325
+ scope: "scope" in subject ? subject.scope : undefined,
326
+ projectPath: "projectPath" in subject ? subject.projectPath : undefined,
327
+ preferProjectVersion: "preferProjectVersion" in subject ? subject.preferProjectVersion : undefined,
328
+ strictVersion: "strictVersion" in subject ? subject.strictVersion : undefined
329
+ });
330
+ return {
331
+ artifactId: artifact.artifactId,
332
+ artifact,
333
+ warnings: [...artifact.warnings]
334
+ };
335
+ }
336
+ async handleVersions(input, detail, include) {
337
+ const versions = await this.deps.listVersions({
338
+ includeSnapshots: input.includeSnapshots,
339
+ limit: input.limit
340
+ });
341
+ const summary = {
342
+ status: "ok",
343
+ headline: `Found ${versions.totalAvailable} Minecraft versions.`,
344
+ counts: {
345
+ releases: versions.releases.length,
346
+ snapshots: versions.snapshots?.length ?? 0
347
+ },
348
+ nextActions: nextActionsOrUndefined([
349
+ createNextAction("inspect-minecraft", {
350
+ task: "artifact",
351
+ subject: {
352
+ kind: "version",
353
+ version: versions.latest.release ?? versions.releases[0]?.id
354
+ }
355
+ })
356
+ ])
357
+ };
358
+ return {
359
+ ...buildEntryToolResult({
360
+ task: "versions",
361
+ summary,
362
+ detail,
363
+ include,
364
+ blocks: {
365
+ versions: {
366
+ latest: versions.latest,
367
+ releases: detail === "summary" ? versions.releases.slice(0, 5) : versions.releases,
368
+ snapshots: input.includeSnapshots ? versions.snapshots : undefined,
369
+ cached: versions.cached
370
+ }
371
+ }
372
+ }),
373
+ warnings: []
374
+ };
375
+ }
376
+ async handleArtifact(subject, detail, include) {
377
+ const resolved = await this.resolveArtifactReference(subject);
378
+ if (!resolved.artifactId) {
379
+ const summary = {
380
+ status: "blocked",
381
+ headline: "Could not resolve an artifact without a Minecraft version.",
382
+ nextActions: nextActionsOrUndefined([
383
+ createNextAction("inspect-minecraft", {
384
+ task: "artifact",
385
+ subject: {
386
+ kind: "version",
387
+ version: "1.21.10"
388
+ }
389
+ })
390
+ ])
391
+ };
392
+ return {
393
+ ...buildEntryToolResult({
394
+ task: "artifact",
395
+ summary,
396
+ detail,
397
+ include,
398
+ blocks: {
399
+ subject: {
400
+ requested: subject
401
+ }
402
+ },
403
+ alwaysBlocks: ["subject"]
404
+ }),
405
+ warnings: resolved.warnings
406
+ };
407
+ }
408
+ const summary = {
409
+ status: "ok",
410
+ headline: `Resolved artifact ${resolved.artifactId}.`,
411
+ counts: {
412
+ warnings: resolved.warnings.length
413
+ }
414
+ };
415
+ return {
416
+ ...buildEntryToolResult({
417
+ task: "artifact",
418
+ summary,
419
+ detail,
420
+ include,
421
+ blocks: {
422
+ subject: {
423
+ requested: subject,
424
+ resolved: {
425
+ artifactId: resolved.artifactId,
426
+ version: resolved.version
427
+ }
428
+ },
429
+ artifact: resolved.artifact
430
+ ? {
431
+ artifactId: resolved.artifact.artifactId,
432
+ origin: resolved.artifact.origin,
433
+ mappingApplied: resolved.artifact.mappingApplied,
434
+ version: resolved.artifact.version,
435
+ artifactContents: resolved.artifact.artifactContents
436
+ }
437
+ : { artifactId: resolved.artifactId }
438
+ },
439
+ alwaysBlocks: ["subject"]
440
+ }),
441
+ warnings: resolved.warnings
442
+ };
443
+ }
444
+ async handleClassOverview(subject, detail, include) {
445
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
446
+ throw createError({
447
+ code: ERROR_CODES.INVALID_INPUT,
448
+ message: "class-overview requires a class or workspace focus subject."
449
+ });
450
+ }
451
+ const classSubject = this.buildClassSubject(subject);
452
+ const className = classSubject.className;
453
+ const artifact = await this.resolveClassArtifactReference(subject, classSubject);
454
+ if (!artifact.artifactId) {
455
+ const summary = {
456
+ status: "blocked",
457
+ headline: `Could not resolve artifact context for ${className}.`
458
+ };
459
+ return {
460
+ ...buildEntryToolResult({
461
+ task: "class-overview",
462
+ summary,
463
+ detail,
464
+ include,
465
+ blocks: {
466
+ subject: {
467
+ requested: subject
468
+ }
469
+ },
470
+ alwaysBlocks: ["subject"]
471
+ }),
472
+ warnings: artifact.warnings
473
+ };
474
+ }
475
+ const matches = await this.deps.findClass({
476
+ artifactId: artifact.artifactId,
477
+ className,
478
+ limit: 10
479
+ });
480
+ if (matches.total === 0) {
481
+ const summary = {
482
+ status: "not_found",
483
+ headline: `No class match was found for ${className}.`
484
+ };
485
+ return {
486
+ ...buildEntryToolResult({
487
+ task: "class-overview",
488
+ summary,
489
+ detail,
490
+ include,
491
+ blocks: {
492
+ subject: {
493
+ requested: subject,
494
+ resolved: {
495
+ artifactId: artifact.artifactId
496
+ }
497
+ }
498
+ },
499
+ alwaysBlocks: ["subject"]
500
+ }),
501
+ warnings: [...artifact.warnings, ...matches.warnings]
502
+ };
503
+ }
504
+ if (matches.total > 1) {
505
+ const candidateActions = matches.matches.slice(0, 3).map((match) => createNextAction("inspect-minecraft", {
506
+ task: "class-source",
507
+ subject: {
508
+ kind: "class",
509
+ className: match.qualifiedName,
510
+ artifact: {
511
+ type: "resolved-id",
512
+ artifactId: artifact.artifactId
513
+ }
514
+ },
515
+ include: ["source"]
516
+ }));
517
+ const summary = {
518
+ status: "ambiguous",
519
+ headline: `Found ${matches.total} class matches for ${className}.`,
520
+ counts: {
521
+ matches: matches.total
522
+ },
523
+ nextActions: nextActionsOrUndefined(candidateActions)
524
+ };
525
+ return {
526
+ ...buildEntryToolResult({
527
+ task: "class-overview",
528
+ summary,
529
+ detail,
530
+ include,
531
+ blocks: {
532
+ subject: {
533
+ requested: subject,
534
+ resolved: {
535
+ artifactId: artifact.artifactId
536
+ }
537
+ },
538
+ candidates: matches.matches
539
+ },
540
+ alwaysBlocks: ["subject"]
541
+ }),
542
+ warnings: [...artifact.warnings, ...matches.warnings]
543
+ };
544
+ }
545
+ const match = matches.matches[0];
546
+ const metadata = await this.deps.getClassSource({
547
+ className: match.qualifiedName,
548
+ artifactId: artifact.artifactId,
549
+ mode: "metadata"
550
+ });
551
+ const summary = {
552
+ status: "ok",
553
+ headline: `Resolved class overview for ${match.qualifiedName}.`,
554
+ counts: {
555
+ totalLines: metadata.totalLines
556
+ },
557
+ nextActions: nextActionsOrUndefined([
558
+ createNextAction("inspect-minecraft", {
559
+ task: "class-source",
560
+ subject: {
561
+ kind: "class",
562
+ className: match.qualifiedName,
563
+ artifact: {
564
+ type: "resolved-id",
565
+ artifactId: artifact.artifactId
566
+ }
567
+ },
568
+ include: ["source"]
569
+ })
570
+ ])
571
+ };
572
+ return {
573
+ ...buildEntryToolResult({
574
+ task: "class-overview",
575
+ summary,
576
+ detail,
577
+ include,
578
+ blocks: {
579
+ subject: {
580
+ requested: subject,
581
+ resolved: {
582
+ artifactId: artifact.artifactId,
583
+ className: match.qualifiedName
584
+ }
585
+ },
586
+ artifact: artifact.artifact
587
+ ? {
588
+ artifactId: artifact.artifact.artifactId,
589
+ version: artifact.artifact.version,
590
+ origin: artifact.artifact.origin
591
+ }
592
+ : { artifactId: artifact.artifactId },
593
+ class: {
594
+ className: match.qualifiedName,
595
+ filePath: match.filePath,
596
+ totalLines: metadata.totalLines,
597
+ returnedNamespace: metadata.returnedNamespace
598
+ }
599
+ },
600
+ alwaysBlocks: ["subject"]
601
+ }),
602
+ warnings: [...artifact.warnings, ...matches.warnings, ...metadata.warnings]
603
+ };
604
+ }
605
+ async handleClassSource(subject, detail, include) {
606
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
607
+ throw createError({
608
+ code: ERROR_CODES.INVALID_INPUT,
609
+ message: "class-source requires a class or workspace focus subject."
610
+ });
611
+ }
612
+ const classSubject = this.buildClassSubject(subject);
613
+ const className = classSubject.className;
614
+ const artifactContext = await this.resolveClassArtifactReference(subject, classSubject);
615
+ const source = await this.deps.getClassSource({
616
+ className,
617
+ artifactId: artifactContext.artifactId || undefined,
618
+ mapping: classSubject.mapping,
619
+ scope: classSubject.scope,
620
+ projectPath: classSubject.projectPath,
621
+ preferProjectVersion: classSubject.preferProjectVersion,
622
+ strictVersion: classSubject.strictVersion,
623
+ mode: include.includes("source") || detail === "full" ? "snippet" : "metadata"
624
+ });
625
+ const summary = {
626
+ status: "ok",
627
+ headline: `Resolved source for ${source.className}.`,
628
+ counts: {
629
+ totalLines: source.totalLines
630
+ }
631
+ };
632
+ return {
633
+ ...buildEntryToolResult({
634
+ task: "class-source",
635
+ summary,
636
+ detail,
637
+ include,
638
+ blocks: {
639
+ subject: {
640
+ requested: subject,
641
+ resolved: {
642
+ artifactId: source.artifactId,
643
+ className: source.className
644
+ }
645
+ },
646
+ source: {
647
+ className: source.className,
648
+ mode: source.mode,
649
+ returnedRange: source.returnedRange,
650
+ totalLines: source.totalLines,
651
+ sourceText: source.sourceText
652
+ }
653
+ },
654
+ alwaysBlocks: ["subject"]
655
+ }),
656
+ warnings: [...artifactContext.warnings, ...source.warnings]
657
+ };
658
+ }
659
+ async handleClassMembers(subject, detail, include, limit) {
660
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
661
+ throw createError({
662
+ code: ERROR_CODES.INVALID_INPUT,
663
+ message: "class-members requires a class or workspace focus subject."
664
+ });
665
+ }
666
+ const classSubject = this.buildClassSubject(subject);
667
+ const artifact = await this.resolveClassArtifactReference(subject, classSubject);
668
+ const members = await this.deps.getClassMembers({
669
+ className: classSubject.className,
670
+ artifactId: artifact.artifactId || undefined,
671
+ mapping: classSubject.mapping,
672
+ scope: classSubject.scope,
673
+ projectPath: classSubject.projectPath,
674
+ preferProjectVersion: classSubject.preferProjectVersion,
675
+ strictVersion: classSubject.strictVersion,
676
+ maxMembers: limit
677
+ });
678
+ const summary = {
679
+ status: members.truncated ? "partial" : "ok",
680
+ headline: `Collected ${members.counts.total} members for ${members.className}.`,
681
+ counts: members.counts
682
+ };
683
+ return {
684
+ ...buildEntryToolResult({
685
+ task: "class-members",
686
+ summary,
687
+ detail,
688
+ include,
689
+ blocks: {
690
+ subject: {
691
+ requested: subject,
692
+ resolved: {
693
+ artifactId: members.artifactId,
694
+ className: members.className
695
+ }
696
+ },
697
+ members: include.includes("members") || detail !== "summary"
698
+ ? members.members
699
+ : {
700
+ counts: members.counts
701
+ }
702
+ },
703
+ alwaysBlocks: ["subject"]
704
+ }),
705
+ warnings: [...artifact.warnings, ...members.warnings],
706
+ ...(members.truncated
707
+ ? {
708
+ meta: buildEntryToolMeta({
709
+ detail,
710
+ include,
711
+ warnings: [...artifact.warnings, ...members.warnings],
712
+ truncated: createTruncationMeta({
713
+ omittedGroups: ["members"],
714
+ nextActions: [
715
+ createNextAction("inspect-minecraft", {
716
+ task: "class-members",
717
+ detail: "full",
718
+ include: ["members"],
719
+ subject
720
+ })
721
+ ]
722
+ })
723
+ })
724
+ }
725
+ : {})
726
+ };
727
+ }
728
+ async handleSearch(subject, detail, include, limit, cursor) {
729
+ if (subject.kind !== "search" && !(subject.kind === "workspace" && subject.focus?.kind === "search")) {
730
+ throw createError({
731
+ code: ERROR_CODES.INVALID_INPUT,
732
+ message: "search requires a search or workspace focus subject."
733
+ });
734
+ }
735
+ const searchSubject = subject.kind === "search" ? subject : this.requireWorkspaceSearchFocus(subject);
736
+ const artifact = subject.kind === "search"
737
+ ? await this.resolveArtifactReference(subject)
738
+ : await this.resolveWorkspaceArtifactReference(subject, searchSubject.artifact);
739
+ const search = await this.deps.searchClassSource({
740
+ artifactId: artifact.artifactId,
741
+ query: searchSubject.query,
742
+ intent: searchSubject.intent,
743
+ match: searchSubject.match,
744
+ queryMode: searchSubject.queryMode,
745
+ limit,
746
+ cursor,
747
+ scope: searchSubject.packagePrefix || searchSubject.fileGlob || searchSubject.symbolKind
748
+ ? {
749
+ packagePrefix: searchSubject.packagePrefix,
750
+ fileGlob: searchSubject.fileGlob,
751
+ symbolKind: searchSubject.symbolKind
752
+ }
753
+ : undefined
754
+ });
755
+ const sampledHits = capArray(search.hits, 5);
756
+ const summary = {
757
+ status: search.hits.length > 0 ? "ok" : "not_found",
758
+ headline: search.hits.length > 0
759
+ ? `Found ${search.hits.length} source hits for ${searchSubject.query}.`
760
+ : `No source hits were found for ${searchSubject.query}.`,
761
+ counts: {
762
+ hits: search.hits.length
763
+ }
764
+ };
765
+ return {
766
+ ...buildEntryToolResult({
767
+ task: "search",
768
+ summary,
769
+ detail,
770
+ include,
771
+ blocks: {
772
+ subject: {
773
+ requested: subject,
774
+ resolved: {
775
+ artifactId: artifact.artifactId
776
+ }
777
+ },
778
+ search: {
779
+ query: searchSubject.query,
780
+ hits: detail === "summary" ? sampledHits.items : search.hits,
781
+ nextCursor: search.nextCursor
782
+ }
783
+ },
784
+ alwaysBlocks: ["subject"]
785
+ }),
786
+ warnings: [...artifact.warnings]
787
+ };
788
+ }
789
+ async handleFile(subject, detail, include) {
790
+ if (subject.kind !== "file" && !(subject.kind === "workspace" && subject.focus?.kind === "file")) {
791
+ throw createError({
792
+ code: ERROR_CODES.INVALID_INPUT,
793
+ message: "file task requires a file or workspace focus subject."
794
+ });
795
+ }
796
+ const fileSubject = subject.kind === "file" ? subject : this.requireWorkspaceFileFocus(subject);
797
+ const artifact = subject.kind === "file"
798
+ ? await this.resolveArtifactReference(subject)
799
+ : await this.resolveWorkspaceArtifactReference(subject, fileSubject.artifact);
800
+ const file = await this.deps.getArtifactFile({
801
+ artifactId: artifact.artifactId,
802
+ filePath: fileSubject.filePath
803
+ });
804
+ const summary = {
805
+ status: "ok",
806
+ headline: `Read ${file.filePath}.`,
807
+ counts: {
808
+ bytes: file.contentBytes
809
+ }
810
+ };
811
+ return {
812
+ ...buildEntryToolResult({
813
+ task: "file",
814
+ summary,
815
+ detail,
816
+ include,
817
+ blocks: {
818
+ subject: {
819
+ requested: subject,
820
+ resolved: {
821
+ artifactId: artifact.artifactId,
822
+ filePath: file.filePath
823
+ }
824
+ },
825
+ file: {
826
+ filePath: file.filePath,
827
+ contentBytes: file.contentBytes,
828
+ truncated: file.truncated,
829
+ content: include.includes("source") || detail !== "summary" ? file.content : undefined
830
+ }
831
+ },
832
+ alwaysBlocks: ["subject"]
833
+ }),
834
+ warnings: [...artifact.warnings]
835
+ };
836
+ }
837
+ async handleListFiles(subject, detail, include, limit, cursor) {
838
+ const artifact = await this.resolveArtifactReference(subject);
839
+ const files = await this.deps.listArtifactFiles({
840
+ artifactId: artifact.artifactId,
841
+ limit,
842
+ cursor
843
+ });
844
+ const sampled = capArray(files.items, 10);
845
+ const summary = {
846
+ status: "ok",
847
+ headline: `Listed ${files.items.length} files for ${artifact.artifactId}.`,
848
+ counts: {
849
+ files: files.items.length
850
+ }
851
+ };
852
+ return {
853
+ ...buildEntryToolResult({
854
+ task: "list-files",
855
+ summary,
856
+ detail,
857
+ include,
858
+ blocks: {
859
+ subject: {
860
+ requested: subject,
861
+ resolved: {
862
+ artifactId: artifact.artifactId
863
+ }
864
+ },
865
+ files: {
866
+ items: detail === "summary" ? sampled.items : files.items,
867
+ nextCursor: files.nextCursor
868
+ }
869
+ },
870
+ alwaysBlocks: ["subject"]
871
+ }),
872
+ warnings: [...artifact.warnings, ...files.warnings]
873
+ };
874
+ }
875
+ }
876
+ //# sourceMappingURL=inspect-minecraft-service.js.map