Skip to content

Update project configuration and enhance documentation#16

Closed
SpyC0der77 wants to merge 12 commits into
mainfrom
add-mod
Closed

Update project configuration and enhance documentation#16
SpyC0der77 wants to merge 12 commits into
mainfrom
add-mod

Conversation

@SpyC0der77

@SpyC0der77 SpyC0der77 commented Jun 9, 2026

Copy link
Copy Markdown
Owner
  • Added new entries to .gitignore for Gradle build artifacts and node modules.
  • Expanded the todo.md file to include detailed implementation plans for the MCS Hot-Reload Mod, covering core functionality, project phases, and configuration management.
  • Introduced a context manager in config_utils.py for temporary configuration overrides.
  • Enhanced argument parsing in shell_commands.py to support new flags for the compile command, including --mc-version and --force.
  • Updated the CompileInterpreter to conditionally track built-in function usage based on logging style.
  • Minor adjustments to built-in log function templates for consistency.

Summary by CodeRabbit

  • New Features

    • Added MCS Packs Mod for Fabric, Forge, and NeoForge that watches datapack folders, auto-compiles, validates, and hot-reloads into your Minecraft world.
    • Enhanced compile CLI with stricter validation, safer path handling, and optional --mc-version and --force flags.
  • Documentation

    • Added mod README covering setup, supported versions, and build instructions.
  • Tests

    • Added integration tests for compile command flags.

- Added new entries to `.gitignore` for Gradle build artifacts and node modules.
- Expanded the `todo.md` file to include detailed implementation plans for the MCS Hot-Reload Mod, covering core functionality, project phases, and configuration management.
- Introduced a context manager in `config_utils.py` for temporary configuration overrides.
- Enhanced argument parsing in `shell_commands.py` to support new flags for the compile command, including `--mc-version` and `--force`.
- Updated the `CompileInterpreter` to conditionally track built-in function usage based on logging style.
- Minor adjustments to built-in log function templates for consistency.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SpyC0der77, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 35 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b4c49c2-6975-498e-9247-597c64a9d38a

📥 Commits

Reviewing files that changed from the base of the PR and between 55623a9 and ed78759.

⛔ Files ignored due to path filters (1)
  • mod/common/.gradle/.architectury-transformer/debug.log is excluded by !**/*.log
📒 Files selected for processing (20)
  • .github/workflows/mod.yml
  • mod/build.gradle
  • mod/common/build.gradle
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java
  • mod/common/src/main/resources/scripts/mcs-compile.cmd
  • mod/common/src/main/resources/scripts/mcs-compile.sh
  • mod/fabric/build.gradle
  • mod/fabric/src/main/resources/fabric.mod.json
  • mod/forge/build.gradle
  • mod/forge/src/main/resources/META-INF/mods.toml
  • mod/gradle.properties
  • mod/gradle/wrapper/gradle-wrapper.properties
  • mod/neoforge/build.gradle
  • mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml
  • mod/scripts/mcs-spyglass-validate.mjs
  • mod/versions/manifest.json
📝 Walkthrough

Walkthrough

This PR implements a full Minecraft pack hot-reload runtime mod and extends the MCS compiler CLI with configurable flags. The mod watches a mcs_packs/ folder, runs each pack through a linting → compiling → Spyglass validation pipeline, and deploys successful datapacks to the active world with optional automatic reload. CLI changes support per-invocation Minecraft version override and forced rebuilds, backed by a temporary config context manager. Build automation is manifest-driven, supporting Fabric, Forge, and NeoForge across multiple Minecraft versions via GitHub Actions matrix jobs.

Changes

MCS Packs Mod Feature

Layer / File(s) Summary
CLI flags and temporary config overrides
minecraft_script/shell_commands.py, minecraft_script/config_utils.py, minecraft_script/compiler/build_templates/builtins/1.21.2/log.mcfunction, minecraft_script/compiler/compile_interpreter.py, tests/test_compile_cli_flags.py
The compile command now parses --mc-version, --force, and --verbose/--no-verbose flags via a new _parse_flag_args helper and validates datapack names. A new temporary_config context manager temporarily overrides global config values and restores them on exit. The log builtin's direct_tellraw style is tracked conditionally to avoid duplicate recording. Log function signature data is reset and rebuilt via explicit append operations. Integration tests verify --force overwrites existing packs and --mc-version does not mutate persistent config.
Configuration, version mapping, filesystem utilities
mod/common/src/main/java/dev/spyc0der77/mcspacks/config/*, mod/common/src/main/java/dev/spyc0der77/mcspacks/util/*, mod/common/src/main/java/dev/spyc0der77/mcspacks/version/*
ModConfig holds compiler selection, debounce timing, auto-reload, and lint/validation blocking rules. ModConfigManager loads/saves config via Gson with atomic file operations. McsPaths centralizes filesystem layout (game root, packs folder, compiled output, config file) and bootstraps bundled resources (compiler wrapper, Spyglass validator). SafePaths validates safe names and enforces parent-contained path resolution. VersionMapper loads an MCS version index and resolves profiles. PlayerFeedback formats and broadcasts diagnostics and messages to players.
Compiler resolution and toolchain execution
mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/*, mod/common/src/main/resources/scripts/mcs-*, mod/scripts/mcs-spyglass-validate.mjs, mod/scripts/package.json
ProcessResult and SubprocessRunner encapsulate subprocess execution with timeout and async I/O. CompilerResolver auto-detects the compiler (mcs, Python, or versioned Python installs), probes with lint --help, and caches resolved commands. McsToolchain runs lint → compile → Spyglass validation, respecting enable/block flags and returning diagnostic lists. Platform-specific compiler wrapper scripts (.cmd for Windows, .sh for Unix) search PATH and fall back to configured paths. The Spyglass validator Node.js module walks datapacks, collects diagnostics, and outputs JSON or text.
Pack discovery and deployment
mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/*, mod/common/src/main/java/dev/spyc0der77/mcspacks/deploy/*
PackRegistry discovers packs under a root by reading pack.json overrides, validating entry files, and computing display names via title-casing. PackDefinition is an immutable record of pack metadata and compiled output paths. DatapackDeployer syncs compiled datapacks to the server's datapack folder via safe child path resolution and recursive copy/delete operations, then reloads via /reload command with elevated permissions.
Filesystem watch and debounce
mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/*
PackWatcher monitors pack folders using WatchService with debounced callbacks. It seeds baseline file modification timestamps, registers the directory tree (skipping _compiled), resolves changed paths to owning pack folders via upward traversal, schedules debounced callbacks per pack, and falls back to periodic polling if watch keys are missed. Stop/start lifecycle control and per-pack pending task management prevent stale and duplicate processing.
Pack processing pipeline
mod/common/src/main/java/dev/spyc0der77/mcspacks/pipeline/*
PackPipeline orchestrates pack discovery, toolchain execution, and deployment on a dedicated single-thread executor. It tracks generation counters per pack to suppress stale runs, binds an active server for lifecycle callbacks, prepares world packs inline on server start, handles pack changes asynchronously, and enforces configurable blocking rules (e.g., do not reload if Spyglass errors exist). Diagnostics are broadcast to players via PlayerFeedback when a server is active.
Core mod initialization and loader entrypoints
mod/common/src/main/java/dev/spyc0der77/mcspacks/McsPacks.java, mod/common/src/*/java/dev/spyc0der77/mcspacks/deploy/CommandSources.java, mod/fabric/src/main/java/..., mod/forge/src/main/java/..., mod/neoforge/src/main/java/...
McsPacks wires the mod system: ensures folder layout, loads config/version mapping, creates registry/pipeline/watcher, and registers server lifecycle listeners (startup: prepare packs; started: start watch; stopping: stop watch). Loader-specific entrypoints (McsPacksFabric, McsPacksForge, McsPacksNeoForge) call McsPacks.init() during mod initialization. CommandSources (legacy and modern variants) provide permission helpers for executing commands.
Build manifest and root Gradle wiring
mod/versions/manifest.json, mod/build.gradle, mod/settings.gradle, mod/gradle.properties, mod/scripts/apply_version.py, mod/scripts/build_all.py
A manifest.json declares supported loaders and Minecraft version profiles with pinned loader/API versions. The root build.gradle reads the manifest, selects a profile from a Gradle property (or CLI argument, defaulting to 1.21.11), and populates extension properties for all subprojects. settings.gradle configures plugin repositories and conditionally includes platform modules based on enabled_platforms. apply_version.py selects a profile and writes gradle.properties; build_all.py builds all profile/loader combinations via the Gradle wrapper.
Common module resources and bundling
mod/common/build.gradle
Determines version-dependent source sets (modern vs. legacy CommandSources based on Minecraft version regex). Defines generated resources directory for synced version index and starter pack. A bundleSpyglassScript task bundles the Spyglass validator via Bun or npx esbuild into CommonJS. processResources expands version placeholders into mod metadata files and depends on all sync/bundle tasks.
Platform-specific module configuration
mod/fabric/build.gradle, mod/forge/build.gradle, mod/neoforge/build.gradle, mod/*/gradle.properties, mod/*/src/main/resources/*
Each platform module extends common configuration, declares loader-specific dependencies, sets up Shadow artifact shading, and reconfigures remapJar to use the shadowed JAR. Forge and NeoForge compute "upper" version bounds for dependency ranges. Each module defines a metadata file (fabric.mod.json, META-INF/mods.toml, META-INF/neoforge.mods.toml) with templated version placeholders and dependency requirements.
CI workflow, Gradle wrapper, and documentation
.github/workflows/mod.yml, mod/gradlew, mod/gradlew.bat, mod/gradle/wrapper/gradle-wrapper.properties, .gitignore, .gitattributes, mod/README.md, todo.md
A GitHub Actions workflow mod.yml triggers on push/pull_request for paths under mod/ and minecraft_script/versions/, defines a matrix over Minecraft profiles and loaders, and runs apply_version.py and Gradle builds for each combination. Gradle wrapper scripts (POSIX shell and Windows batch) bootstrap Gradle 8.12 with Java 21. Ignore rules added for mod/ build artifacts and node_modules. Line-ending enforcement via .gitattributes for batch/cmd scripts. mod/README.md documents prerequisites, build commands, supported versions, and on-disk layout. todo.md expanded into a comprehensive implementation checklist.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • SpyC0der77/Minecraft-Script#12: Both PRs modify minecraft_script/shell_commands.py for path handling and sh_compile argument parsing; PR #12 uses pathlib refactoring while this PR adds stricter flag parsing and output-root escape validation.
  • SpyC0der77/Minecraft-Script#9: Both PRs refine the log builtin's direct_tellraw path: this PR updates builtin tracking in CompileInterpreter and data operations in log.mcfunction; PR #9 refactors text-component serialization for direct-tellraw.
  • SpyC0der77/Minecraft-Script#3: Both PRs touch the log builtin implementation (log.mcfunction template and CompileInterpreter tracking), affecting the same code areas with overlapping concerns.

Poem

🐰 A rabbit hops through JSON paths and build.gradle streams,
Watching packs compile and glow with validator dreams.
From Fabric to Forge, the loaders align—
Hot-reload at last, in Minecraft's design! ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-mod

- Introduced a new `gradlew` script to facilitate Gradle project execution in POSIX-compliant shells.
- The script includes necessary configurations for Java command execution and environment variable handling.
- Ensured compatibility with various operating systems, including Cygwin, Darwin, and MinGW.

This addition streamlines the build process for users in POSIX environments.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

28 issues found across 59 files

Tip: instead of fixing issues one by one fix them all with cubic
Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.

Re-trigger cubic

Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/PackWatcher.java Outdated
Comment thread mod/common/src/main/resources/scripts/mcs-compile.cmd Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java Outdated
Comment thread mod/forge/src/main/resources/META-INF/mods.toml Outdated
Comment thread mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java Outdated
Comment thread mod/scripts/mcs-spyglass-validate.mjs Outdated
Comment thread mod/scripts/mcs-spyglass-validate.mjs Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java Outdated
- Introduced a conditional source set in `build.gradle` to switch between modern and legacy Java directories based on Minecraft version.
- Updated `DatapackDeployer.java` to replace direct permission handling with a new command source method for improved clarity and maintainability.
- Added Bun dependency installation step in the GitHub Actions workflow for improved JavaScript package management.
- Introduced a validation function in `shell_commands.py` to ensure valid datapack names, enhancing error handling.
- Updated `build.gradle` to conditionally set Minecraft version and supported game versions based on CLI input.
- Enhanced Gradle properties to support multiple platforms (fabric, forge, neoforge) for better compatibility.
- Refactored `apply_version.py` to dynamically read enabled platforms from `gradle.properties`.
- Improved error handling in `ModConfigManager` and `PackRegistry` for better resilience against invalid configurations.
- Updated `mcs-spyglass-validate` script to streamline datapack validation and improve error reporting.
- Removed deprecated `mcs-versions.json` and `mcs-spyglass-validate.mjs` files, consolidating functionality into updated scripts.oo
@cubic-dev-ai

cubic-dev-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@SpyC0der77

Copy link
Copy Markdown
Owner Author

@cubic-dev-ai review this

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

@cubic-dev-ai review this

@SpyC0der77 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 issues found across 61 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="mod/common/src/main/resources/starter/pack.mcs">

<violation number="1" location="mod/common/src/main/resources/starter/pack.mcs:1">
P3: This file duplicates `examples/starter_datapack.mcs` verbatim, creating a maintainability risk from having two copies of the same starter script.</violation>
</file>

<file name="mod/gradle/wrapper/gradle-wrapper.properties">

<violation number="1" location="mod/gradle/wrapper/gradle-wrapper.properties:3">
P2: Gradle wrapper distribution is not checksum-pinned; add `distributionSha256Sum` to verify downloaded binaries.</violation>
</file>

<file name="mod/settings.gradle">

<violation number="1" location="mod/settings.gradle:5">
P2: Use the current Forge Maven repository URL in pluginManagement; the configured `files.minecraftforge.net` endpoint is legacy.</violation>
</file>

<file name=".github/workflows/mod.yml">

<violation number="1" location=".github/workflows/mod.yml:4">
P2: Workflow path filters exclude the workflow file itself, so workflow-only changes won't be validated by this CI job.</violation>
</file>

<file name="mod/versions/manifest.json">

<violation number="1" location="mod/versions/manifest.json:80">
P1: The `26.1` profile uses a Forge version (`60.0.0`) that targets Minecraft 1.21.10, so Forge builds for this profile will resolve the wrong artifact coordinate.</violation>
</file>

<file name="mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml">

<violation number="1" location="mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml:2">
P1: `loaderVersion` is set too high for NeoForge and can prevent the mod from loading on otherwise supported versions.</violation>
</file>

<file name="mod/common/src/main/resources/scripts/mcs-compile.cmd">

<violation number="1" location="mod/common/src/main/resources/scripts/mcs-compile.cmd:5">
P2: The wrapper prioritizes hardcoded Python paths before `mcs`, which can cause false failures when Python exists but `minecraft_script` is not installed.</violation>
</file>

<file name="mod/common/src/main/java/dev/spyc0der77/mcspacks/version/VersionMapper.java">

<violation number="1" location="mod/common/src/main/java/dev/spyc0der77/mcspacks/version/VersionMapper.java:33">
P2: Unused parameter `gameVersion`: the method ignores it and calls `Platform.getMinecraftVersion()` directly. This creates a hidden static dependency that makes the method harder to test and the parameter misleading. Use the `gameVersion` parameter instead of the static call.</violation>
</file>

<file name="mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java">

<violation number="1" location="mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java:14">
P1: `validateSafeName` permits `"."`, which resolves to the parent directory and can trigger recursive deletion of the root target directory.</violation>
</file>

<file name="mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java">

<violation number="1" location="mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java:146">
P2: Batch command quoting does not escape `%`, so `cmd.exe` expands `%VAR%` inside quoted args and can corrupt compiler arguments.</violation>
</file>

<file name="mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java">

<violation number="1" location="mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java:115">
P2: `relative()` assumes paths are always relativizable; with absolute `entryFile` overrides it can throw and break diagnostic reporting.</violation>
</file>

<file name="mod/common/build.gradle">

<violation number="1" location="mod/common/build.gradle:55">
P2: The build step invokes `bun` unconditionally, introducing an undocumented hard dependency that can break `processResources` on Node-only environments.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.

Re-trigger cubic

Comment thread mod/versions/manifest.json Outdated
Comment thread mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java Outdated
Comment thread mod/gradle/wrapper/gradle-wrapper.properties Outdated
Comment thread mod/settings.gradle Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java Outdated
Comment thread mod/common/build.gradle Outdated
Comment thread mod/common/src/main/resources/starter/pack.mcs Outdated
- Updated GitHub Actions workflow to pass the loader argument to the version application script, allowing for targeted platform builds.
- Modified `settings.gradle` to dynamically set enabled platforms based on the provided loader or default to multiple platforms.
- Enhanced `apply_version.py` to conditionally set enabled platforms based on the loader argument, improving flexibility in build configurations.
- Refactored `build_all.py` to incorporate the loader argument in the build command, streamlining the build process for specific platforms.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 15 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="mod/common/src/main/resources/scripts/mcs-compile.cmd">

<violation number="1" location="mod/common/src/main/resources/scripts/mcs-compile.cmd:19">
P2: Interpreter resolution order now prefers arbitrary PATH Python over known compatible `C:\Python310+` installs, which can select an unsupported interpreter.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread mod/common/src/main/resources/scripts/mcs-compile.cmd
Comment thread mod/scripts/apply_version.py
@kilo-code-bot

kilo-code-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No New Issues Found | Recommendation: Review cubic comments before merge

This PR has been partially reviewed by cubic. The existing cubic review comments cover the following key issues:

  • CRITICAL (from cubic): mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java:14 - validateSafeName permits ".", which can resolve to the parent directory and trigger recursive deletion of the root target directory.

  • CRITICAL (from cubic): mod/versions/manifest.json:80 - The 26.1 profile uses forge_version: "64.0.0" which may target a different Minecraft version than intended (Minecraft 26.1).

  • WARNING (from cubic): mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml:2 - loaderVersion = "[1,)" is too permissive and may cause compatibility issues.

  • WARNING (from cubic): mod/settings.gradle:5 - The Forge Maven repository URL files.minecraftforge.net is legacy; consider using the current endpoint.

  • WARNING (from cubic): mod/common/build.gradle:55 - The bun.lock file reference may be missing or the fallback to npx may fail in environments without Node.js.

  • WARNING (from cubic): mod/scripts/apply_version.py:33 - The --loader help says the default is "all loaders", but the implementation reuses prior enabled_platforms value, causing inconsistent default behavior.

The new files added in this PR (mod/scripts/mcs-spyglass-validate.mjs, mod/scripts/package.json, mod/settings.gradle, mod/versions/manifest.json, tests/test_compile_cli_flags.py) appear well-structured and follow the existing project patterns.

Please address the cubic review issues before merging.

Files Reviewed (5 files with changes)
  • mod/scripts/mcs-spyglass-validate.mjs - New file, no issues
  • mod/scripts/package.json - New file, no issues
  • mod/settings.gradle - New file, review cubic warnings
  • mod/versions/manifest.json - New file, review cubic CRITICAL warning
  • tests/test_compile_cli_flags.py - New test file, well-structured

}

if (positional.length < 1) {
throw new Error('Usage: node mcs-spyglass-validate.js [--json] [--mc-version <version>] <datapack_dir>')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Usage message references wrong file extension. The usage message says mcs-spyglass-validate.js but the source file is mcs-spyglass-validate.mjs. Should reference the actual filename for consistency.

@kilo-code-bot

kilo-code-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 3
SUGGESTION 1
Issue Details (click to expand)

CRITICAL

File Line Issue
mod/versions/manifest.json 80 The 26.1 profile uses forge_version: 64.0.0 which may target a different Minecraft version than intended (Minecraft 26.1).

WARNING

File Line Issue
mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml 2 loaderVersion = "[1,)" is too permissive and may cause compatibility issues.
mod/settings.gradle 5 The Forge Maven repository URL maven.minecraftforge.net is legacy; consider using the current endpoint.

SUGGESTION

File Line Issue
mod/scripts/mcs-spyglass-validate.mjs 51 Usage message references wrong file extension (mcs-spyglass-validate.js vs actual mcs-spyglass-validate.mjs).
Carried Forward from Previous Review

Issues from cubic on unchanged files:

File Line Issue
mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java 14 validateSafeName permits "." which can resolve to the parent directory and trigger recursive deletion of the root target directory.
mod/common/build.gradle 55 The bun.lock file reference may be missing or the fallback to npx may fail in environments without Node.js.
mod/scripts/apply_version.py 33 The --loader help says the default is "all loaders", but the implementation reuses prior enabled_platforms value, causing inconsistent default behavior.
Files Reviewed (6 files)
  • mod/scripts/mcs-spyglass-validate.mjs - 1 issue
  • mod/scripts/package.json - No issues
  • mod/settings.gradle - No issues
  • mod/versions/manifest.json - Review carried forward
  • mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml - Review carried forward
  • mod/common/build.gradle - Review carried forward

Fix these issues in Kilo Cloud


Reviewed by laguna-m.1-20260312:free · 765,433 tokens

@SpyC0der77

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
mod/gradlew.bat (1)

1-95: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize this batch wrapper to CRLF line endings.

This file is a Windows .bat entrypoint; LF-only endings can cause parser/label issues in some Windows environments. Convert to CRLF to keep wrapper behavior reliable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/gradlew.bat` around lines 1 - 95, Convert this batch wrapper to CRLF line
endings (currently LF-only) so Windows cmd labels and parsing work reliably:
open the gradlew.bat file and save/commit it with CRLF line endings, ensuring
you do not alter any labels or logic (refer to labels like :execute, :fail,
:mainEnd to verify content integrity), and add or update repository settings to
enforce CRLF for .bat files (e.g., add a .gitattributes entry like "*.bat text
eol=crlf" or set core.autocrlf appropriately) so future commits preserve CRLF.

Source: Linters/SAST tools

mod/common/src/main/resources/scripts/mcs-compile.cmd (1)

1-62: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Batch file uses Unix line endings (LF) instead of Windows line endings (CRLF).

Windows batch files require CRLF line endings. LF-only line endings can cause GOTO/CALL label parsing failures and script malfunction due to Windows batch parser bugs, especially at 512-byte boundaries.

Convert this file to Windows CRLF line endings using:

  • dos2unix (or unix2dos)
  • Git: git config core.autocrlf true and re-checkout
  • Or configure .gitattributes with *.cmd text eol=crlf
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/src/main/resources/scripts/mcs-compile.cmd` around lines 1 - 62,
The batch file uses LF-only line endings which can break Windows CMD label
parsing (affecting sections like :run_mcs, :run_python, :run_py and the main
search/exit flow), so convert this file to CRLF line endings (e.g., run
unix2dos/dos2unix -w, set git core.autocrlf=true and re-checkout, or add a
.gitattributes entry like "*.cmd text eol=crlf" and re-checkout) and re-run
tests to ensure GOTO/CALL labels and %ERRORLEVEL% propagation behave correctly.

Source: Linters/SAST tools

🟡 Minor comments (6)
minecraft_script/shell_commands.py-44-47 (1)

44-47: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject unknown --flags instead of treating them as positional arguments.

At Line 44-Line 47, unsupported flags are appended to positional, which can mis-route argument parsing (e.g., source path becomes "--unknown"), producing confusing failures.

Suggested fix
-        if arg.startswith("--"):
-            if arg not in supported_flags:
-                positional.append(arg)
-            index += 1
-            continue
+        if arg.startswith("--"):
+            print(f"Error: Unknown flag {arg}.")
+            exit(-1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@minecraft_script/shell_commands.py` around lines 44 - 47, The code currently
treats unsupported long flags as positional arguments in the argument-parsing
loop (see the handling of arg, supported_flags, positional and index), causing
misrouting; change this so that when arg.startswith("--") and arg is not in
supported_flags you raise/return an explicit error (e.g., raise ValueError or
call parser.error) instead of appending to positional, and ensure index is still
advanced; update the branch in the parser loop that checks arg.startswith("--")
to validate against supported_flags and reject unknown flags with a clear
message referencing the flag.
tests/test_compile_cli_flags.py-69-92 (1)

69-92: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

--mc-version mutation test does not verify persisted config state.

Line 91 checks in-process COMMON_CONFIG, but subprocess.run(...) executes in a separate process. A bad write to config.json could still happen and this assertion would miss it.

Suggested fix
 def test_compile_mc_version_does_not_mutate_config(build_output):
-    original_version = COMMON_CONFIG["minecraft_version"]
+    config_path = Path(module_folder) / "config.json"
+    original_version = json.loads(config_path.read_text(encoding="utf-8"))["minecraft_version"]
     try:
         result = subprocess.run(
@@
         )
         assert result.returncode == 0, result.stderr
-        assert COMMON_CONFIG["minecraft_version"] == original_version
+        current_version = json.loads(config_path.read_text(encoding="utf-8"))["minecraft_version"]
+        assert current_version == original_version
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_compile_cli_flags.py` around lines 69 - 92, The test
test_compile_mc_version_does_not_mutate_config currently only checks the
in-process COMMON_CONFIG after calling subprocess.run, which won't catch writes
to the on-disk config; update the test to, after the subprocess.run completes,
reload the persisted config file (e.g., read and parse the same config.json the
application uses) and assert that the persisted "minecraft_version" equals
original_version (in addition to the existing in-memory assertion). Use the same
symbols from the diff (test_compile_mc_version_does_not_mutate_config,
COMMON_CONFIG, and the subprocess.run invocation) to locate where to add the
file-read/parse and assertion, and ensure cleanup still happens in the finally
block.
todo.md-69-69 (1)

69-69: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix markdownlint violations in docs blocks/tables.

Line 69 and Line 105 use fenced code blocks without language labels, and Line 290 table formatting needs blank lines around the table.

Also applies to: 105-105, 290-290

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@todo.md` at line 69, Update the markdown to satisfy markdownlint: add
language identifiers to the two fenced code blocks (the triple-backtick blocks
referenced) so they become e.g. ```bash or ```json as appropriate, and ensure
the table near the noted section has a blank line before and after it (insert an
empty line above the table header and one below the table) to fix table
formatting violations.

Source: Linters/SAST tools

mod/scripts/apply_version.py-22-24 (1)

22-24: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve explicit enabled_platforms=fabric instead of resetting to all loaders.

Line 22 treats "fabric" as invalid and falls back to default, so an explicit fabric-only config is lost on rewrite.

Suggested fix
-            if value and value != "fabric":
+            if value:
                 return value
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/scripts/apply_version.py` around lines 22 - 24, The code in
apply_version.py wrongly treats the string "fabric" as invalid and falls back to
DEFAULT_ENABLED_PLATFORMS when rewriting enabled_platforms; update the
validation around the variable value (the enabled_platforms parsing branch that
checks `if value and value != "fabric": return value`) to allow "fabric" as a
legitimate value (or explicitly accept any known platform names including
"fabric") so that an explicit enabled_platforms="fabric" is preserved instead of
being reset to DEFAULT_ENABLED_PLATFORMS; ensure the logic that returns value
uses the same variable name `value` and that DEFAULT_ENABLED_PLATFORMS remains
only a fallback when value is empty or truly invalid.
mod/common/build.gradle-71-73 (1)

71-73: ⚠️ Potential issue | 🟡 Minor

Pin or localize the fallback esbuild invocation.

mod/common/build.gradle runs npx --yes esbuild ... when bun isn’t available (lines 71-73), which can download an unpinned esbuild and weaken build reproducibility/supply-chain safety. CI already installs Spyglass via bun install --frozen-lockfile, so this likely only affects local/other environments—still, prefer a pinned esbuild version (from mod/scripts lock/published config) or a local-only invocation (npx --no-install ... with a preinstalled node_modules/.bin/esbuild).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/build.gradle` around lines 71 - 73, The npx fallback invocation in
mod/common/build.gradle currently uses 'npx --yes esbuild ...' which can pull an
unpinned esbuild; change this to either (a) invoke a pinned version by replacing
the call with 'npx --yes esbuild@<version>' using the esbuild version from the
mod/scripts lock/published config, or (b) force a local-only invocation by
switching to 'npx --no-install esbuild' and ensure esbuild is declared/installed
in the project (node_modules/.bin/esbuild) so the local binary is used; locate
the invocation by searching for the commandLine entry containing 'npx',
'esbuild', and update accordingly so builds are reproducible and do not fetch an
arbitrary remote esbuild.
mod/scripts/mcs-spyglass-validate.mjs-30-33 (1)

30-33: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

--mc-version parsing should reject missing/flag values.

Current parsing accepts the next token blindly, so --mc-version --json <dir> treats --json as the version and changes behavior silently.

Suggested fix
     if (arg === '--mc-version') {
-      flags.mcVersion = argv[index + 1]
+      const value = argv[index + 1]
+      if (!value || value.startsWith('--')) {
+        throw new Error('Expected a value after --mc-version')
+      }
+      flags.mcVersion = value
       index += 1
       continue
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/scripts/mcs-spyglass-validate.mjs` around lines 30 - 33, The parser
currently assigns the next argv token to flags.mcVersion without validation;
update the argument-handling logic around the '--mc-version' branch to ensure
argv[index + 1] exists and does not start with '-' (i.e., is not another flag)
before setting flags.mcVersion and incrementing index; if the value is missing
or looks like a flag, emit a clear error message and exit (or return an error)
instead of silently accepting the wrong token. Use the existing symbols
flags.mcVersion and the code block handling if (arg === '--mc-version') to
locate where to add this validation.
🧹 Nitpick comments (7)
mod/common/build.gradle (1)

49-50: ⚡ Quick win

Avoid generating bundled artifacts directly into src/main/resources.

Lines 49 and 73 write build output into the source tree. Move this output under build/generated/... and add that directory as a resources source to avoid dirty working trees and accidental committed generated diffs.

Also applies to: 73-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/build.gradle` around lines 49 - 50, The build writes generated
artifacts into src/main/resources (spyglassBundledScript), which pollutes the
source tree; change the output path to under build/generated (e.g. def
spyglassBundledScript =
file("${buildDir}/generated/resources/scripts/mcs-spyglass-validate.js") and any
other similar file outputs at 73-74), and then register that directory as a
resources source by adding sourceSets.main.resources.srcDir
"${buildDir}/generated/resources" (or call resources.srcDir). Update the tasks
that produce those files to write into the new build/generated path instead of
src/main/resources so generated files stay out of VCS and the working tree.
mod/common/.gradle/architectury-cache/projectID (1)

1-1: ⚡ Quick win

Remove generated Architectury cache state from source control.

This file appears machine/generated and will cause noisy churn across environments; it should not be tracked.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/.gradle/architectury-cache/projectID` at line 1, Remove the
generated Architectury cache file "projectID" from version control and stop
tracking similar generated files: run git rm --cached for the tracked projectID
file (and commit the removal), then add the architectury cache pattern (e.g.,
.gradle/architectury-cache/ or architectury-cache/projectID) to .gitignore so
future generated cache files are not committed; commit the updated .gitignore
and verify the file no longer appears in diffs.
mod/neoforge/build.gradle (1)

7-7: Profile wiring already keeps MC↔NeoForge versions consistent; downgrade the Loom “guard” request to manifest sanity-check.

-Pmcs_profile drives both minecraft_version and neoforge_version from the same entry in mod/versions/manifest.json (via mod/scripts/apply_version.py and mod/build.gradle), and CI passes mcs_profile=${{ matrix.profile }}—so mod/neoforge/build.gradle’s version = rootProject.neoforge_version (line 7) and neoForge ...:${rootProject.neoforge_version} (line 25) won’t see a cross-profile/mismatched pair. The “incompatible pair” risk only exists if the manifest mapping itself is wrong; if you still want protection, add a small validation step in apply_version.py/build logic to assert the selected NeoForge version matches the selected Minecraft version for each profile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/neoforge/build.gradle` at line 7, The current guard in
mod/neoforge/build.gradle that sets version = rootProject.neoforge_version and
uses neoForge ...:${rootProject.neoforge_version} is redundant because
-Pmcs_profile already wires minecraft_version and neoforge_version via
mod/scripts/apply_version.py; change the Loom “guard” approach to a manifest
sanity-check by adding a small assertion in apply_version.py (or equivalent
build logic) that verifies the selected NeoForge version matches the selected
Minecraft version for the given profile, and remove/relax the cross-profile
incompatibility enforcement in build.gradle (keep version =
rootProject.neoforge_version for normal wiring but rely on the manifest
validation to catch mismatches).

Source: Pipeline failures

mod/scripts/package.json (1)

8-11: 💤 Low value

Consider pinning Spyglass dependency versions for reproducible builds.

The Spyglass dependencies use caret ranges (^), which automatically update to newer minor and patch versions. This can lead to non-reproducible builds and potential breakage from upstream changes.

For critical tooling dependencies, consider using exact versions or tilde ranges (~) that only allow patch updates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/scripts/package.json` around lines 8 - 11, The dependencies in
package.json currently use caret ranges which allow minor/patch upgrades; update
the entries for "`@spyglassmc/core`", "`@spyglassmc/java-edition`", and
"`@spyglassmc/mcdoc`" to pinned versions (remove the leading ^ to use exact
versions like "0.4.47", "0.3.60", "0.3.51") or switch to tilde ranges if you
only want patch updates (e.g. "~0.4.47") so builds become reproducible and less
susceptible to upstream minor changes.
mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java (2)

47-47: ⚡ Quick win

Consider catching more specific exception types.

The code catches generic Exception in three locations (lint, compile, validateWithSpyglass). While this ensures all errors become diagnostics, catching more specific types like IOException or InterruptedException would make error handling more precise and avoid masking unexpected runtime exceptions.

Also applies to: 78-78, 109-109

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java`
at line 47, In McsToolchain, replace the generic catch (Exception error) blocks
in the lint, compile, and validateWithSpyglass flows with specific exception
types that the called APIs throw (e.g., IOException, InterruptedException,
ExecutionException, etc.) by inspecting the methods invoked inside those try
blocks and catching those concrete exceptions (for example catch (IOException |
InterruptedException e) { ... }) while preserving the current diagnostic
creation logic; if you need a safety net, add a final catch for RuntimeException
that rethrows (or logs with explicit rethrow) to avoid silently masking
unexpected runtime errors.

128-133: ⚖️ Poor tradeoff

Loading entire directory into memory may cause issues with large directories.

At line 130, stream.toList() loads all directory entries into memory before iterating. For directories with many files, this could cause unnecessary memory pressure.

♻️ Proposed fix to stream children directly
         if (Files.isDirectory(path)) {
-            try (var stream = Files.list(path)) {
-                for (Path child : stream.toList()) {
-                    deleteRecursive(child);
-                }
-            }
+            try (var stream = Files.list(path)) {
+                stream.forEach(child -> {
+                    try {
+                        deleteRecursive(child);
+                    } catch (IOException e) {
+                        throw new java.io.UncheckedIOException(e);
+                    }
+                });
+            } catch (java.io.UncheckedIOException e) {
+                throw e.getCause();
+            }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java`
around lines 128 - 133, The current code calls Files.list(path) and then
stream.toList(), which materializes the entire directory into memory; replace
that with streaming iteration to avoid loading all entries at once — inside the
try (var stream = Files.list(path)) block, iterate the stream directly (e.g.,
stream.forEach(...) or stream.forEachOrdered(...)) and call
deleteRecursive(child) for each Path instead of using stream.toList(); keep the
try-with-resources and ensure deleteRecursive is invoked for every child.
mod/common/src/main/resources/scripts/mcs-compile.cmd (1)

19-24: ⚖️ Poor tradeoff

Hardcoded Python search paths only check C:\Python3xx.

The script only searches for Python installations at C:\Python313 through C:\Python310. This misses common installation locations like:

  • Python installed via Windows Store (in %LOCALAPPDATA%\Microsoft\WindowsApps\)
  • Python in %LOCALAPPDATA%\Programs\Python\
  • Python in %PROGRAMFILES%\Python3xx

While not critical since the script already tries python and py commands first, adding these paths would improve robustness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/common/src/main/resources/scripts/mcs-compile.cmd` around lines 19 - 24,
The loop that searches for Python installs (for %%P in (313 312 311 310) do ...)
only checks C:\Python3xx folders; update the search to also probe common install
locations and call the existing :run_python label with any found executable:
check %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe,
%LOCALAPPDATA%\Programs\Python\Python%P%\python.exe and
%PROGRAMFILES%\Python%P%\python.exe (or equivalent variant patterns) in addition
to the C:\Python%P\python.exe entries so the script will find Store, per-user,
and Program Files installations before/alongside exiting with the same
!ERRORLEVEL! behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/mod.yml:
- Around line 24-29: Update the GitHub Actions step entries to pin the external
actions to immutable commit SHAs and disable credential persistence for
checkout: replace the mutable tags for actions/checkout, actions/setup-java, and
oven-sh/setup-bun with their corresponding full commit SHAs (instead of `@v4/`@v2)
and add with: persist-credentials: false to the actions/checkout step (keeping
the existing java setup keys distribution and java-version intact); ensure the
keyed names actions/checkout, actions/setup-java, and oven-sh/setup-bun are the
ones you edit so the workflow references the exact SHAs and checkout no longer
persists credentials.

In
`@mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java`:
- Around line 35-37: The save method in ModConfigManager currently writes
directly to McsPaths.configFile() which can leave a truncated/corrupted file on
interruption; change ModConfigManager.save(ModConfig) to write the JSON to a
temporary file in the same directory (use GSON.toJson(config) output),
fsync/close it, then atomically replace the target using Files.move(tempPath,
McsPaths.configFile(), StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING); ensure the parent directory exists
(Files.createDirectories(...)) and preserve the method's IOException behavior so
callers can handle failures.

In
`@mod/common/src/main/java/dev/spyc0der77/mcspacks/deploy/DatapackDeployer.java`:
- Around line 22-26: DatapackDeployer.syncPack currently deletes the existing
target before attempting to copy from compiledPack, risking data loss if
compiledPack is missing or not a directory; update syncPack to validate the
source first (e.g., Files.exists(compiledPack) &&
Files.isDirectory(compiledPack) and optionally Files.isReadable(compiledPack))
and only proceed to deleteRecursive(target) and call copyRecursive(compiledPack,
target) when validation passes; if validation fails, throw an IOException or
return an error so the old target is preserved.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/pipeline/PackPipeline.java`:
- Around line 79-100: Resolve the PackDefinition and increment the generation
counter at enqueue time inside handlePackChange (before executor.submit) so
stale queued tasks can be dropped: call registry.resolvePack(packFolder)
synchronously, if null return, then compute long generation =
generations.merge(pack.id(), 1L, Long::sum), then submit a task that calls
processPack(server, pack, true, generation). Change processPack signature to
accept the passed generation (remove its internal generations.merge call) and
ensure runToolchain(pack, generation) uses that parameter; update any other
callers of processPack accordingly.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java`:
- Around line 57-63: The compiled output path is using displayName which can
collide; update the PackRegistry creation so the compiled folder uses the stable
pack id instead of displayName: change the SafePaths.resolveChild call in the
PackRegistry code that constructs the PackDefinition (the call currently passing
displayName) to pass the pack's stable id (the id variable/identifier used to
create the PackDefinition) so each pack's compiledRoot subfolder is unique.

In
`@mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java`:
- Around line 13-15: The static cache fields cachedKey and cachedCommand in
CompilerResolver are not thread-safe; mark them volatile and guard the
read-modify-write logic that updates them with synchronization (e.g., a private
static final Object lock or synchronizing on CompilerResolver.class) inside the
resolver method so concurrent threads cannot observe stale or partially-updated
state; alternatively replace the pair with atomic holders (AtomicReference) and
perform compare-and-set updates. Ensure all accesses (reads and writes) to
cachedKey and cachedCommand use the chosen synchronization/atomic mechanism so
the cache is consistent across threads.
- Around line 119-124: The canRun method swallows interrupts from
SubprocessRunner.run; change CompilerResolver.canRun to catch
InterruptedException separately, call Thread.currentThread().interrupt() to
restore the interrupt flag, and then return false (preserving the original
failure behavior), while keeping the existing catch for other Exceptions for the
normal false return; reference the canRun method and the call to
SubprocessRunner.run(...) so you locate where to add the specific
InterruptedException handler.

In
`@mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java`:
- Around line 23-27: The current waitFor call in SubprocessRunner (method using
process.waitFor(timeoutSeconds, TimeUnit.SECONDS)) can be interrupted and return
early without terminating the child process; modify the method to catch
InterruptedException, call process.destroyForcibly() and then call
process.waitFor() to ensure the child is reaped, restore the thread interrupt
status (Thread.currentThread().interrupt()), and return an appropriate
ProcessResult indicating interruption; also ensure any normal timeout path still
destroys the process and reaps it before returning.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java`:
- Around line 50-53: The resource lookup in McsPaths hardcodes
"/scripts/mcs-spyglass-validate.js" while the shipped asset is
"mcs-spyglass-validate.mjs", causing getResourceAsStream to return null; update
the code in McsPaths so the script filename used for scriptsDir.resolve and the
getResourceAsStream call match the actual asset name (preferably
"mcs-spyglass-validate.mjs"), or implement a small fallback that first tries
"/scripts/mcs-spyglass-validate.mjs" and if null tries
"/scripts/mcs-spyglass-validate.js" before throwing the IOException so
validation can find the bundled script.
- Around line 34-45: McsPaths.compilerWrapper() currently always extracts
mcs-compile.cmd which will be executed directly on non-Windows and fail; change
the method to detect the OS (use the same isWindows() logic as CompilerResolver
or System.getProperty("os.name") check) and choose the appropriate bundled
script resource (e.g., "/scripts/mcs-compile.cmd" for Windows and
"/scripts/mcs-compile.sh" for Unix), extract the selected resource to the
scriptsDir and return that Path, and if the expected resource is missing throw
an IOException with a clear message; alternatively, if you must only support
Windows, gate extraction by returning null or throwing when not Windows so
CompilerResolver won't try to run a .cmd on Unix.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/util/PlayerFeedback.java`:
- Around line 12-21: PlayerFeedback currently calls
server.getPlayerList().broadcastSystemMessage(...) directly from
broadcast/diagnostics which can run off the server thread; change both
broadcast(MinecraftServer, String) and diagnostics(...) to dispatch the actual
broadcast onto the server thread by using server.submit(...) (i.e., inside
broadcast, wrap the creation of Component and the call to
server.getPlayerList().broadcastSystemMessage(...) in a server.submit runnable
so all calls from diagnostics and messenger execute on the server thread).

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/version/VersionMapper.java`:
- Around line 25-27: VersionMapper.load() currently constructs a VersionMapper
from Index without validating that Index.supported and Index.profiles are
non-null and non-empty; add checks after deserializing the Index (the Index
class and its fields supported and profiles) to validate they are present and
the expected types/structures, and if either is null or invalid throw a clear,
specific exception (e.g., IllegalStateException or a custom ConfigException)
with a message referencing /mcs-versions.json so resolveMcsProfile() (which
expects profiles.containsKey(...) and supported.contains(...)) cannot later NPE
— update the load() method to perform these validations and fail fast with that
clear error.

In `@mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/PackWatcher.java`:
- Around line 149-156: The watcher currently ignores deleted files because it
skips non-existent entries and path resolution can return null for
McsPaths.DEFAULT_ENTRY, so removals never trigger schedule(packFolder); update
the loop that inspects entries (the block using Files.exists(entry),
Files.getLastModifiedTime(entry), lastModified map and schedule(packFolder)) to
detect deletions by treating a previously-known path that now does not exist as
a change (i.e., if Files.exists(entry) is false and
lastModified.containsKey(entry) then remove its key and call
schedule(packFolder)); additionally, when resolving the default entry (where
McsPaths.DEFAULT_ENTRY may be missing and the resolver returns null), fall back
to scanning remaining entries or unconditionally schedule the packFolder when
resolution returns null so deletions of the default entry still trigger an
update.

In `@mod/fabric/src/main/resources/fabric.mod.json`:
- Around line 17-18: Replace the wildcard "*" in the depends entries for
architectury and fabric-api with explicit version ranges (e.g., a minimum or
ranged constraint such as "[0,)" or your project's supported minimum like
"[<min-version>,)"); update the depends.architectury and depends.fabric-api
values in fabric.mod.json accordingly so they no longer accept any version at
runtime.

In `@mod/forge/build.gradle`:
- Line 26: The Forge build fails because mod/forge/build.gradle always requests
dev.architectury:architectury-forge:${rootProject.architectury_api_version} even
for versions (13.0.8) where that artifact doesn't exist; update the Forge build
to choose the correct Architectury artifact for Forge (e.g., use
dev.architectury:architectury-neoforge:${rootProject.architectury_api_version}
or map 13.0.8 -> architectury-neoforge) instead of architectury-forge; modify
the statement referenced by modImplementation
"dev.architectury:architectury-forge:${rootProject.architectury_api_version}" to
conditionally select the appropriate artifact (or add a small mapping for
architectury_api_version == "13.0.8") so CI Forge matrix builds resolve
successfully.

In `@mod/forge/src/main/resources/META-INF/mods.toml`:
- Line 2: The loaderVersion and dependency versionRange entries are open-ended;
update the loaderVersion key and the dependency versionRange values (the strings
currently set to "[4,)" and "[${forge_version},)", "[${minecraft_version},)",
"[0,)") to include an exclusive upper bound that matches the next incompatible
version you’ve tested (e.g., change "[4,)" → "[4,X)" and "[${forge_version},)" →
"[${forge_version},${next_incompatible})", similarly for
"[${minecraft_version},)" and the Architectury "[0,)" entry) so the mods.toml
loaderVersion and versionRange fields enforce a concrete tested upper bound
rather than being unbounded.

In `@mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml`:
- Around line 27-31: The Architectury dependency in neoforge.mods.toml uses an
open versionRange "[0,)" for modId "architectury" and the build step doesn't
expand an architectury_api_version property; tighten the versionRange to the
intended constrained range (replace "[0,)" with the specific range or property
placeholder like "${architectury_api_version}" you want to enforce) and add the
matching property expansion for architectury_api_version to the processResources
expand map in build.gradle so the TOML receives the concrete version at build
time; update the TOML entry for modId "architectury" and the processResources
expand configuration accordingly.

In `@mod/scripts/mcs-spyglass-validate.mjs`:
- Around line 167-171: The deduplication Map uses a key that omits severity so a
warning can overwrite an error and mask failures; include the severity field in
the dedupe key (e.g., use
`${diagnostic.file}:${diagnostic.line}:${diagnostic.column}:${diagnostic.severity
|| diagnostic.level}:${diagnostic.message}`) when building the Map in the loop
that creates `unique` and in the similar code at the later duplicate block
(lines around where `unique.set` is called again), so errors are not lost and
exit code logic sees the correct highest-severity diagnostic.

In `@mod/versions/manifest.json`:
- Line 11: Summary: The neoforge_version pin is for the wrong NeoForge series
(21.1.80 targets MC 1.21.1) causing CI to resolve incorrect mappings. Fix:
update the "neoforge_version" entry currently set to "21.1.80" to a NeoForge
21.2.x release that targets Minecraft 1.21.2 (for example change the value to a
21.2.x tag such as "21.2.0" or the latest 21.2.* available) so the profile
correctly maps to MC 1.21.2.
- Line 22: The manifest pins architectury_api_version to values (the
"architectury_api_version" key in mod/versions/manifest.json) that don't exist
in the Architectury Maven repo and the Forge build pulls that value via the
modImplementation line in mod/forge/build.gradle (modImplementation
"dev.architectury:architectury-forge:${rootProject.architectury_api_version}"),
causing 404s; fix by updating manifest.json to a valid Architectury release that
exists in https://maven.architectury.dev (or change the dependency coordinate in
mod/forge/build.gradle to the correct group/artifact if the project moved),
verify the chosen version/artifact resolves in CI, and run a gradle dependency
resolve to confirm no 404s before merging.

---

Outside diff comments:
In `@mod/common/src/main/resources/scripts/mcs-compile.cmd`:
- Around line 1-62: The batch file uses LF-only line endings which can break
Windows CMD label parsing (affecting sections like :run_mcs, :run_python,
:run_py and the main search/exit flow), so convert this file to CRLF line
endings (e.g., run unix2dos/dos2unix -w, set git core.autocrlf=true and
re-checkout, or add a .gitattributes entry like "*.cmd text eol=crlf" and
re-checkout) and re-run tests to ensure GOTO/CALL labels and %ERRORLEVEL%
propagation behave correctly.

In `@mod/gradlew.bat`:
- Around line 1-95: Convert this batch wrapper to CRLF line endings (currently
LF-only) so Windows cmd labels and parsing work reliably: open the gradlew.bat
file and save/commit it with CRLF line endings, ensuring you do not alter any
labels or logic (refer to labels like :execute, :fail, :mainEnd to verify
content integrity), and add or update repository settings to enforce CRLF for
.bat files (e.g., add a .gitattributes entry like "*.bat text eol=crlf" or set
core.autocrlf appropriately) so future commits preserve CRLF.

---

Minor comments:
In `@minecraft_script/shell_commands.py`:
- Around line 44-47: The code currently treats unsupported long flags as
positional arguments in the argument-parsing loop (see the handling of arg,
supported_flags, positional and index), causing misrouting; change this so that
when arg.startswith("--") and arg is not in supported_flags you raise/return an
explicit error (e.g., raise ValueError or call parser.error) instead of
appending to positional, and ensure index is still advanced; update the branch
in the parser loop that checks arg.startswith("--") to validate against
supported_flags and reject unknown flags with a clear message referencing the
flag.

In `@mod/common/build.gradle`:
- Around line 71-73: The npx fallback invocation in mod/common/build.gradle
currently uses 'npx --yes esbuild ...' which can pull an unpinned esbuild;
change this to either (a) invoke a pinned version by replacing the call with
'npx --yes esbuild@<version>' using the esbuild version from the mod/scripts
lock/published config, or (b) force a local-only invocation by switching to 'npx
--no-install esbuild' and ensure esbuild is declared/installed in the project
(node_modules/.bin/esbuild) so the local binary is used; locate the invocation
by searching for the commandLine entry containing 'npx', 'esbuild', and update
accordingly so builds are reproducible and do not fetch an arbitrary remote
esbuild.

In `@mod/scripts/apply_version.py`:
- Around line 22-24: The code in apply_version.py wrongly treats the string
"fabric" as invalid and falls back to DEFAULT_ENABLED_PLATFORMS when rewriting
enabled_platforms; update the validation around the variable value (the
enabled_platforms parsing branch that checks `if value and value != "fabric":
return value`) to allow "fabric" as a legitimate value (or explicitly accept any
known platform names including "fabric") so that an explicit
enabled_platforms="fabric" is preserved instead of being reset to
DEFAULT_ENABLED_PLATFORMS; ensure the logic that returns value uses the same
variable name `value` and that DEFAULT_ENABLED_PLATFORMS remains only a fallback
when value is empty or truly invalid.

In `@mod/scripts/mcs-spyglass-validate.mjs`:
- Around line 30-33: The parser currently assigns the next argv token to
flags.mcVersion without validation; update the argument-handling logic around
the '--mc-version' branch to ensure argv[index + 1] exists and does not start
with '-' (i.e., is not another flag) before setting flags.mcVersion and
incrementing index; if the value is missing or looks like a flag, emit a clear
error message and exit (or return an error) instead of silently accepting the
wrong token. Use the existing symbols flags.mcVersion and the code block
handling if (arg === '--mc-version') to locate where to add this validation.

In `@tests/test_compile_cli_flags.py`:
- Around line 69-92: The test test_compile_mc_version_does_not_mutate_config
currently only checks the in-process COMMON_CONFIG after calling subprocess.run,
which won't catch writes to the on-disk config; update the test to, after the
subprocess.run completes, reload the persisted config file (e.g., read and parse
the same config.json the application uses) and assert that the persisted
"minecraft_version" equals original_version (in addition to the existing
in-memory assertion). Use the same symbols from the diff
(test_compile_mc_version_does_not_mutate_config, COMMON_CONFIG, and the
subprocess.run invocation) to locate where to add the file-read/parse and
assertion, and ensure cleanup still happens in the finally block.

In `@todo.md`:
- Line 69: Update the markdown to satisfy markdownlint: add language identifiers
to the two fenced code blocks (the triple-backtick blocks referenced) so they
become e.g. ```bash or ```json as appropriate, and ensure the table near the
noted section has a blank line before and after it (insert an empty line above
the table header and one below the table) to fix table formatting violations.

---

Nitpick comments:
In `@mod/common/.gradle/architectury-cache/projectID`:
- Line 1: Remove the generated Architectury cache file "projectID" from version
control and stop tracking similar generated files: run git rm --cached for the
tracked projectID file (and commit the removal), then add the architectury cache
pattern (e.g., .gradle/architectury-cache/ or architectury-cache/projectID) to
.gitignore so future generated cache files are not committed; commit the updated
.gitignore and verify the file no longer appears in diffs.

In `@mod/common/build.gradle`:
- Around line 49-50: The build writes generated artifacts into
src/main/resources (spyglassBundledScript), which pollutes the source tree;
change the output path to under build/generated (e.g. def spyglassBundledScript
= file("${buildDir}/generated/resources/scripts/mcs-spyglass-validate.js") and
any other similar file outputs at 73-74), and then register that directory as a
resources source by adding sourceSets.main.resources.srcDir
"${buildDir}/generated/resources" (or call resources.srcDir). Update the tasks
that produce those files to write into the new build/generated path instead of
src/main/resources so generated files stay out of VCS and the working tree.

In
`@mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java`:
- Line 47: In McsToolchain, replace the generic catch (Exception error) blocks
in the lint, compile, and validateWithSpyglass flows with specific exception
types that the called APIs throw (e.g., IOException, InterruptedException,
ExecutionException, etc.) by inspecting the methods invoked inside those try
blocks and catching those concrete exceptions (for example catch (IOException |
InterruptedException e) { ... }) while preserving the current diagnostic
creation logic; if you need a safety net, add a final catch for RuntimeException
that rethrows (or logs with explicit rethrow) to avoid silently masking
unexpected runtime errors.
- Around line 128-133: The current code calls Files.list(path) and then
stream.toList(), which materializes the entire directory into memory; replace
that with streaming iteration to avoid loading all entries at once — inside the
try (var stream = Files.list(path)) block, iterate the stream directly (e.g.,
stream.forEach(...) or stream.forEachOrdered(...)) and call
deleteRecursive(child) for each Path instead of using stream.toList(); keep the
try-with-resources and ensure deleteRecursive is invoked for every child.

In `@mod/common/src/main/resources/scripts/mcs-compile.cmd`:
- Around line 19-24: The loop that searches for Python installs (for %%P in (313
312 311 310) do ...) only checks C:\Python3xx folders; update the search to also
probe common install locations and call the existing :run_python label with any
found executable: check %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe,
%LOCALAPPDATA%\Programs\Python\Python%P%\python.exe and
%PROGRAMFILES%\Python%P%\python.exe (or equivalent variant patterns) in addition
to the C:\Python%P\python.exe entries so the script will find Store, per-user,
and Program Files installations before/alongside exiting with the same
!ERRORLEVEL! behavior.

In `@mod/neoforge/build.gradle`:
- Line 7: The current guard in mod/neoforge/build.gradle that sets version =
rootProject.neoforge_version and uses neoForge
...:${rootProject.neoforge_version} is redundant because -Pmcs_profile already
wires minecraft_version and neoforge_version via mod/scripts/apply_version.py;
change the Loom “guard” approach to a manifest sanity-check by adding a small
assertion in apply_version.py (or equivalent build logic) that verifies the
selected NeoForge version matches the selected Minecraft version for the given
profile, and remove/relax the cross-profile incompatibility enforcement in
build.gradle (keep version = rootProject.neoforge_version for normal wiring but
rely on the manifest validation to catch mismatches).

In `@mod/scripts/package.json`:
- Around line 8-11: The dependencies in package.json currently use caret ranges
which allow minor/patch upgrades; update the entries for "`@spyglassmc/core`",
"`@spyglassmc/java-edition`", and "`@spyglassmc/mcdoc`" to pinned versions (remove
the leading ^ to use exact versions like "0.4.47", "0.3.60", "0.3.51") or switch
to tilde ranges if you only want patch updates (e.g. "~0.4.47") so builds become
reproducible and less susceptible to upstream minor changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a02c9e5-435e-4460-a595-2c034e241828

📥 Commits

Reviewing files that changed from the base of the PR and between 9ab99ad and f1bbb62.

⛔ Files ignored due to path filters (4)
  • mod/common/.gradle/.architectury-transformer/debug.log is excluded by !**/*.log
  • mod/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.class is excluded by !**/*.class
  • mod/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
  • mod/scripts/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (56)
  • .github/workflows/mod.yml
  • .gitignore
  • minecraft_script/compiler/build_templates/builtins/1.21.2/log.mcfunction
  • minecraft_script/compiler/compile_interpreter.py
  • minecraft_script/config_utils.py
  • minecraft_script/shell_commands.py
  • mod/README.md
  • mod/build.gradle
  • mod/common/.gradle/architectury-cache/projectID
  • mod/common/build.gradle
  • mod/common/src/legacy/java/dev/spyc0der77/mcspacks/deploy/CommandSources.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/McsPacks.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfig.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/deploy/DatapackDeployer.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/pipeline/PackPipeline.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackDefinition.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/Diagnostic.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/ProcessResult.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/PlayerFeedback.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/SafePaths.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/version/VersionMapper.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/PackWatcher.java
  • mod/common/src/main/resources/scripts/mcs-compile.cmd
  • mod/common/src/main/resources/scripts/mcs-spyglass-validate.js
  • mod/common/src/modern/java/dev/spyc0der77/mcspacks/deploy/CommandSources.java
  • mod/fabric/build.gradle
  • mod/fabric/gradle.properties
  • mod/fabric/src/main/java/dev/spyc0der77/mcspacks/fabric/McsPacksFabric.java
  • mod/fabric/src/main/resources/fabric.mod.json
  • mod/forge/build.gradle
  • mod/forge/gradle.properties
  • mod/forge/src/main/java/dev/spyc0der77/mcspacks/forge/McsPacksForge.java
  • mod/forge/src/main/resources/META-INF/mods.toml
  • mod/forge/src/main/resources/mcs_packs.mixins.json
  • mod/gradle.properties
  • mod/gradle/wrapper/gradle-wrapper.properties
  • mod/gradlew
  • mod/gradlew.bat
  • mod/neoforge/build.gradle
  • mod/neoforge/gradle.properties
  • mod/neoforge/src/main/java/dev/spyc0der77/mcspacks/neoforge/McsPacksNeoForge.java
  • mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml
  • mod/scripts/apply_version.py
  • mod/scripts/build_all.py
  • mod/scripts/mcs-spyglass-validate.mjs
  • mod/scripts/package.json
  • mod/settings.gradle
  • mod/versions/manifest.json
  • tests/test_compile_cli_flags.py
  • todo.md

Comment thread .github/workflows/mod.yml Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java Outdated
Comment thread mod/forge/src/main/resources/META-INF/mods.toml
Comment thread mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml
Comment thread mod/scripts/mcs-spyglass-validate.mjs
Comment thread mod/versions/manifest.json Outdated
Comment thread mod/versions/manifest.json

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 issues found across 31 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="mod/common/src/main/resources/scripts/mcs-compile.cmd">

<violation number="1" location="mod/common/src/main/resources/scripts/mcs-compile.cmd:25">
P2: The new WindowsApps `python.exe` check can call the Microsoft Store alias and exit early, skipping working `py`/`python` fallbacks.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java Outdated
Comment thread mod/forge/src/main/resources/META-INF/mods.toml Outdated
Comment thread mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml Outdated
Comment thread mod/neoforge/build.gradle Outdated
Comment thread mod/neoforge/build.gradle Outdated
Comment thread mod/gradle.properties Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java Outdated
Comment on lines +25 to +28
if exist "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" (
call :run_python "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" %*
exit /b !ERRORLEVEL!
)

@cubic-dev-ai cubic-dev-ai Bot Jun 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new WindowsApps python.exe check can call the Microsoft Store alias and exit early, skipping working py/python fallbacks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mod/common/src/main/resources/scripts/mcs-compile.cmd, line 25:

<comment>The new WindowsApps `python.exe` check can call the Microsoft Store alias and exit early, skipping working `py`/`python` fallbacks.</comment>

<file context>
@@ -1,61 +1,73 @@
+        exit /b !ERRORLEVEL!
+    )
+)
+if exist "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" (
+    call :run_python "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" %*
+    exit /b !ERRORLEVEL!
</file context>
Suggested change
if exist "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" (
call :run_python "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" %*
exit /b !ERRORLEVEL!
)
rem Skip direct WindowsApps python.exe invocation; rely on where python / py checks below.
Fix with cubic

Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java Outdated
Comment thread mod/common/build.gradle Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
mod/neoforge/build.gradle (1)

47-70: 💤 Low value

Version parsing assumptions may not handle all formats.

The version increment logic assumes specific formats (modern: 2[6-9].x, release: x.y.z, NeoForge/Architectury: x.y.z) and silently falls back to returning the original version if the pattern doesn't match. This approach may not handle:

  • Pre-release versions (e.g., 1.21.2-rc1, 21.1.0-alpha)
  • Snapshot versions
  • Other non-standard version formats

While the current fallback behavior is reasonable, consider adding validation or logging to make mismatches visible during development, especially given the critical range limitation in the modern pattern (lines 49).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/neoforge/build.gradle` around lines 47 - 70, The version increment
closures nextMinecraftVersion, nextNeoForgeVersion and nextArchitecturyVersion
currently assume strict regex formats and silently return the original version
on mismatch; update them to validate inputs and surface mismatches (e.g., with
logger.warn or throw during CI) and broaden parsing to recognize common
pre-release/snapshot suffixes (like -rc, -alpha, -snapshot) by either stripping
suffixes before applying the existing regex or adding alternative regex
branches; ensure each closure logs the original version and the reason for
fallback so non-standard formats are visible during development.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mod/neoforge/build.gradle`:
- Line 49: The hardcoded regex for `modern` only matches majors 26–29; update
the pattern assigned to `modern` (the `def modern = ...` line) to accept major
versions >=26 (including 30+), e.g. replace `/^2[6-9]\.(\d+)$/` with a regex
like `/^(?:2[6-9]|[3-9]\d*)\.(\d+)$/` so the minor capture group remains the
same and future major versions (30, 31, ...) are treated as modern.

---

Nitpick comments:
In `@mod/neoforge/build.gradle`:
- Around line 47-70: The version increment closures nextMinecraftVersion,
nextNeoForgeVersion and nextArchitecturyVersion currently assume strict regex
formats and silently return the original version on mismatch; update them to
validate inputs and surface mismatches (e.g., with logger.warn or throw during
CI) and broaden parsing to recognize common pre-release/snapshot suffixes (like
-rc, -alpha, -snapshot) by either stripping suffixes before applying the
existing regex or adding alternative regex branches; ensure each closure logs
the original version and the reason for fallback so non-standard formats are
visible during development.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73c3c437-db7a-4a4d-af20-b0f1c6924b76

📥 Commits

Reviewing files that changed from the base of the PR and between f1bbb62 and 55623a9.

📒 Files selected for processing (30)
  • .gitattributes
  • .github/workflows/mod.yml
  • .gitignore
  • minecraft_script/shell_commands.py
  • mod/common/build.gradle
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/deploy/DatapackDeployer.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/pipeline/PackPipeline.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/PlayerFeedback.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/version/VersionMapper.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/PackWatcher.java
  • mod/common/src/main/resources/scripts/mcs-compile.cmd
  • mod/common/src/main/resources/scripts/mcs-compile.sh
  • mod/fabric/build.gradle
  • mod/fabric/src/main/resources/fabric.mod.json
  • mod/forge/build.gradle
  • mod/forge/src/main/resources/META-INF/mods.toml
  • mod/gradle.properties
  • mod/neoforge/build.gradle
  • mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml
  • mod/scripts/apply_version.py
  • mod/scripts/mcs-spyglass-validate.mjs
  • mod/scripts/package.json
  • mod/versions/manifest.json
  • tests/test_compile_cli_flags.py
✅ Files skipped from review due to trivial changes (4)
  • .gitattributes
  • mod/forge/src/main/resources/META-INF/mods.toml
  • mod/fabric/src/main/resources/fabric.mod.json
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (20)
  • mod/gradle.properties
  • mod/scripts/package.json
  • mod/forge/build.gradle
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/config/ModConfigManager.java
  • .github/workflows/mod.yml
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java
  • mod/neoforge/src/main/resources/META-INF/neoforge.mods.toml
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/PlayerFeedback.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java
  • tests/test_compile_cli_flags.py
  • mod/fabric/build.gradle
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/util/McsPaths.java
  • minecraft_script/shell_commands.py
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/deploy/DatapackDeployer.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/watch/PackWatcher.java
  • mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java
  • mod/scripts/mcs-spyglass-validate.mjs
  • mod/common/build.gradle
  • mod/versions/manifest.json

Comment thread mod/neoforge/build.gradle Outdated
- Modified `gradle.properties` to enable support for multiple platforms: fabric, forge, and neoforge.
- Updated `build.gradle` scripts to streamline version management for Forge and NeoForge.
- Enhanced `ModConfigManager` to handle atomic file moves with fallback for unsupported operations.
- Refactored `mcs-compile` scripts for improved Python version detection and execution.
- Cleaned up unnecessary candidate paths in `CompilerResolver` and improved error handling in `McsPaths`.
- Adjusted loader version constraints in `mods.toml` files for Forge and NeoForge.
- Introduced `stripPreReleaseSuffix` and `warnVersionFallback` functions to improve version handling.
- Updated `nextMinecraftVersion` and `nextMajorVersion` functions to utilize the new helper methods for better version normalization and fallback warnings.
- Enhanced version retrieval for `forge_version` and `neoforge_version` to include fallback handling for unrecognized formats.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 31 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="mod/common/src/main/resources/scripts/mcs-compile.cmd">

<violation number="1" location="mod/common/src/main/resources/scripts/mcs-compile.cmd:25">
P2: The new WindowsApps `python.exe` check can call the Microsoft Store alias and exit early, skipping working `py`/`python` fallbacks.</violation>
</file>

<file name="mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java">

<violation number="1" location="mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java:61">
P1: Compiled output folder tracking now uses pack ID, but compilation writes to display name, causing post-compile missing-folder failures.</violation>
</file>

<file name="mod/forge/build.gradle">

<violation number="1" location="mod/forge/build.gradle:26">
P1: Forge module now depends on the NeoForge Architectury artifact, which mismatches the Forge target and can break Forge runtime/build compatibility.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

id,
folder,
entry,
SafePaths.resolveChild(compiledRoot, id),

@cubic-dev-ai cubic-dev-ai Bot Jun 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Compiled output folder tracking now uses pack ID, but compilation writes to display name, causing post-compile missing-folder failures.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mod/common/src/main/java/dev/spyc0der77/mcspacks/registry/PackRegistry.java, line 61:

<comment>Compiled output folder tracking now uses pack ID, but compilation writes to display name, causing post-compile missing-folder failures.</comment>

<file context>
@@ -58,7 +58,7 @@ public PackDefinition resolvePack(Path folder) throws IOException {
                 folder,
                 entry,
-                SafePaths.resolveChild(compiledRoot, displayName),
+                SafePaths.resolveChild(compiledRoot, id),
                 displayName
         );
</file context>
Suggested change
SafePaths.resolveChild(compiledRoot, id),
SafePaths.resolveChild(compiledRoot, displayName),
Fix with cubic

Comment thread mod/forge/build.gradle

dependencies {
forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}"
modImplementation "dev.architectury:architectury-neoforge:${rootProject.architectury_api_version}"

@cubic-dev-ai cubic-dev-ai Bot Jun 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Forge module now depends on the NeoForge Architectury artifact, which mismatches the Forge target and can break Forge runtime/build compatibility.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mod/forge/build.gradle, line 26:

<comment>Forge module now depends on the NeoForge Architectury artifact, which mismatches the Forge target and can break Forge runtime/build compatibility.</comment>

<file context>
@@ -23,7 +23,7 @@ configurations {
 dependencies {
     forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}"
-    modImplementation "dev.architectury:architectury-forge:${rootProject.architectury_api_version}"
+    modImplementation "dev.architectury:architectury-neoforge:${rootProject.architectury_api_version}"
 
     common(project(path: ':common', configuration: 'namedElements')) { transitive false }
</file context>
Suggested change
modImplementation "dev.architectury:architectury-neoforge:${rootProject.architectury_api_version}"
modImplementation "dev.architectury:architectury-forge:${rootProject.architectury_api_version}"
Fix with cubic

Comment thread mod/scripts/mcs-spyglass-validate.mjs
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/McsToolchain.java Outdated
Comment thread mod/neoforge/build.gradle Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/SubprocessRunner.java Outdated
Comment thread mod/common/src/main/java/dev/spyc0der77/mcspacks/toolchain/CompilerResolver.java Outdated
- Modified GitHub Actions workflow to dynamically set the Java version based on the profile, allowing for better compatibility with different Minecraft versions.
- Enhanced `build.gradle` files to conditionally set the required Java version based on the Minecraft version, improving version management.
- Updated `McsToolchain` to use the pack ID instead of display name for better clarity.
- Refactored Python version detection in `CompilerResolver` to handle version formatting more robustly.
- Improved error handling in `mcs-spyglass-validate.mjs` to ensure required arguments are provided.
- Updated Gradle wrapper to version 8.14.5 for improved build performance and compatibility.
- Deleted the `mcs-spyglass-validate.js` script as part of the ongoing cleanup and consolidation of validation functionality.
}

if (positional.length < 1) {
throw new Error('Usage: node mcs-spyglass-validate.js [--json] [--mc-version <version>] <datapack_dir>')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Usage message references wrong file extension. The error message says mcs-spyglass-validate.js but the actual file is mcs-spyglass-validate.mjs. This could confuse users when they try to run the script.

… releases

- Updated `forge_version` from 57.0.0 to 58.1.0 for game versions 1.21.8.
- Updated `forge_version` from 58.0.0 to 60.1.0 for game versions 1.21.10, ensuring support for the latest features and improvements.
@SpyC0der77 SpyC0der77 closed this Jun 10, 2026
@SpyC0der77 SpyC0der77 deleted the add-mod branch June 11, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant