@adhisang/minecraft-modding-mcp 7.0.0 → 7.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.
- package/CHANGELOG.md +30 -0
- package/README.md +3 -2
- package/dist/entry-tools/analyze-mod-service.d.ts +2 -2
- package/dist/entry-tools/analyze-symbol-service.d.ts +2 -2
- package/dist/entry-tools/batch-class-members-service.d.ts +3 -2
- package/dist/entry-tools/batch-class-members-service.js +20 -6
- package/dist/entry-tools/batch-class-source-service.d.ts +3 -2
- package/dist/entry-tools/batch-class-source-service.js +10 -0
- package/dist/entry-tools/compare-minecraft-service.d.ts +27 -4
- package/dist/entry-tools/compare-minecraft-service.js +65 -4
- package/dist/entry-tools/entry-tool-schema.d.ts +2 -2
- package/dist/entry-tools/inspect-minecraft-service.d.ts +2 -2
- package/dist/entry-tools/manage-cache-service.d.ts +2 -2
- package/dist/entry-tools/validate-project/cases/project-summary.js +71 -12
- package/dist/entry-tools/validate-project-service.d.ts +2 -2
- package/dist/index.js +37 -15
- package/dist/source/artifact-resolver.d.ts +14 -0
- package/dist/source/artifact-resolver.js +106 -12
- package/dist/source/class-source/members-builder.d.ts +7 -0
- package/dist/source/class-source/members-builder.js +4 -1
- package/dist/source/class-source.d.ts +9 -2
- package/dist/source/class-source.js +173 -25
- package/dist/source/indexer.js +69 -1
- package/dist/source/lifecycle/mapping-helpers.d.ts +20 -1
- package/dist/source/lifecycle/mapping-helpers.js +29 -3
- package/dist/source/lifecycle/runtime-check.d.ts +25 -0
- package/dist/source/lifecycle/runtime-check.js +68 -39
- package/dist/source/symbol-resolver.js +88 -0
- package/dist/source-jar-reader.d.ts +33 -0
- package/dist/source-jar-reader.js +58 -0
- package/dist/source-resolver.d.ts +7 -0
- package/dist/source-resolver.js +20 -5
- package/dist/source-service.d.ts +5 -0
- package/dist/source-service.js +7 -0
- package/dist/storage/db.d.ts +62 -2
- package/dist/storage/db.js +181 -20
- package/dist/storage/sqlite.d.ts +31 -1
- package/dist/storage/sqlite.js +125 -16
- package/dist/tool-guidance.js +4 -1
- package/dist/tool-schemas.d.ts +64 -52
- package/dist/tool-schemas.js +9 -7
- package/dist/types.d.ts +9 -0
- package/dist/v1-parity-schemas.js +36 -2
- package/dist/version-diff-service.d.ts +23 -0
- package/dist/version-diff-service.js +101 -0
- package/dist/version-service.d.ts +14 -0
- package/dist/version-service.js +45 -3
- package/dist/workspace-mapping-service.d.ts +8 -0
- package/dist/workspace-mapping-service.js +35 -7
- package/docs/README-ja.md +2 -0
- package/docs/tool-reference.md +55 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [7.1.0] - 2026-09-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `batch-class-source` and `batch-class-members` accept `target: { "kind": "artifact", "artifactId": "..." }`, the same target `get-class-source` and `get-class-members` accept, so a batch can reuse an artifact that an earlier call already resolved instead of resolving it again from its jar, version, or coordinate. This target was previously rejected with `ERR_INVALID_INPUT`. With it, `summary.sharedArtifactProvenance` is omitted, and an unknown `artifactId` fails each entry with `ERR_SOURCE_NOT_FOUND` rather than failing the whole batch.
|
|
15
|
+
- `compare-minecraft` with `task="migration-overview"` reports which libraries shipped with Minecraft itself were added or removed between the two versions, such as LWJGL's GLFW binding being replaced by SDL, which class and registry diffs do not show. At `detail: "standard"` or `"full"`, the new `migration.libraries` block lists added and removed libraries as `group:artifact:version` and counts libraries whose version changed; `summary.counts.librariesAdded` and `librariesRemoved` carry the same counts at every detail level. A version bump or a per-platform native jar of a library already present is not reported as an addition or removal. When the version details cannot be fetched, or not within 5 seconds, the block is left out with a warning and the rest of the result is unchanged.
|
|
16
|
+
- `provenance.unobfuscatedRuntime: true` marks an artifact whose runtime ships Mojang names (Minecraft 26.1+): version targets, Minecraft runtime coordinates, and jar targets proven to be a 26.1+ runtime jar. `resolve-artifact`, `get-class-source`, `get-class-members`, and the batch tools' `sharedArtifactProvenance` carry it; `provenance` itself is returned only with `include: ["provenance"]` or a fuller `detail` level. `mappingApplied` still reports the mapping you asked for, so `"obfuscated"` on such an artifact means the names as shipped, which are already Mojang names.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `validate-project` with `task="project-summary"` infers an omitted `version` from the project's `gradle.properties` (`minecraft_version`, `mc_version`, or `minecraftVersion`) and names the inferred version in `warnings`; it previously returned `status: "blocked"`. To keep the previous behavior, pass `preferProjectVersion: false`: a call without `version` then returns `blocked`, and the retry suggestions of an invalid-input error keep that explicit `false`. When files are found but no version can be inferred, the blocked reply asks for an explicit `version` instead of suggesting the same call again.
|
|
21
|
+
|
|
22
|
+
### Performance
|
|
23
|
+
|
|
24
|
+
- The first tool call after the server starts no longer stalls on a full consistency check of the local cache database. The check on open now uses SQLite's `quick_check` instead of `integrity_check`: on a 3.6 GB cache it takes about 2 s instead of about 19 s. A damaged cache file is still detected at startup, backed up and rebuilt. Rarer damage that the lighter check misses can surface later during a tool call; a call that hits it directly now fails with `ERR_DB_FAILURE` and restart guidance instead of `ERR_INTERNAL`, and the next start runs the full `integrity_check`. If the server cannot schedule that check, the error explains how to reset the cache by hand. A call where the damage surfaces inside another step may still report that step's own error.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `validate-project` with `task="project-summary"`: a run that finds no Mixin configs, access wideners, or access transformers now reports `Nothing to validate: …`, naming only the file kinds it searched, with a warning that nothing was validated, instead of "Validated 0 mixin config(s), 0 access widener(s), and 0 access transformer(s)." `status` stays `"ok"`.
|
|
29
|
+
- Minecraft 26.x snapshot, pre-release and release-candidate ids such as `26.3-snapshot-6`, `26.2-pre-6`, `26.2-rc-2` and `26.1.2-rc-1` are recognized as unobfuscated. They were treated as obfuscated legacy versions, so `mapping: "mojang"` was refused on them, `intermediary` and `yarn` did not fall back to `obfuscated`, and symbol checks did not use the runtime jar. Previously only weekly ids such as `26w14a` and unhyphenated forms such as `26.1-rc1`, which Mojang does not publish, were recognized.
|
|
30
|
+
- `mapping: "mojang"` is accepted on a `target.kind: "jar"` that points at a Minecraft 26.1+ runtime jar, such as a jar from the Loom cache; it was refused with `ERR_MAPPING_NOT_APPLIED` and a hint to switch to `obfuscated`. A jar counts as a 26.1+ runtime jar when it has a top-level `version.json` with a 26.1+ `id`, contains `net/minecraft/SharedConstants.class`, and has no `.java` sources. Such a jar reports that `id` as its version and falls back from `intermediary` or `yarn` to `obfuscated` like a version target. A jar indexed by an earlier release has its version recorded on its next resolve, after which calls by `artifactId` accept `mojang` as well.
|
|
31
|
+
- On Minecraft 26.1+ artifacts, class-not-found errors from `get-class-source` and `get-class-members`, and `find-class` misses, lead with the nearest class-name suggestion instead of telling you to retry with `mapping="mojang"`. When `mapping: "mojang"` is refused on a jar that is not a proven runtime jar, the error explains that `mapping: "obfuscated"` reads the names as shipped, and in a 26.1+ project it no longer suggests repeating the refused request. Hints for 1.x versions are unchanged.
|
|
32
|
+
- `resolve-workspace-symbol` and `analyze-symbol` with `task="workspace"` resolve symbols in a Minecraft 26.1+ Loom project that declares no `mappings` line by checking the name against the runtime jar; they returned `mapping_unavailable` (`partial` in `analyze-symbol`). A class that is not in the Minecraft jar returns `not_found`, and a lookup that could not be checked returns `mapping_unavailable` with the reason. A path without a Gradle build script behaves as before.
|
|
33
|
+
- On Minecraft 26.1+, `compare-minecraft` with `task="class-diff"` (and `diff-class-signatures`) and `trace-symbol-lifecycle` with `mapping: "mojang"` no longer mark every member as unmapped with "Could not remap" or "Could not map" warnings.
|
|
34
|
+
- `check-symbol-exists` on Minecraft 26.1+ finds constructors: a method query named `<init>` with an existing descriptor resolves instead of returning `not_found`.
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- The tool reference documents a known issue: on Minecraft 26.1+, responses label the runtime's Mojang names `obfuscated`, for example `mappingApplied: "obfuscated"`, whenever a request omits `mapping` or asks for `obfuscated`. The label stays until a future major release corrects it. Until then, `provenance.unobfuscatedRuntime: true` marks an artifact whose names are Mojang names, and `mapping: "mojang"` returns the same names labelled `mojang`.
|
|
39
|
+
|
|
10
40
|
## [7.0.0] - 2026-09-13
|
|
11
41
|
|
|
12
42
|
### Fixed
|
package/README.md
CHANGED
|
@@ -170,7 +170,8 @@ These notes cover high-frequency decisions during onboarding. For the full pitfa
|
|
|
170
170
|
- `trace-symbol-lifecycle` expects `Class.method` in `symbol`. Keep exact overload matching in the separate `descriptor` field.
|
|
171
171
|
- 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.
|
|
172
172
|
- `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`.
|
|
173
|
-
- `validate-project task="project-summary"`
|
|
173
|
+
- `validate-project task="project-summary"` infers an omitted `version` from `gradle.properties` and names the inferred version in `warnings`; pass `preferProjectVersion: false` to turn inference off, in which case a call without `version` returns `status: "blocked"`. The resolved version is passed to every discovered Mixin, Access Widener, and Access Transformer check. If files were discovered but no version can be inferred, the summary returns `status: "blocked"` with a retry that asks for an explicit `version` instead of guessing.
|
|
174
|
+
- When `validate-project task="project-summary"` discovers no files, `status` stays `"ok"` but the headline reads `Nothing to validate: ...` and `warnings` states that nothing was validated, so an `"ok"` status alone does not mean any file passed.
|
|
174
175
|
- `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.
|
|
175
176
|
- `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. It does read the runtime jar's bytes to derive the `artifactId` it reports, and repeat probes of the same jar in one process reuse that digest. Set `VALIDATE_PROJECT_TASKS_OFF=1` to omit the additive `tasks` field.
|
|
176
177
|
- `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.
|
|
@@ -301,7 +302,7 @@ Tools for browsing Minecraft versions, resolving source artifacts, and reading o
|
|
|
301
302
|
|
|
302
303
|
`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.
|
|
303
304
|
|
|
304
|
-
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.
|
|
305
|
+
For unobfuscated releases such as `26.1+`, `obfuscated` names the runtime jar's names as shipped, which are already Mojang names: both `obfuscated` and `mojang` are accepted, `mappingApplied` reports the label you asked for, and `provenance.unobfuscatedRuntime: true` marks the artifact. `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. This applies to version targets, Minecraft runtime coordinates, and JAR targets whose own contents (a top-level `version.json` with a 26.1+ `id`, `net/minecraft/SharedConstants.class`, and no `.java` sources) prove a 26.1+ Minecraft runtime jar; see [docs/tool-reference.md → Lookup Rules](docs/tool-reference.md#lookup-rules). Labelling these Mojang names `obfuscated` is a known issue, kept unchanged for compatibility until a future major release corrects it; see [docs/tool-reference.md → Known issue: obfuscated label on unobfuscated runtimes](docs/tool-reference.md#known-issue-obfuscated-label-on-unobfuscated-runtimes).
|
|
305
306
|
|
|
306
307
|
### Version Comparison & Symbol Tracking
|
|
307
308
|
|
|
@@ -40,9 +40,9 @@ export declare const analyzeModShape: {
|
|
|
40
40
|
apply: "apply";
|
|
41
41
|
}>>;
|
|
42
42
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
43
|
+
full: "full";
|
|
43
44
|
summary: "summary";
|
|
44
45
|
standard: "standard";
|
|
45
|
-
full: "full";
|
|
46
46
|
}>>;
|
|
47
47
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
48
48
|
[x: string]: string;
|
|
@@ -88,9 +88,9 @@ export declare const analyzeModSchema: z.ZodObject<{
|
|
|
88
88
|
apply: "apply";
|
|
89
89
|
}>>;
|
|
90
90
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
91
|
+
full: "full";
|
|
91
92
|
summary: "summary";
|
|
92
93
|
standard: "standard";
|
|
93
|
-
full: "full";
|
|
94
94
|
}>>;
|
|
95
95
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
96
96
|
[x: string]: string;
|
|
@@ -62,9 +62,9 @@ export declare const analyzeSymbolShape: {
|
|
|
62
62
|
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
63
63
|
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
64
64
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
65
|
+
full: "full";
|
|
65
66
|
summary: "summary";
|
|
66
67
|
standard: "standard";
|
|
67
|
-
full: "full";
|
|
68
68
|
}>>;
|
|
69
69
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
70
70
|
[x: string]: string;
|
|
@@ -132,9 +132,9 @@ export declare const analyzeSymbolSchema: z.ZodObject<{
|
|
|
132
132
|
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
133
133
|
maxCandidates: z.ZodDefault<z.ZodNumber>;
|
|
134
134
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
135
|
+
full: "full";
|
|
135
136
|
summary: "summary";
|
|
136
137
|
standard: "standard";
|
|
137
|
-
full: "full";
|
|
138
138
|
}>>;
|
|
139
139
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
140
140
|
[x: string]: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ResponseDetailLevel } from "../response-utils.js";
|
|
2
2
|
import type { GetClassMembersInput, GetClassMembersOutput, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
|
|
3
|
-
import type { ArtifactScope, MappingSourcePriority,
|
|
3
|
+
import type { ArtifactScope, MappingSourcePriority, SourceMapping } from "../types.js";
|
|
4
|
+
import type { SourceLookupTargetInput } from "../tool-schemas.js";
|
|
4
5
|
import type { MemberProjection } from "../source/class-source/members-builder.js";
|
|
5
6
|
import { type BatchOutput } from "./batch-runner.js";
|
|
6
7
|
export type BatchClassMembersDeps = {
|
|
@@ -16,7 +17,7 @@ export type BatchClassMembersEntry = {
|
|
|
16
17
|
maxMembers?: number;
|
|
17
18
|
};
|
|
18
19
|
export type BatchClassMembersInput = {
|
|
19
|
-
target:
|
|
20
|
+
target: SourceLookupTargetInput;
|
|
20
21
|
mapping?: SourceMapping;
|
|
21
22
|
sourcePriority?: MappingSourcePriority;
|
|
22
23
|
allowDecompile?: boolean;
|
|
@@ -16,6 +16,16 @@ export class BatchClassMembersService {
|
|
|
16
16
|
concurrency,
|
|
17
17
|
failFast,
|
|
18
18
|
resolveSharedArtifact: async () => {
|
|
19
|
+
// target.kind === "artifact" reuses an already-resolved artifactId,
|
|
20
|
+
// short-circuiting resolution exactly as get-class-members's own
|
|
21
|
+
// `kind:"artifact"` target does (src/index.ts normalizeSourceLookupTarget).
|
|
22
|
+
// No provenance is invented for a reused artifact: an unknown id is left
|
|
23
|
+
// to surface per-entry (the same SOURCE_NOT_FOUND getClassMembers raises
|
|
24
|
+
// for an unknown artifactId), since resolveSharedArtifact has no way to
|
|
25
|
+
// validate existence without calling deps.resolveArtifact.
|
|
26
|
+
if (input.target.kind === "artifact") {
|
|
27
|
+
return { artifactId: input.target.artifactId };
|
|
28
|
+
}
|
|
19
29
|
const resolved = await this.deps.resolveArtifact({
|
|
20
30
|
target: input.target,
|
|
21
31
|
mapping: input.mapping,
|
|
@@ -49,12 +59,16 @@ export class BatchClassMembersService {
|
|
|
49
59
|
const raw = (await this.deps.getClassMembers({
|
|
50
60
|
artifactId: sharedArtifact.artifactId,
|
|
51
61
|
// This artifactId is OURS, not the caller's: the shared target was
|
|
52
|
-
// resolved
|
|
53
|
-
// saying so, get-class-members
|
|
54
|
-
// artifactId as the caller having named
|
|
55
|
-
// missing binary jar as their mistake - once
|
|
56
|
-
//
|
|
57
|
-
//
|
|
62
|
+
// resolved (or, for target.kind==="artifact", reused) above and every
|
|
63
|
+
// entry is dispatched by the result. Without saying so, get-class-members
|
|
64
|
+
// reads the bare presence of an artifactId as the caller having named
|
|
65
|
+
// the artifact, and reports a missing binary jar as their mistake - once
|
|
66
|
+
// per entry. A `kind:"artifact"` target is a resolved-id handle, not a
|
|
67
|
+
// caller choice either (mirrors resolveClassArtifactReference's
|
|
68
|
+
// artifactSelectedByFor in inspect-minecraft/handlers/class-members.ts):
|
|
69
|
+
// it says nothing about whether the artifact carries a binary jar and
|
|
70
|
+
// cannot be re-resolved into one that does. Only a jar the caller named
|
|
71
|
+
// themselves is genuinely their choice.
|
|
58
72
|
artifactSelectedBy: input.target.kind === "jar" ? "caller" : "tool",
|
|
59
73
|
className: entry.className,
|
|
60
74
|
access: entry.access,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ResponseDetailLevel } from "../response-utils.js";
|
|
2
2
|
import type { GetClassSourceInput, GetClassSourceOutput, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
|
|
3
|
-
import type { ArtifactScope, MappingSourcePriority,
|
|
3
|
+
import type { ArtifactScope, MappingSourcePriority, SourceMapping } from "../types.js";
|
|
4
|
+
import type { SourceLookupTargetInput } from "../tool-schemas.js";
|
|
4
5
|
import { type BatchOutput } from "./batch-runner.js";
|
|
5
6
|
type SourceMode = "metadata" | "snippet" | "full";
|
|
6
7
|
export type BatchClassSourceEntry = {
|
|
@@ -13,7 +14,7 @@ export type BatchClassSourceEntry = {
|
|
|
13
14
|
outputFile?: string;
|
|
14
15
|
};
|
|
15
16
|
export type BatchClassSourceInput = {
|
|
16
|
-
target:
|
|
17
|
+
target: SourceLookupTargetInput;
|
|
17
18
|
mapping?: SourceMapping;
|
|
18
19
|
sourcePriority?: MappingSourcePriority;
|
|
19
20
|
allowDecompile?: boolean;
|
|
@@ -16,6 +16,16 @@ export class BatchClassSourceService {
|
|
|
16
16
|
concurrency,
|
|
17
17
|
failFast,
|
|
18
18
|
resolveSharedArtifact: async () => {
|
|
19
|
+
// target.kind === "artifact" reuses an already-resolved artifactId,
|
|
20
|
+
// short-circuiting resolution exactly as get-class-source's own
|
|
21
|
+
// `kind:"artifact"` target does (src/index.ts normalizeSourceLookupTarget).
|
|
22
|
+
// No provenance is invented for a reused artifact: an unknown id is left
|
|
23
|
+
// to surface per-entry (the same SOURCE_NOT_FOUND getClassSource raises
|
|
24
|
+
// for an unknown artifactId), since resolveSharedArtifact has no way to
|
|
25
|
+
// validate existence without calling deps.resolveArtifact.
|
|
26
|
+
if (input.target.kind === "artifact") {
|
|
27
|
+
return { artifactId: input.target.artifactId };
|
|
28
|
+
}
|
|
19
29
|
const resolved = await this.deps.resolveArtifact({
|
|
20
30
|
target: input.target,
|
|
21
31
|
mapping: input.mapping,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { DiffClassSignaturesOutput } from "../source-service.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type CompareVersionsOutput } from "../version-diff-service.js";
|
|
4
4
|
import type { GetRegistryDataOutput } from "../registry-service.js";
|
|
5
5
|
export declare const compareMinecraftShape: {
|
|
6
6
|
task: z.ZodOptional<z.ZodEnum<{
|
|
@@ -38,9 +38,9 @@ export declare const compareMinecraftShape: {
|
|
|
38
38
|
registry: z.ZodOptional<z.ZodString>;
|
|
39
39
|
}, z.core.$strip>], "kind">;
|
|
40
40
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
full: "full";
|
|
41
42
|
summary: "summary";
|
|
42
43
|
standard: "standard";
|
|
43
|
-
full: "full";
|
|
44
44
|
}>>;
|
|
45
45
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
46
46
|
[x: string]: string;
|
|
@@ -86,9 +86,9 @@ export declare const compareMinecraftSchema: z.ZodObject<{
|
|
|
86
86
|
registry: z.ZodOptional<z.ZodString>;
|
|
87
87
|
}, z.core.$strip>], "kind">;
|
|
88
88
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
89
|
+
full: "full";
|
|
89
90
|
summary: "summary";
|
|
90
91
|
standard: "standard";
|
|
91
|
-
full: "full";
|
|
92
92
|
}>>;
|
|
93
93
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
94
94
|
[x: string]: string;
|
|
@@ -122,10 +122,33 @@ type CompareMinecraftDeps = {
|
|
|
122
122
|
includeData?: boolean;
|
|
123
123
|
maxEntriesPerRegistry?: number;
|
|
124
124
|
}) => Promise<GetRegistryDataOutput>;
|
|
125
|
+
/**
|
|
126
|
+
* Raw `libraries[].name` coordinates for one version, used only by
|
|
127
|
+
* migration-overview to surface library swaps (e.g. LWJGL GLFW replaced by
|
|
128
|
+
* SDL) that a class/registry diff cannot see. Optional so existing callers
|
|
129
|
+
* that do not wire it keep working: migration-overview simply omits the
|
|
130
|
+
* libraries block when this is absent.
|
|
131
|
+
*/
|
|
132
|
+
getVersionLibraries?: (input: {
|
|
133
|
+
version: string;
|
|
134
|
+
}) => Promise<string[]>;
|
|
135
|
+
};
|
|
136
|
+
export type CompareMinecraftOptions = {
|
|
137
|
+
/**
|
|
138
|
+
* Deadline in milliseconds for fetching both sides' library lists during
|
|
139
|
+
* migration-overview, so a restart with cached jars but no network access
|
|
140
|
+
* fails fast instead of waiting the full underlying fetch timeout. On
|
|
141
|
+
* expiry the libraries block is omitted and a warning is added; the
|
|
142
|
+
* in-flight fetch is left to continue in the background (it may still
|
|
143
|
+
* warm the version-detail cache). Test-only injection point — never read
|
|
144
|
+
* from an environment variable.
|
|
145
|
+
*/
|
|
146
|
+
libraryDiffDeadlineMs?: number;
|
|
125
147
|
};
|
|
126
148
|
export declare class CompareMinecraftService {
|
|
127
149
|
private readonly deps;
|
|
128
|
-
|
|
150
|
+
private readonly libraryDiffDeadlineMs;
|
|
151
|
+
constructor(deps: CompareMinecraftDeps, options?: CompareMinecraftOptions);
|
|
129
152
|
execute(input: CompareMinecraftInput): Promise<Record<string, unknown> & {
|
|
130
153
|
warnings?: string[];
|
|
131
154
|
}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { diffLibraries } from "../version-diff-service.js";
|
|
2
3
|
import { createError, ERROR_CODES } from "../errors.js";
|
|
3
4
|
import { buildIncludeSchema, detailSchema, positiveIntSchema } from "./entry-tool-schema.js";
|
|
4
5
|
import { buildEntryToolMeta, buildEntryToolResult, createNextAction, createSummarySubject, createTruncationMeta } from "./response-contract.js";
|
|
@@ -42,10 +43,37 @@ export const compareMinecraftSchema = z.object(compareMinecraftShape);
|
|
|
42
43
|
function compareStatusFromCounts(changedCount) {
|
|
43
44
|
return changedCount > 0 ? "changed" : "unchanged";
|
|
44
45
|
}
|
|
46
|
+
/** Default bound on migration-overview's library enrichment (see `libraryDiffDeadlineMs`). */
|
|
47
|
+
const DEFAULT_LIBRARY_DIFF_DEADLINE_MS = 5000;
|
|
48
|
+
class LibraryDiffTimeoutError extends Error {
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Races `promise` against a `ms` timer. The timer is always cleared (on
|
|
52
|
+
* either settlement) and unref'd so it can never keep the process alive.
|
|
53
|
+
*/
|
|
54
|
+
function withDeadline(promise, ms) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
reject(new LibraryDiffTimeoutError(`timed out after ${ms}ms`));
|
|
58
|
+
}, ms);
|
|
59
|
+
if (typeof timer.unref === "function") {
|
|
60
|
+
timer.unref();
|
|
61
|
+
}
|
|
62
|
+
promise.then((value) => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve(value);
|
|
65
|
+
}, (error) => {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
reject(error);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
45
71
|
export class CompareMinecraftService {
|
|
46
72
|
deps;
|
|
47
|
-
|
|
73
|
+
libraryDiffDeadlineMs;
|
|
74
|
+
constructor(deps, options) {
|
|
48
75
|
this.deps = deps;
|
|
76
|
+
this.libraryDiffDeadlineMs = options?.libraryDiffDeadlineMs ?? DEFAULT_LIBRARY_DIFF_DEADLINE_MS;
|
|
49
77
|
}
|
|
50
78
|
async execute(input) {
|
|
51
79
|
const task = input.task && input.task !== "auto"
|
|
@@ -362,6 +390,32 @@ export class CompareMinecraftService {
|
|
|
362
390
|
const registrySignals = compare.registry?.summary.registriesChanged ?? 0;
|
|
363
391
|
const status = compareStatusFromCounts(classSignals + registrySignals);
|
|
364
392
|
const representativeClassName = compare.classes?.added[0] ?? compare.classes?.removed[0];
|
|
393
|
+
// Library changes (e.g. LWJGL GLFW replaced by SDL) never show up in the
|
|
394
|
+
// class/registry diff above, so fetch and diff them separately. This must
|
|
395
|
+
// never fail the whole migration-overview: a missing dependency wiring, a
|
|
396
|
+
// fetch failure, or a deadline expiry (e.g. offline after a restart, with
|
|
397
|
+
// only cached jars) just omits the block and adds a warning. On a
|
|
398
|
+
// timeout, the in-flight fetch is left running — it may still warm the
|
|
399
|
+
// version-detail cache for a later call — we simply stop waiting on it.
|
|
400
|
+
let librariesResult;
|
|
401
|
+
const libraryWarnings = [];
|
|
402
|
+
if (this.deps.getVersionLibraries) {
|
|
403
|
+
try {
|
|
404
|
+
const [fromLibraries, toLibraries] = await withDeadline(Promise.all([
|
|
405
|
+
this.deps.getVersionLibraries({ version: subject.fromVersion }),
|
|
406
|
+
this.deps.getVersionLibraries({ version: subject.toVersion })
|
|
407
|
+
]), this.libraryDiffDeadlineMs);
|
|
408
|
+
librariesResult = diffLibraries(fromLibraries, toLibraries);
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
if (error instanceof LibraryDiffTimeoutError) {
|
|
412
|
+
libraryWarnings.push(`Library comparison between ${subject.fromVersion} and ${subject.toVersion} timed out after ${this.libraryDiffDeadlineMs}ms.`);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
libraryWarnings.push(`Could not compare libraries between ${subject.fromVersion} and ${subject.toVersion}: ${error instanceof Error ? error.message : String(error)}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
365
419
|
const nextActions = representativeClassName
|
|
366
420
|
? [
|
|
367
421
|
createNextAction("compare-minecraft", {
|
|
@@ -399,7 +453,13 @@ export class CompareMinecraftService {
|
|
|
399
453
|
}),
|
|
400
454
|
counts: {
|
|
401
455
|
classSignals,
|
|
402
|
-
registrySignals
|
|
456
|
+
registrySignals,
|
|
457
|
+
...(librariesResult
|
|
458
|
+
? {
|
|
459
|
+
librariesAdded: librariesResult.addedCount,
|
|
460
|
+
librariesRemoved: librariesResult.removedCount
|
|
461
|
+
}
|
|
462
|
+
: {})
|
|
403
463
|
},
|
|
404
464
|
nextActions
|
|
405
465
|
},
|
|
@@ -413,11 +473,12 @@ export class CompareMinecraftService {
|
|
|
413
473
|
: registrySignals > 0
|
|
414
474
|
? "registry"
|
|
415
475
|
: "minimal",
|
|
416
|
-
nextActions
|
|
476
|
+
nextActions,
|
|
477
|
+
...(librariesResult ? { libraries: librariesResult } : {})
|
|
417
478
|
}
|
|
418
479
|
}
|
|
419
480
|
}),
|
|
420
|
-
warnings: compare.warnings
|
|
481
|
+
warnings: [...compare.warnings, ...libraryWarnings]
|
|
421
482
|
};
|
|
422
483
|
}
|
|
423
484
|
default:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const detailSchema: z.ZodEnum<{
|
|
3
|
+
full: "full";
|
|
3
4
|
summary: "summary";
|
|
4
5
|
standard: "standard";
|
|
5
|
-
full: "full";
|
|
6
6
|
}>;
|
|
7
7
|
export declare const includeGroupSchema: z.ZodEnum<{
|
|
8
8
|
workspace: "workspace";
|
|
@@ -17,6 +17,7 @@ export declare const includeGroupSchema: z.ZodEnum<{
|
|
|
17
17
|
samples: "samples";
|
|
18
18
|
timings: "timings";
|
|
19
19
|
matrix: "matrix";
|
|
20
|
+
artifact: "artifact";
|
|
20
21
|
provenance: "provenance";
|
|
21
22
|
descriptors: "descriptors";
|
|
22
23
|
diff: "diff";
|
|
@@ -27,7 +28,6 @@ export declare const includeGroupSchema: z.ZodEnum<{
|
|
|
27
28
|
paths: "paths";
|
|
28
29
|
owners: "owners";
|
|
29
30
|
cacheEntries: "cacheEntries";
|
|
30
|
-
artifact: "artifact";
|
|
31
31
|
classes: "classes";
|
|
32
32
|
}>;
|
|
33
33
|
export declare const executionModeSchema: z.ZodEnum<{
|
|
@@ -251,9 +251,9 @@ export declare const inspectMinecraftShape: {
|
|
|
251
251
|
}, z.core.$strip>], "kind">>;
|
|
252
252
|
includeSnapshots: z.ZodDefault<z.ZodBoolean>;
|
|
253
253
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
254
|
+
full: "full";
|
|
254
255
|
summary: "summary";
|
|
255
256
|
standard: "standard";
|
|
256
|
-
full: "full";
|
|
257
257
|
}>>;
|
|
258
258
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
259
259
|
[x: string]: string;
|
|
@@ -512,9 +512,9 @@ export declare const inspectMinecraftSchema: z.ZodObject<{
|
|
|
512
512
|
}, z.core.$strip>], "kind">>;
|
|
513
513
|
includeSnapshots: z.ZodDefault<z.ZodBoolean>;
|
|
514
514
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
515
|
+
full: "full";
|
|
515
516
|
summary: "summary";
|
|
516
517
|
standard: "standard";
|
|
517
|
-
full: "full";
|
|
518
518
|
}>>;
|
|
519
519
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
520
520
|
[x: string]: string;
|
|
@@ -40,9 +40,9 @@ export declare const manageCacheShape: {
|
|
|
40
40
|
projectPath: z.ZodOptional<z.ZodString>;
|
|
41
41
|
}, z.core.$strip>>;
|
|
42
42
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
43
|
+
full: "full";
|
|
43
44
|
summary: "summary";
|
|
44
45
|
standard: "standard";
|
|
45
|
-
full: "full";
|
|
46
46
|
}>>;
|
|
47
47
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
48
48
|
[x: string]: string;
|
|
@@ -94,9 +94,9 @@ export declare const manageCacheSchema: z.ZodObject<{
|
|
|
94
94
|
projectPath: z.ZodOptional<z.ZodString>;
|
|
95
95
|
}, z.core.$strip>>;
|
|
96
96
|
detail: z.ZodOptional<z.ZodEnum<{
|
|
97
|
+
full: "full";
|
|
97
98
|
summary: "summary";
|
|
98
99
|
standard: "standard";
|
|
99
|
-
full: "full";
|
|
100
100
|
}>>;
|
|
101
101
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
102
102
|
[x: string]: string;
|
|
@@ -15,6 +15,40 @@ async function safeEmit(emitter, stage, payload) {
|
|
|
15
15
|
// swallow telemetry failure
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
const DISCOVER_KIND_LABELS = [
|
|
19
|
+
["mixins", "mixin configs"],
|
|
20
|
+
["access-wideners", "access wideners"],
|
|
21
|
+
["access-transformers", "access transformers"]
|
|
22
|
+
];
|
|
23
|
+
function joinWithOr(items) {
|
|
24
|
+
if (items.length <= 2) {
|
|
25
|
+
return items.join(" or ");
|
|
26
|
+
}
|
|
27
|
+
return `${items.slice(0, -1).join(", ")}, or ${items[items.length - 1]}`;
|
|
28
|
+
}
|
|
29
|
+
// A run that discovered no files keeps status "ok" (nothing failed), so the
|
|
30
|
+
// headline and a warning must say that nothing was checked; otherwise the
|
|
31
|
+
// result reads as a pass. Only the kinds subject.discover searched are named.
|
|
32
|
+
function describeNothingToValidate(discover, projectPath) {
|
|
33
|
+
const searched = DISCOVER_KIND_LABELS
|
|
34
|
+
.filter(([kind]) => discover.includes(kind))
|
|
35
|
+
.map(([, label]) => label);
|
|
36
|
+
const notOk = "so status \"ok\" does not mean any file passed validation.";
|
|
37
|
+
const accessTransformerHint = discover.includes("access-transformers")
|
|
38
|
+
? ""
|
|
39
|
+
: " Access Transformer files are searched only when subject.discover includes \"access-transformers\".";
|
|
40
|
+
if (searched.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
headline: "Nothing to validate: subject.discover selected no file kinds to search.",
|
|
43
|
+
warning: `Nothing was validated: subject.discover selected no file kinds to search, ${notOk}${accessTransformerHint}`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const list = joinWithOr(searched);
|
|
47
|
+
return {
|
|
48
|
+
headline: `Nothing to validate: no ${list} were found.`,
|
|
49
|
+
warning: `Nothing was validated: no ${list} were found under ${projectPath}, ${notOk}${accessTransformerHint}`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
18
52
|
export async function handleProjectSummary(deps, input, detail, include, options = {}) {
|
|
19
53
|
// Forwarded emitter for nested validators and probes; same swallow contract
|
|
20
54
|
// as safeEmit so a rejecting raw emitter cannot leak into their outcomes.
|
|
@@ -27,7 +61,9 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
27
61
|
message: "task=project-summary requires subject.kind=workspace."
|
|
28
62
|
});
|
|
29
63
|
}
|
|
30
|
-
|
|
64
|
+
// An omitted version is inferred from the project below; only an explicit
|
|
65
|
+
// preferProjectVersion=false opts out of that inference.
|
|
66
|
+
if (!input.version && input.preferProjectVersion === false) {
|
|
31
67
|
const baseResult = buildEntryToolResult({
|
|
32
68
|
task: "project-summary",
|
|
33
69
|
detail,
|
|
@@ -52,7 +88,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
52
88
|
}
|
|
53
89
|
],
|
|
54
90
|
notes: [
|
|
55
|
-
"Pass version explicitly, or retry
|
|
91
|
+
"Version inference was suppressed by preferProjectVersion=false. Pass version explicitly, or retry without preferProjectVersion=false to infer it from gradle.properties. The suggested retry sets preferProjectVersion=true for you."
|
|
56
92
|
]
|
|
57
93
|
},
|
|
58
94
|
blocks: {
|
|
@@ -79,10 +115,21 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
79
115
|
projectPath,
|
|
80
116
|
discover
|
|
81
117
|
});
|
|
82
|
-
|
|
118
|
+
// preferProjectVersion=true overrides an explicit version; an omitted version is
|
|
119
|
+
// inferred (the explicit-false opt-out returned above).
|
|
120
|
+
const detectedProjectVersion = input.preferProjectVersion === true || !input.version
|
|
83
121
|
? await deps.detectProjectMinecraftVersion?.(projectPath)
|
|
84
122
|
: undefined;
|
|
85
123
|
const resolvedVersion = detectedProjectVersion ?? input.version;
|
|
124
|
+
const versionInferred = !input.version && Boolean(detectedProjectVersion);
|
|
125
|
+
// An inferred version is a project-version resolution: sub-validators and the
|
|
126
|
+
// artifact probe see exactly what an explicit preferProjectVersion=true run sees.
|
|
127
|
+
const effectivePreferProjectVersion = versionInferred ? true : input.preferProjectVersion;
|
|
128
|
+
const versionInferenceWarnings = versionInferred
|
|
129
|
+
? [
|
|
130
|
+
`version was inferred from the workspace: ${detectedProjectVersion} (source: projectPath:gradle.properties (${projectPath})).`
|
|
131
|
+
]
|
|
132
|
+
: [];
|
|
86
133
|
const [mixinConfigs, accessWideners, accessTransformers] = await Promise.all([
|
|
87
134
|
discover.includes("mixins")
|
|
88
135
|
? deps.discoverMixins(projectPath, input.configPaths)
|
|
@@ -111,17 +158,20 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
111
158
|
sourcePriority: input.sourcePriority,
|
|
112
159
|
scope: input.scope
|
|
113
160
|
}),
|
|
161
|
+
// Retrying without a version would infer, fail, and block again, so the
|
|
162
|
+
// recovery asks for an explicit version instead.
|
|
114
163
|
nextActions: [
|
|
115
164
|
{
|
|
116
165
|
tool: "validate-project",
|
|
117
166
|
params: {
|
|
118
167
|
task: "project-summary",
|
|
119
|
-
subject: input.subject
|
|
168
|
+
subject: input.subject,
|
|
169
|
+
version: "<minecraft-version>"
|
|
120
170
|
}
|
|
121
171
|
}
|
|
122
172
|
],
|
|
123
173
|
notes: [
|
|
124
|
-
"
|
|
174
|
+
"The Minecraft version could not be inferred from gradle.properties (minecraft_version, mc_version, or minecraftVersion). Retry with version set explicitly: replace \"<minecraft-version>\" in the suggested call with the project's Minecraft version, or declare it in gradle.properties."
|
|
125
175
|
]
|
|
126
176
|
},
|
|
127
177
|
blocks: {
|
|
@@ -150,14 +200,16 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
150
200
|
]
|
|
151
201
|
};
|
|
152
202
|
}
|
|
203
|
+
// Reached only when discovery found nothing (the branch above handles found files).
|
|
153
204
|
if (!resolvedVersion) {
|
|
205
|
+
const nothingToValidate = describeNothingToValidate(discover, projectPath);
|
|
154
206
|
const baseResult = buildEntryToolResult({
|
|
155
207
|
task: "project-summary",
|
|
156
208
|
detail,
|
|
157
209
|
include,
|
|
158
210
|
summary: {
|
|
159
211
|
status: "ok",
|
|
160
|
-
headline:
|
|
212
|
+
headline: nothingToValidate.headline,
|
|
161
213
|
subject: createSummarySubject({
|
|
162
214
|
task: "project-summary",
|
|
163
215
|
kind: input.subject.kind,
|
|
@@ -187,11 +239,11 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
187
239
|
return {
|
|
188
240
|
...baseResult,
|
|
189
241
|
...(tasks ? { tasks } : {}),
|
|
190
|
-
warnings: []
|
|
242
|
+
warnings: [nothingToValidate.warning]
|
|
191
243
|
};
|
|
192
244
|
}
|
|
193
245
|
const validationVersion = resolvedVersion;
|
|
194
|
-
const warnings = [];
|
|
246
|
+
const warnings = [...versionInferenceWarnings];
|
|
195
247
|
const mixinDurationStart = Date.now();
|
|
196
248
|
let validMixins = 0;
|
|
197
249
|
let partialMixins = 0;
|
|
@@ -288,7 +340,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
288
340
|
projectPath,
|
|
289
341
|
gradleUserHome,
|
|
290
342
|
scope: input.scope,
|
|
291
|
-
preferProjectVersion:
|
|
343
|
+
preferProjectVersion: effectivePreferProjectVersion
|
|
292
344
|
});
|
|
293
345
|
if (output.valid) {
|
|
294
346
|
validAw += 1;
|
|
@@ -337,7 +389,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
337
389
|
projectPath,
|
|
338
390
|
gradleUserHome,
|
|
339
391
|
scope: input.scope,
|
|
340
|
-
preferProjectVersion:
|
|
392
|
+
preferProjectVersion: effectivePreferProjectVersion
|
|
341
393
|
});
|
|
342
394
|
if (output.valid) {
|
|
343
395
|
validAt += 1;
|
|
@@ -361,13 +413,20 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
361
413
|
const invalidCount = invalidMixins + invalidAw + invalidAt;
|
|
362
414
|
const partialCount = partialMixins;
|
|
363
415
|
const status = invalidCount > 0 ? "invalid" : partialCount > 0 ? "partial" : "ok";
|
|
416
|
+
const nothingToValidate = mixinConfigs.length === 0 && accessWideners.length === 0 && accessTransformers.length === 0
|
|
417
|
+
? describeNothingToValidate(discover, projectPath)
|
|
418
|
+
: undefined;
|
|
419
|
+
if (nothingToValidate) {
|
|
420
|
+
warnings.push(nothingToValidate.warning);
|
|
421
|
+
}
|
|
364
422
|
const baseResult = buildEntryToolResult({
|
|
365
423
|
task: "project-summary",
|
|
366
424
|
detail,
|
|
367
425
|
include,
|
|
368
426
|
summary: {
|
|
369
427
|
status,
|
|
370
|
-
headline:
|
|
428
|
+
headline: nothingToValidate?.headline ??
|
|
429
|
+
`Validated ${mixinConfigs.length} mixin config(s), ${accessWideners.length} access widener(s), and ${accessTransformers.length} access transformer(s).`,
|
|
371
430
|
subject: createSummarySubject({
|
|
372
431
|
task: "project-summary",
|
|
373
432
|
kind: input.subject.kind,
|
|
@@ -416,7 +475,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
416
475
|
sourcePriority: input.sourcePriority,
|
|
417
476
|
gradleUserHome,
|
|
418
477
|
scope: input.scope,
|
|
419
|
-
preferProjectVersion:
|
|
478
|
+
preferProjectVersion: effectivePreferProjectVersion,
|
|
420
479
|
mixinDiscoveryCount: mixinConfigs.length,
|
|
421
480
|
mixinCaughtErrors,
|
|
422
481
|
mixinCounts: { ok: validMixins, partial: partialMixins, invalid: invalidMixins },
|