@adhisang/minecraft-modding-mcp 2.1.0 → 3.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 (32) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/README.md +224 -802
  3. package/dist/cache-registry.d.ts +95 -0
  4. package/dist/cache-registry.js +541 -0
  5. package/dist/entry-tools/analyze-mod-service.d.ts +207 -0
  6. package/dist/entry-tools/analyze-mod-service.js +309 -0
  7. package/dist/entry-tools/analyze-symbol-service.d.ts +209 -0
  8. package/dist/entry-tools/analyze-symbol-service.js +359 -0
  9. package/dist/entry-tools/compare-minecraft-service.d.ts +210 -0
  10. package/dist/entry-tools/compare-minecraft-service.js +429 -0
  11. package/dist/entry-tools/entry-tool-schema.d.ts +6 -0
  12. package/dist/entry-tools/entry-tool-schema.js +10 -0
  13. package/dist/entry-tools/inspect-minecraft-service.d.ts +1954 -0
  14. package/dist/entry-tools/inspect-minecraft-service.js +1030 -0
  15. package/dist/entry-tools/manage-cache-service.d.ts +130 -0
  16. package/dist/entry-tools/manage-cache-service.js +264 -0
  17. package/dist/entry-tools/request-normalizers.d.ts +10 -0
  18. package/dist/entry-tools/request-normalizers.js +36 -0
  19. package/dist/entry-tools/response-contract.d.ts +45 -0
  20. package/dist/entry-tools/response-contract.js +99 -0
  21. package/dist/entry-tools/validate-project-service.d.ts +543 -0
  22. package/dist/entry-tools/validate-project-service.js +414 -0
  23. package/dist/index.js +183 -59
  24. package/dist/observability.d.ts +18 -2
  25. package/dist/observability.js +47 -10
  26. package/dist/source-service.d.ts +0 -1
  27. package/dist/source-service.js +44 -54
  28. package/dist/storage/files-repo.d.ts +1 -0
  29. package/dist/storage/files-repo.js +29 -5
  30. package/dist/tool-contract-manifest.d.ts +4 -0
  31. package/dist/tool-contract-manifest.js +139 -0
  32. package/package.json +1 -1
@@ -0,0 +1,1030 @@
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, createSummarySubject, 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"]).default("auto")
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"]).default("auto")
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().default(false),
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 && 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
+ summarizeRequestedSubject(subject) {
264
+ if (subject.kind === "search") {
265
+ if (subject.queryMode !== "auto") {
266
+ return subject;
267
+ }
268
+ const { queryMode: _queryMode, ...requestedSubject } = subject;
269
+ return requestedSubject;
270
+ }
271
+ if (subject.kind === "workspace" && subject.focus?.kind === "search") {
272
+ if (subject.focus.queryMode !== "auto") {
273
+ return subject;
274
+ }
275
+ const { queryMode: _queryMode, ...requestedFocus } = subject.focus;
276
+ return {
277
+ ...subject,
278
+ focus: requestedFocus
279
+ };
280
+ }
281
+ return subject;
282
+ }
283
+ async resolveArtifactReference(subject) {
284
+ if (subject.kind === "artifact") {
285
+ return this.resolveArtifactRef(subject.artifact, subject);
286
+ }
287
+ if (subject.kind === "class" || subject.kind === "file" || subject.kind === "search") {
288
+ if (!subject.artifact) {
289
+ throw createError({
290
+ code: ERROR_CODES.INVALID_INPUT,
291
+ message: `${subject.kind} subject requires artifact context.`
292
+ });
293
+ }
294
+ return this.resolveArtifactRef(subject.artifact, subject);
295
+ }
296
+ if (subject.kind === "version") {
297
+ const artifact = await this.deps.resolveArtifact({
298
+ target: { kind: "version", value: subject.version },
299
+ mapping: subject.mapping,
300
+ scope: subject.scope,
301
+ projectPath: subject.projectPath,
302
+ preferProjectVersion: subject.preferProjectVersion,
303
+ strictVersion: subject.strictVersion
304
+ });
305
+ return {
306
+ artifactId: artifact.artifactId,
307
+ artifact,
308
+ version: subject.version,
309
+ warnings: [...artifact.warnings]
310
+ };
311
+ }
312
+ const version = await this.deps.detectProjectMinecraftVersion(subject.projectPath);
313
+ if (!version) {
314
+ return {
315
+ artifactId: "",
316
+ version: undefined,
317
+ warnings: [`Could not infer Minecraft version from ${subject.projectPath}.`]
318
+ };
319
+ }
320
+ const artifact = await this.deps.resolveArtifact({
321
+ target: { kind: "version", value: version },
322
+ mapping: subject.mapping,
323
+ scope: subject.scope,
324
+ projectPath: subject.projectPath,
325
+ preferProjectVersion: subject.preferProjectVersion ?? true,
326
+ strictVersion: subject.strictVersion
327
+ });
328
+ return {
329
+ artifactId: artifact.artifactId,
330
+ artifact,
331
+ version,
332
+ warnings: [...artifact.warnings]
333
+ };
334
+ }
335
+ async resolveArtifactRef(ref, subject) {
336
+ if (ref.type === "resolved-id") {
337
+ return {
338
+ artifactId: ref.artifactId,
339
+ warnings: []
340
+ };
341
+ }
342
+ const artifact = await this.deps.resolveArtifact({
343
+ target: ref.target,
344
+ mapping: "mapping" in subject ? subject.mapping : undefined,
345
+ scope: "scope" in subject ? subject.scope : undefined,
346
+ projectPath: "projectPath" in subject ? subject.projectPath : undefined,
347
+ preferProjectVersion: "preferProjectVersion" in subject ? subject.preferProjectVersion : undefined,
348
+ strictVersion: "strictVersion" in subject ? subject.strictVersion : undefined
349
+ });
350
+ return {
351
+ artifactId: artifact.artifactId,
352
+ artifact,
353
+ warnings: [...artifact.warnings]
354
+ };
355
+ }
356
+ async handleVersions(input, detail, include) {
357
+ const versions = await this.deps.listVersions({
358
+ includeSnapshots: input.includeSnapshots,
359
+ limit: input.limit
360
+ });
361
+ const summary = {
362
+ status: "ok",
363
+ headline: `Found ${versions.totalAvailable} Minecraft versions.`,
364
+ subject: createSummarySubject({
365
+ task: "versions",
366
+ kind: "versions",
367
+ includeSnapshots: input.includeSnapshots === false ? undefined : input.includeSnapshots,
368
+ limit: input.limit
369
+ }),
370
+ counts: {
371
+ releases: versions.releases.length,
372
+ snapshots: versions.snapshots?.length ?? 0
373
+ },
374
+ nextActions: nextActionsOrUndefined([
375
+ createNextAction("inspect-minecraft", {
376
+ task: "artifact",
377
+ subject: {
378
+ kind: "version",
379
+ version: versions.latest.release ?? versions.releases[0]?.id
380
+ }
381
+ })
382
+ ])
383
+ };
384
+ return {
385
+ ...buildEntryToolResult({
386
+ task: "versions",
387
+ summary,
388
+ detail,
389
+ include,
390
+ blocks: {
391
+ versions: {
392
+ latest: versions.latest,
393
+ releases: detail === "summary" ? versions.releases.slice(0, 5) : versions.releases,
394
+ snapshots: input.includeSnapshots ? versions.snapshots : undefined,
395
+ cached: versions.cached
396
+ }
397
+ }
398
+ }),
399
+ warnings: []
400
+ };
401
+ }
402
+ async handleArtifact(subject, detail, include) {
403
+ const resolved = await this.resolveArtifactReference(subject);
404
+ if (!resolved.artifactId) {
405
+ const summary = {
406
+ status: "blocked",
407
+ headline: "Could not resolve an artifact without a Minecraft version.",
408
+ subject: createSummarySubject({
409
+ task: "artifact",
410
+ requested: subject
411
+ }),
412
+ nextActions: nextActionsOrUndefined([
413
+ createNextAction("inspect-minecraft", {
414
+ task: "artifact",
415
+ subject: {
416
+ kind: "version",
417
+ version: "1.21.10"
418
+ }
419
+ })
420
+ ])
421
+ };
422
+ return {
423
+ ...buildEntryToolResult({
424
+ task: "artifact",
425
+ summary,
426
+ detail,
427
+ include,
428
+ blocks: {
429
+ subject: {
430
+ requested: subject
431
+ }
432
+ },
433
+ alwaysBlocks: ["subject"]
434
+ }),
435
+ warnings: resolved.warnings
436
+ };
437
+ }
438
+ const summary = {
439
+ status: "ok",
440
+ headline: `Resolved artifact ${resolved.artifactId}.`,
441
+ subject: createSummarySubject({
442
+ task: "artifact",
443
+ requested: subject,
444
+ artifactId: resolved.artifactId,
445
+ version: resolved.version
446
+ }),
447
+ counts: {
448
+ warnings: resolved.warnings.length
449
+ }
450
+ };
451
+ return {
452
+ ...buildEntryToolResult({
453
+ task: "artifact",
454
+ summary,
455
+ detail,
456
+ include,
457
+ blocks: {
458
+ subject: {
459
+ requested: subject,
460
+ resolved: {
461
+ artifactId: resolved.artifactId,
462
+ version: resolved.version
463
+ }
464
+ },
465
+ artifact: resolved.artifact
466
+ ? {
467
+ artifactId: resolved.artifact.artifactId,
468
+ origin: resolved.artifact.origin,
469
+ mappingApplied: resolved.artifact.mappingApplied,
470
+ version: resolved.artifact.version,
471
+ artifactContents: resolved.artifact.artifactContents
472
+ }
473
+ : { artifactId: resolved.artifactId }
474
+ },
475
+ alwaysBlocks: ["subject"]
476
+ }),
477
+ warnings: resolved.warnings
478
+ };
479
+ }
480
+ async handleClassOverview(subject, detail, include) {
481
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
482
+ throw createError({
483
+ code: ERROR_CODES.INVALID_INPUT,
484
+ message: "class-overview requires a class or workspace focus subject."
485
+ });
486
+ }
487
+ const classSubject = this.buildClassSubject(subject);
488
+ const className = classSubject.className;
489
+ const artifact = await this.resolveClassArtifactReference(subject, classSubject);
490
+ if (!artifact.artifactId) {
491
+ const summary = {
492
+ status: "blocked",
493
+ headline: `Could not resolve artifact context for ${className}.`,
494
+ subject: createSummarySubject({
495
+ task: "class-overview",
496
+ requested: subject,
497
+ className
498
+ })
499
+ };
500
+ return {
501
+ ...buildEntryToolResult({
502
+ task: "class-overview",
503
+ summary,
504
+ detail,
505
+ include,
506
+ blocks: {
507
+ subject: {
508
+ requested: subject
509
+ }
510
+ },
511
+ alwaysBlocks: ["subject"]
512
+ }),
513
+ warnings: artifact.warnings
514
+ };
515
+ }
516
+ const matches = await this.deps.findClass({
517
+ artifactId: artifact.artifactId,
518
+ className,
519
+ limit: 10
520
+ });
521
+ if (matches.total === 0) {
522
+ const summary = {
523
+ status: "not_found",
524
+ headline: `No class match was found for ${className}.`,
525
+ subject: createSummarySubject({
526
+ task: "class-overview",
527
+ requested: subject,
528
+ className,
529
+ artifactId: artifact.artifactId
530
+ })
531
+ };
532
+ return {
533
+ ...buildEntryToolResult({
534
+ task: "class-overview",
535
+ summary,
536
+ detail,
537
+ include,
538
+ blocks: {
539
+ subject: {
540
+ requested: subject,
541
+ resolved: {
542
+ artifactId: artifact.artifactId
543
+ }
544
+ }
545
+ },
546
+ alwaysBlocks: ["subject"]
547
+ }),
548
+ warnings: [...artifact.warnings, ...matches.warnings]
549
+ };
550
+ }
551
+ if (matches.total > 1) {
552
+ const candidateActions = matches.matches.slice(0, 3).map((match) => createNextAction("inspect-minecraft", {
553
+ task: "class-source",
554
+ subject: {
555
+ kind: "class",
556
+ className: match.qualifiedName,
557
+ artifact: {
558
+ type: "resolved-id",
559
+ artifactId: artifact.artifactId
560
+ }
561
+ },
562
+ include: ["source"]
563
+ }));
564
+ const summary = {
565
+ status: "ambiguous",
566
+ headline: `Found ${matches.total} class matches for ${className}.`,
567
+ subject: createSummarySubject({
568
+ task: "class-overview",
569
+ requested: subject,
570
+ className,
571
+ artifactId: artifact.artifactId
572
+ }),
573
+ counts: {
574
+ matches: matches.total
575
+ },
576
+ nextActions: nextActionsOrUndefined(candidateActions)
577
+ };
578
+ return {
579
+ ...buildEntryToolResult({
580
+ task: "class-overview",
581
+ summary,
582
+ detail,
583
+ include,
584
+ blocks: {
585
+ subject: {
586
+ requested: subject,
587
+ resolved: {
588
+ artifactId: artifact.artifactId
589
+ }
590
+ },
591
+ candidates: matches.matches
592
+ },
593
+ alwaysBlocks: ["subject"]
594
+ }),
595
+ warnings: [...artifact.warnings, ...matches.warnings]
596
+ };
597
+ }
598
+ const match = matches.matches[0];
599
+ const metadata = await this.deps.getClassSource({
600
+ className: match.qualifiedName,
601
+ artifactId: artifact.artifactId,
602
+ mode: "metadata"
603
+ });
604
+ const summary = {
605
+ status: "ok",
606
+ headline: `Resolved class overview for ${match.qualifiedName}.`,
607
+ subject: createSummarySubject({
608
+ task: "class-overview",
609
+ requested: subject,
610
+ className: match.qualifiedName,
611
+ artifactId: artifact.artifactId
612
+ }),
613
+ counts: {
614
+ totalLines: metadata.totalLines
615
+ },
616
+ nextActions: nextActionsOrUndefined([
617
+ createNextAction("inspect-minecraft", {
618
+ task: "class-source",
619
+ subject: {
620
+ kind: "class",
621
+ className: match.qualifiedName,
622
+ artifact: {
623
+ type: "resolved-id",
624
+ artifactId: artifact.artifactId
625
+ }
626
+ },
627
+ include: ["source"]
628
+ })
629
+ ])
630
+ };
631
+ return {
632
+ ...buildEntryToolResult({
633
+ task: "class-overview",
634
+ summary,
635
+ detail,
636
+ include,
637
+ blocks: {
638
+ subject: {
639
+ requested: subject,
640
+ resolved: {
641
+ artifactId: artifact.artifactId,
642
+ className: match.qualifiedName
643
+ }
644
+ },
645
+ artifact: artifact.artifact
646
+ ? {
647
+ artifactId: artifact.artifact.artifactId,
648
+ version: artifact.artifact.version,
649
+ origin: artifact.artifact.origin
650
+ }
651
+ : { artifactId: artifact.artifactId },
652
+ class: {
653
+ className: match.qualifiedName,
654
+ filePath: match.filePath,
655
+ totalLines: metadata.totalLines,
656
+ returnedNamespace: metadata.returnedNamespace
657
+ }
658
+ },
659
+ alwaysBlocks: ["subject"]
660
+ }),
661
+ warnings: [...artifact.warnings, ...matches.warnings, ...metadata.warnings]
662
+ };
663
+ }
664
+ async handleClassSource(subject, detail, include) {
665
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
666
+ throw createError({
667
+ code: ERROR_CODES.INVALID_INPUT,
668
+ message: "class-source requires a class or workspace focus subject."
669
+ });
670
+ }
671
+ const classSubject = this.buildClassSubject(subject);
672
+ const className = classSubject.className;
673
+ const artifactContext = await this.resolveClassArtifactReference(subject, classSubject);
674
+ const source = await this.deps.getClassSource({
675
+ className,
676
+ artifactId: artifactContext.artifactId || undefined,
677
+ mapping: classSubject.mapping,
678
+ scope: classSubject.scope,
679
+ projectPath: classSubject.projectPath,
680
+ preferProjectVersion: classSubject.preferProjectVersion,
681
+ strictVersion: classSubject.strictVersion,
682
+ mode: include.includes("source") || detail === "full" ? "snippet" : "metadata"
683
+ });
684
+ const summary = {
685
+ status: "ok",
686
+ headline: `Resolved source for ${source.className}.`,
687
+ subject: createSummarySubject({
688
+ task: "class-source",
689
+ requested: subject,
690
+ className: source.className,
691
+ artifactId: source.artifactId
692
+ }),
693
+ counts: {
694
+ totalLines: source.totalLines
695
+ }
696
+ };
697
+ return {
698
+ ...buildEntryToolResult({
699
+ task: "class-source",
700
+ summary,
701
+ detail,
702
+ include,
703
+ blocks: {
704
+ subject: {
705
+ requested: subject,
706
+ resolved: {
707
+ artifactId: source.artifactId,
708
+ className: source.className
709
+ }
710
+ },
711
+ source: {
712
+ className: source.className,
713
+ mode: source.mode,
714
+ returnedRange: source.returnedRange,
715
+ totalLines: source.totalLines,
716
+ sourceText: source.sourceText
717
+ }
718
+ },
719
+ alwaysBlocks: ["subject"]
720
+ }),
721
+ warnings: [...artifactContext.warnings, ...source.warnings]
722
+ };
723
+ }
724
+ async handleClassMembers(subject, detail, include, limit) {
725
+ if (subject.kind !== "class" && !(subject.kind === "workspace" && subject.focus?.kind === "class")) {
726
+ throw createError({
727
+ code: ERROR_CODES.INVALID_INPUT,
728
+ message: "class-members requires a class or workspace focus subject."
729
+ });
730
+ }
731
+ const classSubject = this.buildClassSubject(subject);
732
+ const artifact = await this.resolveClassArtifactReference(subject, classSubject);
733
+ const members = await this.deps.getClassMembers({
734
+ className: classSubject.className,
735
+ artifactId: artifact.artifactId || undefined,
736
+ mapping: classSubject.mapping,
737
+ scope: classSubject.scope,
738
+ projectPath: classSubject.projectPath,
739
+ preferProjectVersion: classSubject.preferProjectVersion,
740
+ strictVersion: classSubject.strictVersion,
741
+ maxMembers: limit
742
+ });
743
+ const summary = {
744
+ status: members.truncated ? "partial" : "ok",
745
+ headline: `Collected ${members.counts.total} members for ${members.className}.`,
746
+ subject: createSummarySubject({
747
+ task: "class-members",
748
+ requested: subject,
749
+ className: members.className,
750
+ artifactId: members.artifactId
751
+ }),
752
+ counts: members.counts
753
+ };
754
+ return {
755
+ ...buildEntryToolResult({
756
+ task: "class-members",
757
+ summary,
758
+ detail,
759
+ include,
760
+ blocks: {
761
+ subject: {
762
+ requested: subject,
763
+ resolved: {
764
+ artifactId: members.artifactId,
765
+ className: members.className
766
+ }
767
+ },
768
+ members: include.includes("members") || detail !== "summary"
769
+ ? members.members
770
+ : {
771
+ counts: members.counts
772
+ }
773
+ },
774
+ alwaysBlocks: ["subject"]
775
+ }),
776
+ warnings: [...artifact.warnings, ...members.warnings],
777
+ ...(members.truncated
778
+ ? {
779
+ meta: buildEntryToolMeta({
780
+ detail,
781
+ include,
782
+ warnings: [...artifact.warnings, ...members.warnings],
783
+ truncated: createTruncationMeta({
784
+ omittedGroups: ["members"],
785
+ nextActions: [
786
+ createNextAction("inspect-minecraft", {
787
+ task: "class-members",
788
+ detail: "full",
789
+ include: ["members"],
790
+ subject
791
+ })
792
+ ]
793
+ })
794
+ })
795
+ }
796
+ : {})
797
+ };
798
+ }
799
+ async handleSearch(subject, detail, include, limit, cursor) {
800
+ if (subject.kind !== "search" && !(subject.kind === "workspace" && subject.focus?.kind === "search")) {
801
+ throw createError({
802
+ code: ERROR_CODES.INVALID_INPUT,
803
+ message: "search requires a search or workspace focus subject."
804
+ });
805
+ }
806
+ const searchSubject = subject.kind === "search" ? subject : this.requireWorkspaceSearchFocus(subject);
807
+ const requestedSubject = this.summarizeRequestedSubject(subject);
808
+ const queryMode = searchSubject.queryMode ?? "auto";
809
+ const artifact = subject.kind === "search"
810
+ ? await this.resolveArtifactReference(subject)
811
+ : await this.resolveWorkspaceArtifactReference(subject, searchSubject.artifact);
812
+ const search = await this.deps.searchClassSource({
813
+ artifactId: artifact.artifactId,
814
+ query: searchSubject.query,
815
+ intent: searchSubject.intent,
816
+ match: searchSubject.match,
817
+ queryMode,
818
+ limit,
819
+ cursor,
820
+ scope: searchSubject.packagePrefix || searchSubject.fileGlob || searchSubject.symbolKind
821
+ ? {
822
+ packagePrefix: searchSubject.packagePrefix,
823
+ fileGlob: searchSubject.fileGlob,
824
+ symbolKind: searchSubject.symbolKind
825
+ }
826
+ : undefined
827
+ });
828
+ const sampledHits = capArray(search.hits, 5);
829
+ const isAutoSeparatorMiss = search.hits.length === 0 &&
830
+ queryMode === "auto" &&
831
+ /[._$]/.test(searchSubject.query);
832
+ const literalRetrySubject = subject.kind === "search"
833
+ ? {
834
+ ...subject,
835
+ queryMode: "literal"
836
+ }
837
+ : {
838
+ ...subject,
839
+ focus: {
840
+ ...searchSubject,
841
+ kind: "search",
842
+ queryMode: "literal"
843
+ }
844
+ };
845
+ const summary = {
846
+ status: search.hits.length > 0 ? "ok" : "not_found",
847
+ headline: search.hits.length > 0
848
+ ? `Found ${search.hits.length} source hits for ${searchSubject.query}.`
849
+ : `No source hits were found for ${searchSubject.query}.`,
850
+ subject: createSummarySubject({
851
+ task: "search",
852
+ requested: requestedSubject,
853
+ query: searchSubject.query,
854
+ artifactId: artifact.artifactId
855
+ }),
856
+ counts: {
857
+ hits: search.hits.length
858
+ },
859
+ nextActions: nextActionsOrUndefined([
860
+ ...(search.hits.length > 0
861
+ ? [
862
+ createNextAction("inspect-minecraft", {
863
+ task: "file",
864
+ subject: {
865
+ kind: "file",
866
+ filePath: search.hits[0].filePath,
867
+ artifact: {
868
+ type: "resolved-id",
869
+ artifactId: artifact.artifactId
870
+ }
871
+ },
872
+ include: ["source"]
873
+ })
874
+ ]
875
+ : []),
876
+ ...(isAutoSeparatorMiss
877
+ ? [
878
+ createNextAction("inspect-minecraft", {
879
+ task: "search",
880
+ subject: literalRetrySubject
881
+ })
882
+ ]
883
+ : [])
884
+ ]),
885
+ ...(isAutoSeparatorMiss
886
+ ? {
887
+ notes: [
888
+ "Separator query returned no indexed hits. Retry with queryMode=\"literal\" for an explicit full substring scan."
889
+ ]
890
+ }
891
+ : {})
892
+ };
893
+ return {
894
+ ...buildEntryToolResult({
895
+ task: "search",
896
+ summary,
897
+ detail,
898
+ include,
899
+ blocks: {
900
+ subject: {
901
+ requested: requestedSubject,
902
+ resolved: {
903
+ artifactId: artifact.artifactId
904
+ }
905
+ },
906
+ search: {
907
+ query: searchSubject.query,
908
+ hits: detail === "summary" ? sampledHits.items : search.hits,
909
+ nextCursor: search.nextCursor
910
+ }
911
+ },
912
+ alwaysBlocks: ["subject"]
913
+ }),
914
+ warnings: [...artifact.warnings]
915
+ };
916
+ }
917
+ async handleFile(subject, detail, include) {
918
+ if (subject.kind !== "file" && !(subject.kind === "workspace" && subject.focus?.kind === "file")) {
919
+ throw createError({
920
+ code: ERROR_CODES.INVALID_INPUT,
921
+ message: "file task requires a file or workspace focus subject."
922
+ });
923
+ }
924
+ const fileSubject = subject.kind === "file" ? subject : this.requireWorkspaceFileFocus(subject);
925
+ const artifact = subject.kind === "file"
926
+ ? await this.resolveArtifactReference(subject)
927
+ : await this.resolveWorkspaceArtifactReference(subject, fileSubject.artifact);
928
+ const file = await this.deps.getArtifactFile({
929
+ artifactId: artifact.artifactId,
930
+ filePath: fileSubject.filePath
931
+ });
932
+ const summary = {
933
+ status: "ok",
934
+ headline: `Read ${file.filePath}.`,
935
+ subject: createSummarySubject({
936
+ task: "file",
937
+ requested: subject,
938
+ filePath: file.filePath,
939
+ artifactId: artifact.artifactId
940
+ }),
941
+ counts: {
942
+ bytes: file.contentBytes
943
+ }
944
+ };
945
+ return {
946
+ ...buildEntryToolResult({
947
+ task: "file",
948
+ summary,
949
+ detail,
950
+ include,
951
+ blocks: {
952
+ subject: {
953
+ requested: subject,
954
+ resolved: {
955
+ artifactId: artifact.artifactId,
956
+ filePath: file.filePath
957
+ }
958
+ },
959
+ file: {
960
+ filePath: file.filePath,
961
+ contentBytes: file.contentBytes,
962
+ truncated: file.truncated,
963
+ content: include.includes("source") || detail !== "summary" ? file.content : undefined
964
+ }
965
+ },
966
+ alwaysBlocks: ["subject"]
967
+ }),
968
+ warnings: [...artifact.warnings]
969
+ };
970
+ }
971
+ async handleListFiles(subject, detail, include, limit, cursor) {
972
+ const artifact = await this.resolveArtifactReference(subject);
973
+ const files = await this.deps.listArtifactFiles({
974
+ artifactId: artifact.artifactId,
975
+ limit,
976
+ cursor
977
+ });
978
+ const sampled = capArray(files.items, 10);
979
+ const summary = {
980
+ status: "ok",
981
+ headline: `Listed ${files.items.length} files for ${artifact.artifactId}.`,
982
+ subject: createSummarySubject({
983
+ task: "list-files",
984
+ requested: subject,
985
+ artifactId: artifact.artifactId
986
+ }),
987
+ counts: {
988
+ files: files.items.length
989
+ },
990
+ nextActions: nextActionsOrUndefined(files.items.length > 0
991
+ ? [
992
+ createNextAction("inspect-minecraft", {
993
+ task: "file",
994
+ subject: {
995
+ kind: "file",
996
+ filePath: files.items[0],
997
+ artifact: {
998
+ type: "resolved-id",
999
+ artifactId: artifact.artifactId
1000
+ }
1001
+ }
1002
+ })
1003
+ ]
1004
+ : [])
1005
+ };
1006
+ return {
1007
+ ...buildEntryToolResult({
1008
+ task: "list-files",
1009
+ summary,
1010
+ detail,
1011
+ include,
1012
+ blocks: {
1013
+ subject: {
1014
+ requested: subject,
1015
+ resolved: {
1016
+ artifactId: artifact.artifactId
1017
+ }
1018
+ },
1019
+ files: {
1020
+ items: detail === "summary" ? sampled.items : files.items,
1021
+ nextCursor: files.nextCursor
1022
+ }
1023
+ },
1024
+ alwaysBlocks: ["subject"]
1025
+ }),
1026
+ warnings: [...artifact.warnings, ...files.warnings]
1027
+ };
1028
+ }
1029
+ }
1030
+ //# sourceMappingURL=inspect-minecraft-service.js.map