@adhisang/minecraft-modding-mcp 6.1.1 → 6.3.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.
- package/CHANGELOG.md +40 -1
- package/README.md +12 -4
- package/dist/cache-registry.d.ts +1 -1
- package/dist/cache-registry.js +3 -0
- package/dist/entry-tools/analyze-mod-service.d.ts +12 -6
- package/dist/entry-tools/analyze-mod-service.js +37 -3
- package/dist/entry-tools/analyze-symbol-service.d.ts +6 -4
- package/dist/entry-tools/analyze-symbol-service.js +37 -2
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +7 -3
- package/dist/entry-tools/inspect-minecraft/internal.js +43 -15
- package/dist/entry-tools/inspect-minecraft-service.d.ts +12 -12
- package/dist/entry-tools/inspect-minecraft-service.js +1 -1
- package/dist/entry-tools/manage-cache-service.d.ts +4 -4
- package/dist/error-mapping.d.ts +13 -0
- package/dist/error-mapping.js +35 -2
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/index.js +39 -20
- package/dist/mapping/internal-types.d.ts +7 -0
- package/dist/mapping/types.d.ts +18 -0
- package/dist/mapping-service.js +16 -3
- package/dist/minecraft-explorer-service.d.ts +4 -0
- package/dist/minecraft-explorer-service.js +156 -17
- package/dist/mod-analyzer.d.ts +7 -0
- package/dist/mod-analyzer.js +28 -7
- package/dist/source/artifact-resolver.d.ts +2 -0
- package/dist/source/artifact-resolver.js +24 -1
- package/dist/source/class-source/members-builder.d.ts +4 -0
- package/dist/source/class-source/members-builder.js +3 -1
- package/dist/source/class-source.d.ts +3 -1
- package/dist/source/class-source.js +192 -19
- package/dist/source/did-you-mean.d.ts +14 -0
- package/dist/source/did-you-mean.js +79 -0
- package/dist/source/file-access.js +159 -3
- package/dist/source/indexer.js +72 -2
- package/dist/source/lifecycle/runtime-check.js +9 -5
- package/dist/source/nested-jars.d.ts +78 -0
- package/dist/source/nested-jars.js +267 -0
- package/dist/source/workspace-target.js +5 -2
- package/dist/source-jar-reader.d.ts +16 -0
- package/dist/source-jar-reader.js +82 -0
- package/dist/source-service.d.ts +37 -0
- package/dist/source-service.js +52 -6
- package/dist/stage-emitter.js +24 -8
- package/dist/stdio-supervisor.d.ts +92 -9
- package/dist/stdio-supervisor.js +915 -103
- package/dist/tool-contract-manifest.js +2 -2
- package/dist/tool-guidance.js +115 -7
- package/dist/tool-schemas.d.ts +1343 -149
- package/dist/tool-schemas.js +39 -7
- package/dist/types.d.ts +23 -0
- package/dist/workspace-mapping-service.d.ts +1 -0
- package/dist/workspace-mapping-service.js +120 -8
- package/docs/README-ja.md +4 -0
- package/docs/tool-reference.md +92 -6
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,46 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [6.3.0] - 2026-07-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `find-class` accepts top-level `projectPath` for workspace-relative `target.kind="workspace"` and dependency targets using `versionFromProject`, and searches simple or qualified class names across nested `.class` inventories when the resolved artifact is a Jar-in-Jar shell such as the Fabric API umbrella JAR. Nested matches retain the existing response shape, deduplicate repeated qualified names, and honor `limit`; top-level matches remain directly usable by the source/member tools.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- `inspect-minecraft` now describes `task`, structured workspace `subject.focus`, and each class/search/file focus branch in `tools/list`. A string focus remains invalid instead of being guessed or coerced, but its `ERR_INVALID_INPUT` response now includes three schema-validated `exampleCalls` that preserve valid workspace context and show the exact object shapes for retrying.
|
|
17
|
+
- `find-class` no longer interprets a native dependency or Jar-in-Jar shell miss as evidence of obfuscated Minecraft runtime names, so it does not recommend `mapping="mojang"` for those artifacts. Dotted inner-class names discovered in a shell also continue through `get-class-source` instead of missing the binary `$` entry.
|
|
18
|
+
|
|
19
|
+
## [6.2.0] - 2026-07-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Workspace ergonomics for artifact addressing, all additive: (1) `find-class`, `get-artifact-file`, `list-artifact-files`, `search-class-source`, and `index-artifact` accept the shared `target` shape as an alternative to a flat `artifactId` (mutually exclusive — exactly one must be supplied; existing `artifactId` calls are unchanged). (2) `analyze-symbol` infers an omitted `version` from a supplied `projectPath` instead of rejecting at schema time; inferred runs always carry a `versionInference { version, source }` block plus a warning, an explicit `version` is never overridden, and an undetectable version fails with `ERR_WORKSPACE_VERSION_UNRESOLVED`. (3) `inspect-minecraft` direct subjects (`class`/`file`/`search`) without `subject.artifact` auto-resolve through the workspace only when exactly one workspace is known, always with a provenance warning; several known workspaces refuse with `workspaceCandidates`, and an explicit `subject.artifact` is never overridden.
|
|
24
|
+
|
|
25
|
+
- Bytecode member reads now surface annotation metadata: annotation-type classes (`@interface`) expose each member's default value as `annotationDefault` (rendered from the `AnnotationDefault` attribute, including string/enum/class/array/nested-annotation and numeric constants), and `get-class-members` gains an optional `includeAnnotations` flag that adds runtime-visible member annotations (e.g. `@java.lang.Deprecated`) to each member. Defaults are always present when the classfile carries them; member annotations appear only under the opt-in flag. `analyze-mod` `task="members"` includes both without a flag. No new dependencies — the existing classfile reader parses the attribute bodies.
|
|
26
|
+
|
|
27
|
+
- `analyze-mod` gains a `members` task (`subject: { kind: "class", jarPath, className }`) that reads a mod class's constructors, fields, and methods (all access levels, including private and protected) directly from bytecode — no decompiler invocation on this path, unlike `task: "class-source"`. Responses carry `extractionMethod: "bytecode-only"`. Additive: existing tasks and inputs are unchanged, and the tool count stays at 41.
|
|
28
|
+
|
|
29
|
+
- `get-artifact-file` serves `assets/**` and `data/**` text files (including vanilla `data/minecraft/dimension_type/*.json`, and resources in any jar-backed artifact such as mod jars) directly from the backing jar when the source index has no row for them — responses carry `deliveryMode: "jar-read-through"`. Delivery is text-only with a 512 KiB per-file cap and explicit `truncated` flag; binary entries answer with size metadata and a `contentOmittedReason` instead of content; traversal-shaped paths are rejected with `ERR_INVALID_INPUT`; misses return `ERR_FILE_NOT_FOUND` with `nearbyPaths` hints that surface directory relocations such as the `assets/minecraft/models/item/*` → `assets/minecraft/items/*` move. The `list-artifact-files` assets/data warning now points at this read-through path.
|
|
30
|
+
|
|
31
|
+
- Class-not-found errors (`ERR_CLASS_NOT_FOUND`) from `get-class-source`, `get-class-members`, and their batch variants now include a top-level `didYouMean` array with ranked near-miss candidates from the artifact's symbol index — `{ className, matchReason }` with reasons `exact-simple-name` (moved class, ranked first), `case-insensitive`, and `edit-distance:N`. A stale fully-qualified name like a pre-move `ItemTransform` package now names the current location instead of dead-ending. The array is empty when the index has no usable candidates, and candidates are phrased as hints, not assertions.
|
|
32
|
+
|
|
33
|
+
- Jar-in-Jar shell jars (near-zero own classes, content bundled under `META-INF/jars`, like the Fabric API umbrella jar) resolve instead of failing with `ERR_DECOMPILER_FAILED`. The resolved artifact carries `qualityFlags: ["shell-jar"]` and the nested-jar inventory in `provenance.nestedJars` (also surfaced by `analyze-mod-jar` as `nestedJars`); `get-class-source` and `get-class-members` redirect automatically to the single nested jar containing the requested class, marking the response with `provenance.nestedJar`. A class present in several nested jars returns the new `ERR_NESTED_JAR_AMBIGUOUS` error with `nestedJarCandidates` instead of a silent pick, and class-not-found errors on shells list the inventory. Extracted nested jars live in a content-addressed `nested-jars` cache directory, manageable via `manage-cache` `cacheKinds: ["nested-jar"]`.
|
|
34
|
+
|
|
35
|
+
- `validate-project` calls now have a supervisor-owned 120-second end-to-end deadline, configurable with `MCP_VALIDATE_PROJECT_TIMEOUT_MS` from 10,000 through 600,000 ms. Expiry returns the standard synthetic `{ error, meta }` tool-result envelope with `ERR_TOOL_TIMEOUT`, queue/running phase diagnostics, redacted progress context, and restart-initiation state. Queue expiry leaves the active worker untouched; running expiry replaces the isolated worker and preserves admitted later requests when initialization replay succeeds. Replacement startup/replay failure terminalizes queued work with existing restart errors. The supervisor FIFO holds at most two worker-bound requests and returns `ERR_LIMIT_EXCEEDED` (tool calls) or JSON-RPC `-32000` (non-tool requests) on overflow; cap-blocked requests fail immediately and undeliverable notifications are warning-dropped. POSIX process-group cleanup treats an already-gone process group as clean so stale cleanup tokens do not block later validation.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- The two constant per-response warning sentences on unobfuscated versions are replaced by structured flags on `mappingContext` — agents that pattern-matched the old strings should read the flags instead. `"Version X is unobfuscated; mapping graph is empty because the runtime already uses deobfuscated names."` is now `mappingContext.unobfuscatedRuntime: true`, and `"Version X is unobfuscated; validated symbol existence against runtime bytecode."` is now `mappingContext.runtimeValidated: true` (`get-class-api-matrix`, which has no `mappingContext`, reports a top-level `unobfuscatedRuntime: true`). No information is removed: the flags carry the same facts, the full former wording stays documented in `docs/tool-reference.md`, and all other warnings are untouched.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Loom split-source workspaces (a `minecraft-common` / `minecraft-clientOnly` sources-jar pair with no merged jar) no longer lose one half's classes to single-jar selection: version-target resolution with project Loom sources indexes the best-scored jar of the other half too (`provenance.companionSourceJars` names it), so client-only classes like `net.minecraft.client.renderer.block.model.ItemTransform` are indexed and findable via symbol search. Class-not-found errors for version targets additionally carry an `exampleCalls` retry using `scope: "vanilla"` (existing enum values only), whose client-jar decompilation also contains client-only classes.
|
|
44
|
+
- Inherited-member expansion no longer emits `Could not resolve super class ...` warnings for platform packages that are never inside a Minecraft jar (`java.*`, `javax.*`, `jdk.*`, `sun.*`, and the separately-shipped `com.mojang.serialization.*` — each verified against real 26.x and 1.21.10 jars). Suppression is allowlist-only: resolution failures for real Minecraft classes, including `com.mojang.blaze3d.*` client-jar content, stay visible.
|
|
45
|
+
- `check-symbol-exists` no longer reports false negatives for fields and classes on unobfuscated versions: the runtime-bytecode fallback introduced in 6.0.0 only re-checked `not_found` verdicts for methods, so a mapping graph lacking a record for a real field like `EntityType.ITEM` returned `not_found` without consulting the jar. Every `not_found` verdict is now runtime-checked (genuinely-missing symbols stay `not_found`), and bytecode-derived response contexts report `mappingNamespace: "mojang"` on unobfuscated versions instead of a hardcoded `"obfuscated"`.
|
|
46
|
+
- Dependency version detection probes the snake_case transforms of hyphenated artifact names (`fabric_api_version` and the compound `fabric_api_fabric_api_version` for `net.fabricmc.fabric-api:fabric-api`). Previously only submodules received a snake_case umbrella fallback key, so resolving the umbrella artifact itself against a standard Fabric template that declares `fabric_api_version` failed with `ERR_DEPENDENCY_VERSION_UNRESOLVED`.
|
|
47
|
+
- Umbrella submodule dependency targets (e.g. `net.fabricmc.fabric-api:fabric-gametest-api-v1`) no longer copy the umbrella's version into the submodule coordinate, which synthesized nonexistent coordinates like `fabric-gametest-api-v1:0.153.0+26.2` and failed downstream. Resolution now adopts the single cached modules-2 version, or — when several are cached — the version named by the cached umbrella POM's `<dependency>` entry (recorded as `provenance.submoduleVersionSource: "umbrella-pom"`). Anything still ambiguous fails closed with `ERR_DEPENDENCY_VERSION_UNRESOLVED` and `candidatesSeen` instead of guessing.
|
|
9
48
|
|
|
10
49
|
## [6.1.1] - 2026-07-04
|
|
11
50
|
|
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ All six return `result.summary` first and can include `summary.nextActions` when
|
|
|
147
147
|
| `inspect-minecraft` | versions, artifacts, classes, files, and source search |
|
|
148
148
|
| `analyze-symbol` | symbol existence checks, mapping conversion, lifecycle tracing, and workspace symbol resolution |
|
|
149
149
|
| `compare-minecraft` | version-pair diffs, class diffs, registry diffs, and migration-oriented overviews |
|
|
150
|
-
| `analyze-mod` | mod metadata, decompile/search flows, class source, and safe remap preview/apply |
|
|
150
|
+
| `analyze-mod` | mod metadata, decompile/search flows, class source, bytecode member queries, and safe remap preview/apply |
|
|
151
151
|
| `validate-project` | workspace summaries plus direct Mixin, Access Widener, and Access Transformer validation |
|
|
152
152
|
| `manage-cache` | cache inventory, verification, and preview/apply cleanup workflows |
|
|
153
153
|
|
|
@@ -156,13 +156,15 @@ All six return `result.summary` first and can include `summary.nextActions` when
|
|
|
156
156
|
These notes cover high-frequency decisions during onboarding. For the full pitfall list, exact contracts, migration notes, and environment variables, see [docs/tool-reference.md](docs/tool-reference.md).
|
|
157
157
|
|
|
158
158
|
- `search-class-source` defaults to `queryMode="auto"` and keeps separator queries such as `foo.bar`, `foo_bar`, and `foo$bar` on the indexed path. Use `queryMode="literal"` for an explicit full substring scan.
|
|
159
|
-
- If you do not already have an artifact, prefer `subject.kind="workspace"` for `inspect-minecraft` instead of guessing artifact details.
|
|
159
|
+
- If you do not already have an artifact, prefer `subject.kind="workspace"` for `inspect-minecraft` instead of guessing artifact details. `subject.focus` is an object, not a string: use `{ "kind": "class", "className": "..." }`, `{ "kind": "search", "query": "..." }`, or `{ "kind": "file", "filePath": "..." }`. `task="auto"` dispatches only from `subject.kind` and `focus.kind`; it is not a natural-language planner. Invalid string focus returns class/search/file `exampleCalls`, while missing artifact context keeps the requested task in a retryable `suggestedCall`.
|
|
160
160
|
- `trace-symbol-lifecycle` expects `Class.method` in `symbol`. Keep exact overload matching in the separate `descriptor` field.
|
|
161
161
|
- For unobfuscated releases such as `26.1+`, `check-symbol-exists` and `analyze-symbol task="exists"` validate `mojang` lookups against runtime bytecode when no mapping graph exists, and return `mapping_unavailable` when the runtime JAR itself is unreachable.
|
|
162
162
|
- `analyze-mod` and `validate-project` require structured `subject` objects and canonical `include` groups; stale string-subject or domain-include payloads return `ERR_INVALID_INPUT` with a retryable `suggestedCall`.
|
|
163
163
|
- `validate-project task="project-summary"` propagates `preferProjectVersion=true` across discovered Mixin, Access Widener, and Access Transformer checks. If no version can be resolved from the request or `gradle.properties`, the summary returns recovery guidance instead of guessing.
|
|
164
164
|
- `validate-mixin` and `validate-project` keep `mapping-health` lightweight for `obfuscated` and `mojang` validation, avoiding full Tiny mapping graph loads unless `intermediary` or `yarn` namespaces are requested.
|
|
165
165
|
- `validate-project task="project-summary"` uses a lightweight artifact probe for `tasks["minecraft.artifact.resolved"]`; it does not decompile Minecraft or rebuild the source index just to report per-probe status. Set `VALIDATE_PROJECT_TASKS_OFF=1` to omit the additive `tasks` field.
|
|
166
|
+
- `validate-project` has a supervisor-owned end-to-end deadline of 120 seconds, including queue time. Set `MCP_VALIDATE_PROJECT_TIMEOUT_MS` to an ASCII-decimal value from `10000` through `600000` to override it. A timeout returns `ERR_TOOL_TIMEOUT`; a running timeout restarts the isolated worker before queued calls resume, while a queue timeout leaves the current worker untouched.
|
|
167
|
+
- Queued calls resume only after the replacement worker completes initialization replay. If replacement startup or replay fails, queued tool calls terminate with `ERR_WORKER_RESTART` instead of waiting indefinitely. If unresolved process-tree cleanup fills the supervisor's two live-generation slots, new requests fail with the existing restart envelope and unavailable notifications are warning-dropped until cleanup or reconnect. On POSIX, an already-gone process group counts as cleaned up rather than leaving a stale cleanup token.
|
|
166
168
|
- If a workspace was built with `GRADLE_USER_HOME=/tmp/...` or another isolated Gradle home, pass that path as `gradleUserHome` so source, mapping, runtime, and project validation lookups use the same Loom cache instead of stale caches under the MCP process home.
|
|
167
169
|
- `manage-cache` reports corrupt Mojang binary-remap cache directories under `cacheKinds: ["binary-remap"]` with `status: "corrupt"`, and can delete them by `selector.artifactId` in preview/apply workflows.
|
|
168
170
|
|
|
@@ -262,7 +264,7 @@ Start with these top-level workflow tools unless you already know the exact spec
|
|
|
262
264
|
| `inspect-minecraft` | Inspect versions, artifacts, classes, files, source text, and workspace-aware lookup flows |
|
|
263
265
|
| `analyze-symbol` | Handle symbol existence checks, namespace mapping, lifecycle tracing, workspace symbol resolution, and API overviews |
|
|
264
266
|
| `compare-minecraft` | Compare version pairs, class diffs, registry diffs, and migration-oriented summaries |
|
|
265
|
-
| `analyze-mod` | Summarize mod metadata, decompile and search mod code, inspect class source, and preview or apply remaps |
|
|
267
|
+
| `analyze-mod` | Summarize mod metadata, decompile and search mod code, inspect class source, read class members from bytecode, and preview or apply remaps |
|
|
266
268
|
| `validate-project` | Summarize workspaces and run direct Mixin, Access Widener, or Access Transformer validation |
|
|
267
269
|
| `manage-cache` | List, verify, and preview or apply cache cleanup and rebuild operations |
|
|
268
270
|
<!-- END GENERATED TOOL TABLE: top-level-workflow-tools -->
|
|
@@ -276,7 +278,7 @@ Tools for browsing Minecraft versions, resolving source artifacts, and reading o
|
|
|
276
278
|
| --- | --- |
|
|
277
279
|
| `list-versions` | List available Minecraft versions from Mojang metadata and local cache |
|
|
278
280
|
| `resolve-artifact` | Resolve source artifacts from versions, JAR paths, or Maven coordinates |
|
|
279
|
-
| `find-class` | Find simple or fully-qualified class names
|
|
281
|
+
| `find-class` | Find simple or fully-qualified class names, including classes bundled in nested JARs |
|
|
280
282
|
| `get-class-source` | Read class source from an artifact or resolve the backing artifact on demand |
|
|
281
283
|
| `get-class-members` | List constructors, fields, and methods from bytecode |
|
|
282
284
|
| `search-class-source` | Search indexed class source by symbol, text, or path |
|
|
@@ -285,6 +287,8 @@ Tools for browsing Minecraft versions, resolving source artifacts, and reading o
|
|
|
285
287
|
| `index-artifact` | Rebuild indexed metadata for an existing artifact |
|
|
286
288
|
<!-- END GENERATED TOOL TABLE: source-exploration -->
|
|
287
289
|
|
|
290
|
+
`find-class` accepts either an `artifactId` or the shared object `target` shape. For a workspace-relative dependency, pass `target: { kind: "dependency", group, name, versionFromProject: true }` with top-level `projectPath`. Fabric-style umbrella JARs are searched through their nested `.class` inventories; a top-level class match can then be passed to `get-class-source` or `get-class-members`, which resolve the containing nested JAR. Dotted inner-class matches are also readable through `get-class-source`. An empty result still means that the requested class name is absent from the resolved dependency version.
|
|
291
|
+
|
|
288
292
|
For unobfuscated releases such as `26.1+`, `mapping="mojang"` uses the runtime/decompile path directly and skips Loom source-jar discovery, while `intermediary` and `yarn` fall back to `obfuscated` with a warning.
|
|
289
293
|
|
|
290
294
|
### Version Comparison & Symbol Tracking
|
|
@@ -413,12 +417,16 @@ pnpm test
|
|
|
413
417
|
|
|
414
418
|
Run these when relevant:
|
|
415
419
|
|
|
420
|
+
- `pnpm test:file <path...>` for focused ordinary suites
|
|
421
|
+
- `pnpm test:grep <name-pattern>` for recursive ordinary suites filtered by test name
|
|
416
422
|
- `pnpm test:manual:stdio-smoke` for MCP transport, registration, or manual workflow changes
|
|
417
423
|
- `pnpm test:manual:package-smoke` when checking packaged install and distribution behavior
|
|
418
424
|
- `pnpm test:perf` for search, index, or performance-sensitive changes
|
|
419
425
|
- `pnpm test:coverage` or `pnpm test:coverage:lcov` for coverage checks (`lines=80`, `branches=70`, `functions=80`)
|
|
420
426
|
- `pnpm validate` for the full local validation suite
|
|
421
427
|
|
|
428
|
+
Ordinary `.test.ts` files live in domain directories under `tests/` (for example `source-service/`, `entry-tools/`, `mapping/`, `mixin/`, `integration/mcp-tools/`, `contracts/`, and `utils/`). Keep helper-only modules in `tests/helpers`; manual smoke, performance, resource, and smoke-only files remain in their existing special directories.
|
|
429
|
+
|
|
422
430
|
## License
|
|
423
431
|
|
|
424
432
|
[MIT](LICENSE)
|
package/dist/cache-registry.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type PathRuntimeInfo } from "./path-converter.js";
|
|
2
2
|
import { type WorkspaceContextCache } from "./workspace-context-cache.js";
|
|
3
|
-
export declare const PUBLIC_CACHE_KINDS: readonly ["artifact-index", "downloads", "mapping", "registry", "decompiled-source", "mod-remap", "binary-remap", "workspace"];
|
|
3
|
+
export declare const PUBLIC_CACHE_KINDS: readonly ["artifact-index", "downloads", "mapping", "registry", "decompiled-source", "mod-remap", "binary-remap", "nested-jar", "workspace"];
|
|
4
4
|
export type PublicCacheKind = (typeof PUBLIC_CACHE_KINDS)[number];
|
|
5
5
|
export declare const CACHE_HEALTH_STATES: readonly ["healthy", "partial", "stale", "orphaned", "corrupt", "in_use"];
|
|
6
6
|
export type CacheHealthState = (typeof CACHE_HEALTH_STATES)[number];
|
package/dist/cache-registry.js
CHANGED
|
@@ -14,6 +14,7 @@ export const PUBLIC_CACHE_KINDS = [
|
|
|
14
14
|
"decompiled-source",
|
|
15
15
|
"mod-remap",
|
|
16
16
|
"binary-remap",
|
|
17
|
+
"nested-jar",
|
|
17
18
|
"workspace"
|
|
18
19
|
];
|
|
19
20
|
export const CACHE_HEALTH_STATES = [
|
|
@@ -44,6 +45,8 @@ function kindRoot(config, cacheKind) {
|
|
|
44
45
|
return join(config.cacheDir, "remapped-mods");
|
|
45
46
|
case "binary-remap":
|
|
46
47
|
return join(config.cacheDir, "remapped");
|
|
48
|
+
case "nested-jar":
|
|
49
|
+
return join(config.cacheDir, "nested-jars");
|
|
47
50
|
case "workspace":
|
|
48
51
|
return "<in-memory:workspace-context-cache>";
|
|
49
52
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnalyzeModOptions, ModAnalysisResult } from "../mod-analyzer.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
export declare const analyzeModShape: {
|
|
4
|
-
task: z.ZodEnum<["summary", "decompile", "search", "class-source", "remap"]>;
|
|
4
|
+
task: z.ZodEnum<["summary", "decompile", "search", "class-source", "members", "remap"]>;
|
|
5
5
|
subject: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
6
6
|
kind: z.ZodLiteral<"jar">;
|
|
7
7
|
jarPath: z.ZodString;
|
|
@@ -38,7 +38,7 @@ export declare const analyzeModShape: {
|
|
|
38
38
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
|
|
39
39
|
};
|
|
40
40
|
export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
41
|
-
task: z.ZodEnum<["summary", "decompile", "search", "class-source", "remap"]>;
|
|
41
|
+
task: z.ZodEnum<["summary", "decompile", "search", "class-source", "members", "remap"]>;
|
|
42
42
|
subject: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
43
43
|
kind: z.ZodLiteral<"jar">;
|
|
44
44
|
jarPath: z.ZodString;
|
|
@@ -75,7 +75,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
75
75
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
|
|
76
76
|
}, "strip", z.ZodTypeAny, {
|
|
77
77
|
limit: number;
|
|
78
|
-
task: "search" | "summary" | "class-source" | "remap" | "decompile";
|
|
78
|
+
task: "search" | "summary" | "class-source" | "members" | "remap" | "decompile";
|
|
79
79
|
subject: {
|
|
80
80
|
kind: "jar";
|
|
81
81
|
jarPath: string;
|
|
@@ -96,7 +96,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
96
96
|
outputJar?: string | undefined;
|
|
97
97
|
maxFiles?: number | undefined;
|
|
98
98
|
}, {
|
|
99
|
-
task: "search" | "summary" | "class-source" | "remap" | "decompile";
|
|
99
|
+
task: "search" | "summary" | "class-source" | "members" | "remap" | "decompile";
|
|
100
100
|
subject: {
|
|
101
101
|
kind: "jar";
|
|
102
102
|
jarPath: string;
|
|
@@ -119,7 +119,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
119
119
|
maxFiles?: number | undefined;
|
|
120
120
|
}>, {
|
|
121
121
|
limit: number;
|
|
122
|
-
task: "search" | "summary" | "class-source" | "remap" | "decompile";
|
|
122
|
+
task: "search" | "summary" | "class-source" | "members" | "remap" | "decompile";
|
|
123
123
|
subject: {
|
|
124
124
|
kind: "jar";
|
|
125
125
|
jarPath: string;
|
|
@@ -140,7 +140,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
140
140
|
outputJar?: string | undefined;
|
|
141
141
|
maxFiles?: number | undefined;
|
|
142
142
|
}, {
|
|
143
|
-
task: "search" | "summary" | "class-source" | "remap" | "decompile";
|
|
143
|
+
task: "search" | "summary" | "class-source" | "members" | "remap" | "decompile";
|
|
144
144
|
subject: {
|
|
145
145
|
kind: "jar";
|
|
146
146
|
jarPath: string;
|
|
@@ -196,6 +196,12 @@ type AnalyzeModDeps = {
|
|
|
196
196
|
}) => Promise<Record<string, unknown> & {
|
|
197
197
|
warnings?: string[];
|
|
198
198
|
}>;
|
|
199
|
+
getModClassMembers: (input: {
|
|
200
|
+
jarPath: string;
|
|
201
|
+
className: string;
|
|
202
|
+
}) => Promise<Record<string, unknown> & {
|
|
203
|
+
warnings?: string[];
|
|
204
|
+
}>;
|
|
199
205
|
};
|
|
200
206
|
export declare class AnalyzeModService {
|
|
201
207
|
private readonly deps;
|
|
@@ -20,7 +20,7 @@ const subjectSchema = z.discriminatedUnion("kind", [
|
|
|
20
20
|
})
|
|
21
21
|
]);
|
|
22
22
|
export const analyzeModShape = {
|
|
23
|
-
task: z.enum(["summary", "decompile", "search", "class-source", "remap"]),
|
|
23
|
+
task: z.enum(["summary", "decompile", "search", "class-source", "members", "remap"]),
|
|
24
24
|
subject: subjectSchema,
|
|
25
25
|
query: nonEmptyString.optional(),
|
|
26
26
|
searchType: z.enum(["class", "method", "field", "content", "all"]).default("all"),
|
|
@@ -43,11 +43,11 @@ export const analyzeModSchema = z.object(analyzeModShape).superRefine((value, ct
|
|
|
43
43
|
message: `${value.task} requires subject.kind=jar.`
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
if (value.task === "class-source" && value.subject.kind !== "class") {
|
|
46
|
+
if ((value.task === "class-source" || value.task === "members") && value.subject.kind !== "class") {
|
|
47
47
|
ctx.addIssue({
|
|
48
48
|
code: z.ZodIssueCode.custom,
|
|
49
49
|
path: ["subject", "kind"],
|
|
50
|
-
message:
|
|
50
|
+
message: `${value.task} requires subject.kind=class.`
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
if (value.task === "search" && !value.query) {
|
|
@@ -206,6 +206,40 @@ export class AnalyzeModService {
|
|
|
206
206
|
warnings: Array.isArray(output.warnings) ? output.warnings : []
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
+
case "members": {
|
|
210
|
+
if (input.subject.kind !== "class") {
|
|
211
|
+
throw createError({
|
|
212
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
213
|
+
message: "members requires subject.kind=class."
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const output = await this.deps.getModClassMembers({
|
|
217
|
+
jarPath: input.subject.jarPath,
|
|
218
|
+
className: input.subject.className
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
...buildEntryToolResult({
|
|
222
|
+
task: "members",
|
|
223
|
+
detail,
|
|
224
|
+
include,
|
|
225
|
+
summary: {
|
|
226
|
+
status: "ok",
|
|
227
|
+
headline: `Read bytecode members of ${input.subject.className}.`,
|
|
228
|
+
subject: createSummarySubject({
|
|
229
|
+
task: "members",
|
|
230
|
+
kind: input.subject.kind,
|
|
231
|
+
jarPath: input.subject.jarPath,
|
|
232
|
+
className: input.subject.className
|
|
233
|
+
})
|
|
234
|
+
},
|
|
235
|
+
blocks: {
|
|
236
|
+
members: output
|
|
237
|
+
},
|
|
238
|
+
alwaysBlocks: ["members"]
|
|
239
|
+
}),
|
|
240
|
+
warnings: Array.isArray(output.warnings) ? output.warnings : []
|
|
241
|
+
};
|
|
242
|
+
}
|
|
209
243
|
case "remap": {
|
|
210
244
|
const normalizedInputJar = normalizePathForHost(input.subject.jarPath, undefined, "jarPath");
|
|
211
245
|
const analysis = await this.deps.analyzeModJar(normalizedInputJar);
|
|
@@ -90,10 +90,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
90
90
|
detail?: "summary" | "standard" | "full" | undefined;
|
|
91
91
|
include?: string[] | undefined;
|
|
92
92
|
targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
93
|
+
gradleUserHome?: string | undefined;
|
|
93
94
|
maxVersions?: number | undefined;
|
|
94
95
|
sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
95
96
|
classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
96
|
-
gradleUserHome?: string | undefined;
|
|
97
97
|
includeSnapshots?: boolean | undefined;
|
|
98
98
|
fromVersion?: string | undefined;
|
|
99
99
|
toVersion?: string | undefined;
|
|
@@ -114,10 +114,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
114
114
|
detail?: "summary" | "standard" | "full" | undefined;
|
|
115
115
|
include?: string[] | undefined;
|
|
116
116
|
targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
117
|
+
gradleUserHome?: string | undefined;
|
|
117
118
|
maxVersions?: number | undefined;
|
|
118
119
|
sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
119
120
|
classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
120
|
-
gradleUserHome?: string | undefined;
|
|
121
121
|
nameMode?: "auto" | "fqcn" | undefined;
|
|
122
122
|
includeSnapshots?: boolean | undefined;
|
|
123
123
|
fromVersion?: string | undefined;
|
|
@@ -142,10 +142,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
142
142
|
detail?: "summary" | "standard" | "full" | undefined;
|
|
143
143
|
include?: string[] | undefined;
|
|
144
144
|
targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
145
|
+
gradleUserHome?: string | undefined;
|
|
145
146
|
maxVersions?: number | undefined;
|
|
146
147
|
sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
147
148
|
classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
148
|
-
gradleUserHome?: string | undefined;
|
|
149
149
|
includeSnapshots?: boolean | undefined;
|
|
150
150
|
fromVersion?: string | undefined;
|
|
151
151
|
toVersion?: string | undefined;
|
|
@@ -166,10 +166,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
166
166
|
detail?: "summary" | "standard" | "full" | undefined;
|
|
167
167
|
include?: string[] | undefined;
|
|
168
168
|
targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
169
|
+
gradleUserHome?: string | undefined;
|
|
169
170
|
maxVersions?: number | undefined;
|
|
170
171
|
sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
171
172
|
classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
|
|
172
|
-
gradleUserHome?: string | undefined;
|
|
173
173
|
nameMode?: "auto" | "fqcn" | undefined;
|
|
174
174
|
includeSnapshots?: boolean | undefined;
|
|
175
175
|
fromVersion?: string | undefined;
|
|
@@ -180,6 +180,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
180
180
|
}>;
|
|
181
181
|
export type AnalyzeSymbolInput = z.infer<typeof analyzeSymbolSchema>;
|
|
182
182
|
type AnalyzeSymbolDeps = {
|
|
183
|
+
detectProjectMinecraftVersion: (projectPath: string) => Promise<string | undefined>;
|
|
183
184
|
checkSymbolExists: (input: {
|
|
184
185
|
version: string;
|
|
185
186
|
kind: "class" | "field" | "method";
|
|
@@ -254,5 +255,6 @@ export declare class AnalyzeSymbolService {
|
|
|
254
255
|
execute(input: AnalyzeSymbolInput): Promise<Record<string, unknown> & {
|
|
255
256
|
warnings?: string[];
|
|
256
257
|
}>;
|
|
258
|
+
private dispatch;
|
|
257
259
|
}
|
|
258
260
|
export {};
|
|
@@ -112,11 +112,11 @@ const LIFECYCLE_ONLY_FIELDS = [
|
|
|
112
112
|
"includeSnapshots"
|
|
113
113
|
];
|
|
114
114
|
export const analyzeSymbolSchema = z.object(analyzeSymbolShape).superRefine((value, ctx) => {
|
|
115
|
-
if (value.task !== "lifecycle" && !value.version) {
|
|
115
|
+
if (value.task !== "lifecycle" && !value.version && !value.projectPath) {
|
|
116
116
|
ctx.addIssue({
|
|
117
117
|
code: z.ZodIssueCode.custom,
|
|
118
118
|
path: ["version"],
|
|
119
|
-
message: "version is required for non-lifecycle tasks."
|
|
119
|
+
message: "version is required for non-lifecycle tasks (or pass projectPath so the version can be inferred from the workspace)."
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
if (value.task === "lifecycle" && !value.version && !value.toVersion) {
|
|
@@ -189,6 +189,41 @@ export class AnalyzeSymbolService {
|
|
|
189
189
|
const detail = resolveDetail(input.detail);
|
|
190
190
|
const include = resolveInclude(input.include);
|
|
191
191
|
const { kind: subjectKind, warning: inferenceWarning } = inferSubjectKind(input.subject);
|
|
192
|
+
// Omitted version with a supplied projectPath: infer from the workspace.
|
|
193
|
+
// Inference always carries provenance (versionInference block + warning);
|
|
194
|
+
// an explicit version is never overridden.
|
|
195
|
+
let versionInference;
|
|
196
|
+
if (input.task !== "lifecycle" && !input.version && input.projectPath) {
|
|
197
|
+
const detected = await this.deps.detectProjectMinecraftVersion(input.projectPath);
|
|
198
|
+
if (!detected) {
|
|
199
|
+
throw createError({
|
|
200
|
+
code: ERROR_CODES.WORKSPACE_VERSION_UNRESOLVED,
|
|
201
|
+
message: `Could not infer a Minecraft version from ${input.projectPath}.`,
|
|
202
|
+
details: {
|
|
203
|
+
projectPath: input.projectPath,
|
|
204
|
+
nextAction: "Declare minecraft_version (or mc_version) in gradle.properties, or pass version explicitly."
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
versionInference = {
|
|
209
|
+
version: detected,
|
|
210
|
+
source: `projectPath:gradle.properties (${input.projectPath})`
|
|
211
|
+
};
|
|
212
|
+
input = { ...input, version: detected };
|
|
213
|
+
}
|
|
214
|
+
const inferenceWarnings = versionInference
|
|
215
|
+
? [
|
|
216
|
+
`version was inferred from the workspace: ${versionInference.version} (source: ${versionInference.source}).`
|
|
217
|
+
]
|
|
218
|
+
: [];
|
|
219
|
+
const result = await this.dispatch(input, detail, include, subjectKind, inferenceWarning);
|
|
220
|
+
if (versionInference) {
|
|
221
|
+
result.versionInference = versionInference;
|
|
222
|
+
result.warnings = [...(result.warnings ?? []), ...inferenceWarnings];
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
async dispatch(input, detail, include, subjectKind, inferenceWarning) {
|
|
192
227
|
switch (input.task) {
|
|
193
228
|
case "exists": {
|
|
194
229
|
const output = await this.deps.checkSymbolExists({
|
|
@@ -401,6 +401,7 @@ export declare const subjectSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject
|
|
|
401
401
|
scope?: "vanilla" | "merged" | "loader" | undefined;
|
|
402
402
|
preferProjectVersion?: boolean | undefined;
|
|
403
403
|
strictVersion?: boolean | undefined;
|
|
404
|
+
gradleUserHome?: string | undefined;
|
|
404
405
|
artifact?: {
|
|
405
406
|
artifactId: string;
|
|
406
407
|
type: "resolved-id";
|
|
@@ -411,7 +412,6 @@ export declare const subjectSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject
|
|
|
411
412
|
};
|
|
412
413
|
type: "resolve-target";
|
|
413
414
|
} | undefined;
|
|
414
|
-
gradleUserHome?: string | undefined;
|
|
415
415
|
}, {
|
|
416
416
|
kind: "class";
|
|
417
417
|
className: string;
|
|
@@ -420,6 +420,7 @@ export declare const subjectSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject
|
|
|
420
420
|
scope?: "vanilla" | "merged" | "loader" | undefined;
|
|
421
421
|
preferProjectVersion?: boolean | undefined;
|
|
422
422
|
strictVersion?: boolean | undefined;
|
|
423
|
+
gradleUserHome?: string | undefined;
|
|
423
424
|
artifact?: {
|
|
424
425
|
artifactId: string;
|
|
425
426
|
type: "resolved-id";
|
|
@@ -430,7 +431,6 @@ export declare const subjectSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject
|
|
|
430
431
|
};
|
|
431
432
|
type: "resolve-target";
|
|
432
433
|
} | undefined;
|
|
433
|
-
gradleUserHome?: string | undefined;
|
|
434
434
|
}>, z.ZodObject<{
|
|
435
435
|
kind: z.ZodLiteral<"file">;
|
|
436
436
|
filePath: z.ZodString;
|
|
@@ -1015,6 +1015,10 @@ export type InspectMinecraftDeps = {
|
|
|
1015
1015
|
cursor?: string;
|
|
1016
1016
|
}) => Promise<ListArtifactFilesOutput>;
|
|
1017
1017
|
detectProjectMinecraftVersion: (projectPath: string) => Promise<string | undefined>;
|
|
1018
|
+
listWorkspaceContexts: () => Array<{
|
|
1019
|
+
projectPath: string;
|
|
1020
|
+
minecraftVersion?: string;
|
|
1021
|
+
}>;
|
|
1018
1022
|
};
|
|
1019
1023
|
export declare function requireWorkspaceClassFocus(subject: Subject): WorkspaceClassFocus;
|
|
1020
1024
|
export declare function requireWorkspaceSearchFocus(subject: Subject): WorkspaceSearchFocus;
|
|
@@ -1040,7 +1044,7 @@ export declare function resolveWorkspaceArtifactReference(deps: InspectMinecraft
|
|
|
1040
1044
|
version?: string;
|
|
1041
1045
|
warnings: string[];
|
|
1042
1046
|
}>;
|
|
1043
|
-
export declare function resolveTask(task: InspectMinecraftTask | undefined, subject: Subject | undefined): "search" | "class-source" | "
|
|
1047
|
+
export declare function resolveTask(task: InspectMinecraftTask | undefined, subject: Subject | undefined): "search" | "class-source" | "file" | "class-overview" | "class-members" | "artifact" | "versions" | "list-files";
|
|
1044
1048
|
export declare function summarizeRequestedSubject(subject: Subject): Record<string, unknown>;
|
|
1045
1049
|
export declare function taskForSubject(subject: Subject): ConcreteInspectMinecraftTask;
|
|
1046
1050
|
export declare function invalidTaskSubjectError(task: ConcreteInspectMinecraftTask, subject: Subject): never;
|
|
@@ -30,27 +30,27 @@ export const artifactRefSchema = z.discriminatedUnion("type", [
|
|
|
30
30
|
]);
|
|
31
31
|
export const workspaceFocusSchema = z.discriminatedUnion("kind", [
|
|
32
32
|
z.object({
|
|
33
|
-
kind: z.literal("class"),
|
|
34
|
-
className: nonEmptyString,
|
|
35
|
-
artifact: artifactRefSchema.optional()
|
|
36
|
-
}),
|
|
33
|
+
kind: z.literal("class").describe("Select class inspection."),
|
|
34
|
+
className: nonEmptyString.describe("Fully-qualified class name to inspect."),
|
|
35
|
+
artifact: artifactRefSchema.optional().describe("Optional explicit artifact override; otherwise resolve it from the workspace.")
|
|
36
|
+
}).describe('Class focus object: {"kind":"class","className":"net.minecraft.world.item.Item"}. task=auto dispatches it to class-overview; select class-source or class-members explicitly when needed.'),
|
|
37
37
|
z.object({
|
|
38
|
-
kind: z.literal("file"),
|
|
39
|
-
filePath: nonEmptyString,
|
|
40
|
-
artifact: artifactRefSchema.optional()
|
|
41
|
-
}),
|
|
38
|
+
kind: z.literal("file").describe("Select an artifact-relative file read."),
|
|
39
|
+
filePath: nonEmptyString.describe("Artifact-relative file path to read."),
|
|
40
|
+
artifact: artifactRefSchema.optional().describe("Optional explicit artifact override; otherwise resolve it from the workspace.")
|
|
41
|
+
}).describe('File focus object: {"kind":"file","filePath":"net/minecraft/world/item/Item.java"}. task=auto dispatches it to file.'),
|
|
42
42
|
z.object({
|
|
43
|
-
kind: z.literal("search"),
|
|
44
|
-
query: nonEmptyString,
|
|
45
|
-
artifact: artifactRefSchema.optional(),
|
|
43
|
+
kind: z.literal("search").describe("Select source search."),
|
|
44
|
+
query: nonEmptyString.describe("Source, symbol, or path query to search for."),
|
|
45
|
+
artifact: artifactRefSchema.optional().describe("Optional explicit artifact override; otherwise resolve it from the workspace."),
|
|
46
46
|
intent: z.enum(["symbol", "text", "path"]).optional(),
|
|
47
47
|
match: z.enum(["exact", "prefix", "contains", "regex"]).optional(),
|
|
48
48
|
symbolKind: z.enum(["class", "interface", "enum", "record", "method", "field"]).optional(),
|
|
49
49
|
packagePrefix: nonEmptyString.optional(),
|
|
50
50
|
fileGlob: nonEmptyString.optional(),
|
|
51
51
|
queryMode: z.enum(["auto", "token", "literal"]).default("auto")
|
|
52
|
-
})
|
|
53
|
-
]);
|
|
52
|
+
}).describe('Search focus object: {"kind":"search","query":"CreativeModeTab"}. task=auto dispatches it to search.')
|
|
53
|
+
]).describe('Structured workspace focus. Object, not string. Choose {kind:"class",className}, {kind:"file",filePath}, or {kind:"search",query}.');
|
|
54
54
|
export const subjectSchema = z.discriminatedUnion("kind", [
|
|
55
55
|
z.object({
|
|
56
56
|
kind: z.literal("version"),
|
|
@@ -108,8 +108,8 @@ export const subjectSchema = z.discriminatedUnion("kind", [
|
|
|
108
108
|
preferProjectVersion: z.boolean().optional(),
|
|
109
109
|
strictVersion: z.boolean().optional(),
|
|
110
110
|
focus: workspaceFocusSchema.optional()
|
|
111
|
-
})
|
|
112
|
-
]);
|
|
111
|
+
}).describe("Workspace subject. Resolves Minecraft artifact context from projectPath; add a structured focus object for class, file, or search work.")
|
|
112
|
+
]).describe("Structured inspection subject. Use kind=workspace with a structured focus object when the workspace should resolve artifact context.");
|
|
113
113
|
export function hasPartialVanillaCoverage(artifact) {
|
|
114
114
|
return artifact?.qualityFlags.includes("partial-source-no-net-minecraft") === true
|
|
115
115
|
|| artifact?.artifactContents.sourceCoverage === "partial";
|
|
@@ -392,6 +392,31 @@ export async function resolveArtifactReference(deps, subject, task) {
|
|
|
392
392
|
}
|
|
393
393
|
if (subject.kind === "class" || subject.kind === "file" || subject.kind === "search") {
|
|
394
394
|
if (!subject.artifact) {
|
|
395
|
+
// Guardrailed auto-resolution: an omitted subject.artifact resolves
|
|
396
|
+
// through the workspace ONLY when exactly one workspace is known, and
|
|
397
|
+
// the response always carries a provenance warning. An explicit
|
|
398
|
+
// subject.artifact is never overridden (this branch requires absence).
|
|
399
|
+
const workspaces = deps.listWorkspaceContexts();
|
|
400
|
+
const unique = workspaces.length === 1 ? workspaces[0] : undefined;
|
|
401
|
+
if (unique) {
|
|
402
|
+
const version = unique.minecraftVersion ??
|
|
403
|
+
(await deps.detectProjectMinecraftVersion(unique.projectPath));
|
|
404
|
+
if (version) {
|
|
405
|
+
const artifact = await deps.resolveArtifact({
|
|
406
|
+
target: { kind: "version", value: version },
|
|
407
|
+
projectPath: unique.projectPath
|
|
408
|
+
});
|
|
409
|
+
return {
|
|
410
|
+
artifactId: artifact.artifactId,
|
|
411
|
+
artifact,
|
|
412
|
+
version,
|
|
413
|
+
warnings: [
|
|
414
|
+
`subject.artifact was omitted; auto-resolved through the unique known workspace ${unique.projectPath} (Minecraft ${version}). Pass subject.artifact to target a different artifact.`,
|
|
415
|
+
...artifact.warnings
|
|
416
|
+
]
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
395
420
|
const suggestedTask = task
|
|
396
421
|
?? (subject.kind === "class"
|
|
397
422
|
? "class-overview"
|
|
@@ -403,6 +428,9 @@ export async function resolveArtifactReference(deps, subject, task) {
|
|
|
403
428
|
message: `${subject.kind} subject requires artifact context.`,
|
|
404
429
|
details: {
|
|
405
430
|
nextAction: "Add subject.artifact or use subject.kind=workspace so inspect-minecraft can resolve the artifact first.",
|
|
431
|
+
...(workspaces.length > 1
|
|
432
|
+
? { workspaceCandidates: workspaces.map((workspace) => workspace.projectPath) }
|
|
433
|
+
: {}),
|
|
406
434
|
...(await buildArtifactContextSuggestedCall(deps, suggestedTask, subject))
|
|
407
435
|
}
|
|
408
436
|
});
|