modkeel 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. modkeel-0.1.1/.github/workflows/cli.yml +53 -0
  2. modkeel-0.1.1/.github/workflows/release.yml +92 -0
  3. modkeel-0.1.1/.gitignore +49 -0
  4. modkeel-0.1.1/CONTRIBUTING.md +65 -0
  5. modkeel-0.1.1/LICENSE +21 -0
  6. modkeel-0.1.1/PKG-INFO +268 -0
  7. modkeel-0.1.1/README.md +242 -0
  8. modkeel-0.1.1/demo/README.md +48 -0
  9. modkeel-0.1.1/demo/compile.gif +0 -0
  10. modkeel-0.1.1/demo/compile.tape +25 -0
  11. modkeel-0.1.1/demo/demo_repos.txt +3 -0
  12. modkeel-0.1.1/demo/get.gif +0 -0
  13. modkeel-0.1.1/demo/get.tape +18 -0
  14. modkeel-0.1.1/demo/search.gif +0 -0
  15. modkeel-0.1.1/demo/search.tape +19 -0
  16. modkeel-0.1.1/demo/status.gif +0 -0
  17. modkeel-0.1.1/demo/status.tape +20 -0
  18. modkeel-0.1.1/mod_auto_compiler.py +592 -0
  19. modkeel-0.1.1/modkeel/__init__.py +26 -0
  20. modkeel-0.1.1/modkeel/build.py +496 -0
  21. modkeel-0.1.1/modkeel/buildinfo.py +236 -0
  22. modkeel-0.1.1/modkeel/cli.py +59 -0
  23. modkeel-0.1.1/modkeel/commands/__init__.py +5 -0
  24. modkeel-0.1.1/modkeel/commands/_shared.py +158 -0
  25. modkeel-0.1.1/modkeel/commands/compile.py +290 -0
  26. modkeel-0.1.1/modkeel/commands/get.py +227 -0
  27. modkeel-0.1.1/modkeel/commands/recommend.py +264 -0
  28. modkeel-0.1.1/modkeel/commands/search.py +219 -0
  29. modkeel-0.1.1/modkeel/commands/status.py +108 -0
  30. modkeel-0.1.1/modkeel/commands/token.py +56 -0
  31. modkeel-0.1.1/modkeel/config.py +127 -0
  32. modkeel-0.1.1/modkeel/constants.py +19 -0
  33. modkeel-0.1.1/modkeel/crowdsource.py +205 -0
  34. modkeel-0.1.1/modkeel/docker.py +883 -0
  35. modkeel-0.1.1/modkeel/evidence.py +297 -0
  36. modkeel-0.1.1/modkeel/github.py +811 -0
  37. modkeel-0.1.1/modkeel/javascan.py +187 -0
  38. modkeel-0.1.1/modkeel/linkage.py +458 -0
  39. modkeel-0.1.1/modkeel/loaders.py +242 -0
  40. modkeel-0.1.1/modkeel/mappings.py +394 -0
  41. modkeel-0.1.1/modkeel/memberlink.py +410 -0
  42. modkeel-0.1.1/modkeel/mixinscan.py +536 -0
  43. modkeel-0.1.1/modkeel/models.py +271 -0
  44. modkeel-0.1.1/modkeel/modrinth.py +566 -0
  45. modkeel-0.1.1/modkeel/pipeline.py +1422 -0
  46. modkeel-0.1.1/modkeel/prebuild.py +648 -0
  47. modkeel-0.1.1/modkeel/recommend.py +346 -0
  48. modkeel-0.1.1/modkeel/relax.py +147 -0
  49. modkeel-0.1.1/modkeel/resolve.py +293 -0
  50. modkeel-0.1.1/modkeel/scanner.py +223 -0
  51. modkeel-0.1.1/modkeel/sources.py +598 -0
  52. modkeel-0.1.1/modkeel/symbols.py +398 -0
  53. modkeel-0.1.1/modkeel/target.py +249 -0
  54. modkeel-0.1.1/modkeel/utils.py +231 -0
  55. modkeel-0.1.1/modkeel/validation.py +595 -0
  56. modkeel-0.1.1/modkeel/version.py +165 -0
  57. modkeel-0.1.1/pyproject.toml +70 -0
  58. modkeel-0.1.1/repos.txt +10 -0
  59. modkeel-0.1.1/requirements.txt +7 -0
  60. modkeel-0.1.1/tests/__init__.py +0 -0
  61. modkeel-0.1.1/tests/conftest.py +13 -0
  62. modkeel-0.1.1/tests/fixtures/Sample.class +0 -0
  63. modkeel-0.1.1/tests/test_build.py +88 -0
  64. modkeel-0.1.1/tests/test_build_jars.py +31 -0
  65. modkeel-0.1.1/tests/test_buildinfo.py +261 -0
  66. modkeel-0.1.1/tests/test_cli.py +677 -0
  67. modkeel-0.1.1/tests/test_crowdsource.py +508 -0
  68. modkeel-0.1.1/tests/test_docker.py +826 -0
  69. modkeel-0.1.1/tests/test_evidence.py +286 -0
  70. modkeel-0.1.1/tests/test_linkage.py +548 -0
  71. modkeel-0.1.1/tests/test_loaders.py +248 -0
  72. modkeel-0.1.1/tests/test_matching.py +1052 -0
  73. modkeel-0.1.1/tests/test_memberlink.py +179 -0
  74. modkeel-0.1.1/tests/test_mixinscan.py +312 -0
  75. modkeel-0.1.1/tests/test_modrinth.py +85 -0
  76. modkeel-0.1.1/tests/test_pipeline.py +685 -0
  77. modkeel-0.1.1/tests/test_prebuild.py +477 -0
  78. modkeel-0.1.1/tests/test_recommend.py +327 -0
  79. modkeel-0.1.1/tests/test_relax.py +138 -0
  80. modkeel-0.1.1/tests/test_resolve.py +612 -0
  81. modkeel-0.1.1/tests/test_scanner.py +339 -0
  82. modkeel-0.1.1/tests/test_symbols.py +587 -0
  83. modkeel-0.1.1/tests/test_target.py +283 -0
@@ -0,0 +1,53 @@
1
+ # CLI checks: lint and the pytest suite on every Python version the package supports.
2
+ # Runs in the lab and, mirrored by tools/publish_cli.py, in the public Modkeel/modkeel repo,
3
+ # so it needs no secrets and only reads the repository.
4
+ name: cli
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ paths:
10
+ - "modkeel/**"
11
+ - "tests/**"
12
+ - "mod_auto_compiler.py"
13
+ - "pyproject.toml"
14
+ - ".github/workflows/cli.yml"
15
+ pull_request:
16
+ paths:
17
+ - "modkeel/**"
18
+ - "tests/**"
19
+ - "mod_auto_compiler.py"
20
+ - "pyproject.toml"
21
+ - ".github/workflows/cli.yml"
22
+ workflow_dispatch:
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ concurrency:
28
+ group: cli-${{ github.ref }}
29
+ cancel-in-progress: true
30
+
31
+ jobs:
32
+ # One job per Python version; ruff runs inside the 3.12 job instead of its own job.
33
+ # Pull requests test the oldest and newest supported Python (2 jobs); pushes to main and
34
+ # manual runs test every version. Fewer jobs per PR keeps several open PRs under the
35
+ # account's concurrent-job limit for GitHub-hosted runners.
36
+ test:
37
+ runs-on: ubuntu-latest
38
+ timeout-minutes: 10
39
+ strategy:
40
+ fail-fast: false
41
+ matrix:
42
+ python-version: ${{ fromJSON(github.event_name == 'pull_request' && '["3.10", "3.12"]' || '["3.10", "3.11", "3.12"]') }}
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - uses: actions/setup-python@v5
46
+ with:
47
+ python-version: ${{ matrix.python-version }}
48
+ cache: pip
49
+ - run: pip install -e ".[dev]"
50
+ - name: Lint
51
+ if: matrix.python-version == '3.12'
52
+ run: ruff check modkeel tests
53
+ - run: python -m pytest tests/ -q
@@ -0,0 +1,92 @@
1
+ # Release the CLI when a version tag (vX.Y.Z) reaches this repo: build the wheel and sdist
2
+ # once, then create the GitHub Release (notes = the tag's message) and upload the same files
3
+ # to PyPI. The tag is created by the maintainers' publishing job when the package version
4
+ # changes; nobody pushes it by hand.
5
+ #
6
+ # PyPI uses trusted publishing (OIDC): no token is stored. The project on pypi.org trusts
7
+ # this repository, this workflow file and the environment "pypi".
8
+ name: release
9
+
10
+ on:
11
+ push:
12
+ tags: ["v*.*.*"]
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ build:
19
+ if: github.repository == 'Modkeel/modkeel'
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 10
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ # The tag and the package must agree, or the release would ship another version.
30
+ - name: Build
31
+ env:
32
+ TAG: ${{ github.ref_name }}
33
+ run: |
34
+ pip install build twine
35
+ python -m build
36
+ version=$(python -c "import re,pathlib;print(re.search(r'MODKEEL_VERSION = \"([^\"]+)\"', pathlib.Path('modkeel/constants.py').read_text())[1])")
37
+ if [ "v$version" != "$TAG" ]; then
38
+ echo "::error::tag $TAG does not match the package version $version"
39
+ exit 1
40
+ fi
41
+ twine check --strict dist/*
42
+ ls -l dist
43
+
44
+ - uses: actions/upload-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ retention-days: 7
49
+
50
+ github-release:
51
+ needs: build
52
+ runs-on: ubuntu-latest
53
+ timeout-minutes: 5
54
+ permissions:
55
+ contents: write
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+
59
+ - uses: actions/download-artifact@v4
60
+ with:
61
+ name: dist
62
+ path: dist/
63
+
64
+ # actions/checkout leaves the tag as a plain ref; fetch the annotated tag to read its
65
+ # message: first line = title, the rest = notes.
66
+ - name: Create the release
67
+ env:
68
+ GH_TOKEN: ${{ github.token }}
69
+ TAG: ${{ github.ref_name }}
70
+ run: |
71
+ git fetch --force origin "refs/tags/$TAG:refs/tags/$TAG"
72
+ git tag -l --format='%(contents:body)' "$TAG" > notes.md
73
+ title=$(git tag -l --format='%(contents:subject)' "$TAG")
74
+ gh release create "$TAG" dist/* --verify-tag --title "${title:-$TAG}" \
75
+ --notes-file notes.md
76
+
77
+ pypi:
78
+ needs: build
79
+ runs-on: ubuntu-latest
80
+ timeout-minutes: 5
81
+ environment:
82
+ name: pypi
83
+ url: https://pypi.org/project/modkeel/
84
+ permissions:
85
+ id-token: write
86
+ steps:
87
+ - uses: actions/download-artifact@v4
88
+ with:
89
+ name: dist
90
+ path: dist/
91
+
92
+ - uses: pypa/gh-action-pypi-publish@v1.12.4
@@ -0,0 +1,49 @@
1
+ # Secrets
2
+ github_token.txt
3
+ *.token
4
+ .env
5
+ .env.*
6
+
7
+ # Python
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+ *.egg-info/
12
+ dist/
13
+ build/
14
+ *.egg
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+ *.swo
21
+ *~
22
+
23
+ # OS
24
+ .DS_Store
25
+ Thumbs.db
26
+ *:Zone.Identifier
27
+
28
+ # ModForge specific
29
+ compiled_mods/
30
+ temp_repos/
31
+ *.jar
32
+ compilation_report*.txt
33
+ out/
34
+ *.log
35
+
36
+ # Virtual environments
37
+ venv/
38
+ .venv/
39
+ env/
40
+
41
+ # Research caches
42
+ research/.cache/
43
+ research/.runtime/
44
+ research/.overnight/
45
+ research/.overnight.console
46
+
47
+ # Modpack crawl: committed gzipped (packs_*.json.gz)
48
+ research/packs_*.json
49
+ research/.aeo/
@@ -0,0 +1,65 @@
1
+ # Contributing to Modkeel
2
+
3
+ ## Development Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/Modkeel/modkeel.git
7
+ cd modkeel
8
+ pip install -e ".[dev]" # the CLI plus pytest and ruff
9
+ ```
10
+
11
+ ## Running Tests and Lint
12
+
13
+ ```bash
14
+ python3 -m pytest tests/ -q
15
+ ruff check modkeel tests
16
+ ```
17
+
18
+ Both must pass before submitting a PR. CI runs them on Python 3.10 and 3.12 for pull
19
+ requests, and on 3.10, 3.11 and 3.12 for every push to `main`
20
+ ([`.github/workflows/cli.yml`](.github/workflows/cli.yml)).
21
+
22
+ ## Project Structure
23
+
24
+ All new code goes into the `modkeel/` package. The root `mod_auto_compiler.py` is a deprecated shim -- do not modify it.
25
+
26
+ The package is composition-based: `Pipeline` (`pipeline.py`) orchestrates one client per concern.
27
+
28
+ | Module | Role |
29
+ |--------|------|
30
+ | `cli.py`, `commands/` | Typer app; one module per command: `compile`, `search`, `get`, `token`, `status`, `recommend` |
31
+ | `pipeline.py` | Clone, compile, multi-pass dependency retry, Docker test, report |
32
+ | `github.py`, `validation.py`, `buildinfo.py` | Fork discovery, branch scoring, reading build targets remotely |
33
+ | `prebuild.py`, `symbols.py`, `mappings.py`, `javascan.py` | Skip builds whose outcome is knowable before cloning |
34
+ | `linkage.py`, `memberlink.py`, `mixinscan.py` | Bytecode checks: does a JAR really target the requested version |
35
+ | `build.py` | Gradle build and JAR validation |
36
+ | `modrinth.py`, `recommend.py`, `scanner.py` | Modrinth lookups, mods-folder scanning, version/loader recommendations |
37
+ | `docker.py` | Headless server boot test |
38
+ | `loaders.py`, `version.py`, `models.py`, `config.py` | Loader data, version ranges, data classes, user config |
39
+
40
+ Tests patch `modkeel.<module>.<object>`, never the deprecated shim.
41
+
42
+ ## Code Style
43
+
44
+ - **Python 3.10+** target
45
+ - **100 character** line length
46
+ - **Type hints** on all public functions
47
+ - `snake_case` for functions/variables, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants
48
+ - All code, comments, docstrings, and error messages in **English**
49
+
50
+ ## Pull Request Guidelines
51
+
52
+ 1. Create a feature branch from `main`
53
+ 2. Use [conventional commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
54
+ 3. Ensure tests and lint pass (`pytest tests/ -q`, `ruff check modkeel tests`)
55
+ 4. Keep PRs focused -- one feature or fix per PR
56
+
57
+ ## Reporting Issues
58
+
59
+ Open an issue at [github.com/Modkeel/modkeel/issues](https://github.com/Modkeel/modkeel/issues) with:
60
+
61
+ - Modkeel version (`modkeel --version`)
62
+ - Python version
63
+ - OS and architecture
64
+ - Steps to reproduce
65
+ - Relevant log output (use `--log-file modkeel.log`)
modkeel-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Modkeel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
modkeel-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.4
2
+ Name: modkeel
3
+ Version: 0.1.1
4
+ Summary: Minecraft Mod Auto-Compiler - find, compile, and verify unofficial mod forks
5
+ Author: Modkeel
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: compiler,fabric,forge,minecraft,mods,neoforge
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Games/Entertainment
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: requests>=2.31.0
19
+ Requires-Dist: rich>=13.0.0
20
+ Requires-Dist: toml>=0.10.2
21
+ Requires-Dist: typer>=0.12.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == 'dev'
24
+ Requires-Dist: ruff<0.17,>=0.15; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Modkeel
28
+
29
+ > **Compile the mods Mojang left behind**
30
+
31
+ ![Python](https://img.shields.io/badge/python-3.10+-blue)
32
+ ![License](https://img.shields.io/badge/license-MIT-green)
33
+ [![PyPI](https://img.shields.io/pypi/v/modkeel)](https://pypi.org/project/modkeel/)
34
+ [![CI](https://github.com/Modkeel/modkeel/actions/workflows/cli.yml/badge.svg)](https://github.com/Modkeel/modkeel/actions/workflows/cli.yml)
35
+
36
+ ---
37
+
38
+ Modkeel is a CLI tool that finds, compiles, and verifies unofficial Minecraft mod forks for versions the original authors don't support. When Mojang releases a patch version (like 1.21.10), major mods skip it -- but community forks exist on GitHub as uncompiled branches. Modkeel finds them, builds them, and validates the output.
39
+
40
+ Playing the mods, not building them? The [Modkeel Companion](https://github.com/Modkeel/companion)
41
+ mod backs up your worlds when your mods change and fixes crashes in one click.
42
+
43
+ ## Quick Start
44
+
45
+ ```bash
46
+ pipx install modkeel # or: pip install modkeel
47
+ modkeel compile repos.txt -m 1.21.10 -l neoforge -lv 64 -t "$(cat github_token.txt)"
48
+ ```
49
+
50
+ ## Quick Demo
51
+
52
+ ### Search for a mod
53
+ ![modkeel search](https://raw.githubusercontent.com/Modkeel/modkeel/main/demo/search.gif)
54
+
55
+ ### Compile mods from a list
56
+ ![modkeel compile](https://raw.githubusercontent.com/Modkeel/modkeel/main/demo/compile.gif)
57
+
58
+ ### Check status
59
+ ![modkeel status](https://raw.githubusercontent.com/Modkeel/modkeel/main/demo/status.gif)
60
+
61
+ > GIFs generated with [VHS](https://github.com/charmbracelet/vhs). See [`demo/README.md`](https://github.com/Modkeel/modkeel/blob/main/demo/README.md) to regenerate.
62
+
63
+ ## How It Works
64
+
65
+ For each repository in your list, Modkeel runs:
66
+
67
+ 1. **Modrinth Check** -- Search for a pre-compiled JAR first (skip compilation if found)
68
+ 2. **Fork Discovery** -- Search GitHub for compatible forks and independent ports
69
+ 3. **Branch Scoring** -- Rank branches by version match, loader match, and freshness
70
+ 4. **Pre-validation** -- Check `gradle.properties` via GitHub API before cloning
71
+ 5. **Cross-loader Fallback** -- If no NeoForge match, try Fabric forks via Sinytra Connector
72
+ 6. **Compile** -- Clone, run `gradlew build`, validate output JAR
73
+ 7. **Multi-pass Dependencies** -- Retry failed mods with `mavenLocal()` after others succeed
74
+ 8. **Docker Testing** (opt-in) -- Boot a headless MC server to verify mods load
75
+
76
+ ## Requirements
77
+
78
+ - **Python 3.10+**
79
+ - **Git** (in PATH)
80
+ - **JDK 17 or 21** (for Gradle compilation)
81
+ - **Docker** (optional, for `--docker-test`)
82
+
83
+ ```bash
84
+ python --version # 3.10+
85
+ git --version
86
+ java -version # 17 or 21
87
+ ```
88
+
89
+ ## Installation
90
+
91
+ ```bash
92
+ pipx install modkeel # isolated install of the `modkeel` command (recommended)
93
+ pip install modkeel # or into the current environment
94
+ ```
95
+
96
+ From source, to work on Modkeel itself:
97
+
98
+ ```bash
99
+ git clone https://github.com/Modkeel/modkeel.git
100
+ cd modkeel
101
+ pip install -e ".[dev]"
102
+ ```
103
+
104
+ Releases and their notes: [GitHub Releases](https://github.com/Modkeel/modkeel/releases).
105
+
106
+ ## Usage
107
+
108
+ ### Compile Mods
109
+
110
+ ```bash
111
+ modkeel compile repos.txt \
112
+ --mc-version 1.21.10 \
113
+ --loader neoforge \
114
+ --loader-version 64 \
115
+ --github-token "$(cat github_token.txt)"
116
+ ```
117
+
118
+ ### Compile and Install to Instance
119
+
120
+ ```bash
121
+ modkeel compile repos.txt \
122
+ -m 1.21.10 -l neoforge -lv 64 \
123
+ --instance "/path/to/minecraft/instance" \
124
+ -t "$(cat github_token.txt)"
125
+ ```
126
+
127
+ ### Compile and Test in Docker
128
+
129
+ ```bash
130
+ modkeel compile repos.txt \
131
+ -m 1.21.10 -l neoforge -lv 64 \
132
+ --docker-test \
133
+ -t "$(cat github_token.txt)"
134
+ ```
135
+
136
+ ### Search Without Compiling
137
+
138
+ ```bash
139
+ modkeel search "Create" -m 1.21.10 -l neoforge -t "$(cat github_token.txt)"
140
+ ```
141
+
142
+ ### Get One Mod
143
+
144
+ ```bash
145
+ modkeel get "Create" -m 1.21.10 -l neoforge -lv 21.10.64
146
+ ```
147
+
148
+ `get` (and `search`, without downloading) tries, in order, and prints what it tried:
149
+
150
+ 1. The mod's official build for that Minecraft version.
151
+ 2. An official build for an older version of the same line that still runs on it: its
152
+ metadata must allow your version, every Minecraft class it uses must exist there, no
153
+ method or field it calls may have been removed or renamed since the version it was
154
+ built for, and its mixins must still find their targets (with matching parameters). A
155
+ static check, so test it in game (or with `--docker-test`).
156
+ 3. A community fork compiled for that version (needs a token and `-lv`).
157
+ 4. Last resort: an older official build refused only by its declared Minecraft range. Modkeel
158
+ adds your version to that range, but keeps the result only if its bytecode resolves and a
159
+ headless server boots with it (needs Docker). The file is renamed `...+modkeel-relaxed-...`
160
+ and carries `META-INF/modkeel-relaxed.txt`.
161
+
162
+ It never downloads a different mod with a similar name: addons are listed separately.
163
+
164
+ When nothing runs on your version, Modkeel looks for the nearest Minecraft version with an
165
+ official build and says so: in a terminal a 15-second countdown starts the search (Enter
166
+ starts it now, `n` stops it); without a terminal it only prints the command to run.
167
+ `--fallback auto|never` decides without asking. A result for another version goes to
168
+ `out/mc-<version>/` and is never installed into `--instance`. `compile` does the same for
169
+ the whole list (never with `--strict`): it proposes a version where more of the mods have
170
+ an official build than were built on yours. On the few versions nearest yours, a mod also
171
+ counts when its older build passes the static checks there. JARs from the first run that also pass the
172
+ static checks on the new version are copied instead of built again; mods with an official
173
+ build there are downloaded fresh.
174
+
175
+ The last lines say what the JAR passed (`Evidence: metadata ✓ · linkage ✓ · ...`). With
176
+ `--docker-test`, a headless server boots with each result before it is accepted: a JAR that
177
+ crashes it (for example a mixin whose target changed) is removed and the next candidate is
178
+ tried. Without Docker the JAR is kept and the output says the test did not run.
179
+
180
+ `compile` follows the same order for each repository, with one more step after the official
181
+ build: the author's own branch for that version, prebuilt or compiled, comes before an older
182
+ build. `--strict` never uses an older build. The report says which source each mod came from
183
+ and, for failures, everything that was tried.
184
+
185
+ ### Check Status
186
+
187
+ ```bash
188
+ modkeel status
189
+ ```
190
+
191
+ > **Legacy:** `python mod_auto_compiler.py ...` still works but is deprecated. Use `modkeel compile` instead.
192
+
193
+ ## Repository File Format
194
+
195
+ The `repos.txt` file contains one repository URL per line:
196
+
197
+ ```
198
+ # Base URL -- Modkeel finds the best branch automatically
199
+ https://github.com/Creators-of-Create/Create
200
+ https://github.com/mezz/JustEnoughItems
201
+
202
+ # Specific branch
203
+ https://github.com/PepperCode1/Continuity/tree/1.21.10/dev
204
+
205
+ # Comments start with #
206
+ ```
207
+
208
+ ## Command Reference
209
+
210
+ ### `modkeel compile`
211
+
212
+ | Option | Description | Default |
213
+ |--------|-------------|---------|
214
+ | `repos_file` | Text file with repository URLs | (required) |
215
+ | `-m`, `--mc-version` | Minecraft version | (required) |
216
+ | `-l`, `--loader` | Mod loader: `neoforge`, `forge`, `fabric` | (required) |
217
+ | `-lv`, `--loader-version` | Loader version number | (required) |
218
+ | `-i`, `--instance` | Minecraft instance path | -- |
219
+ | `-o`, `--output-dir` | Output directory | `out` |
220
+ | `-t`, `--github-token` | GitHub API token | -- |
221
+ | `--strict` | Require exact MC version match | off |
222
+ | `--no-cross-loader` | Disable Sinytra Connector fallback | off |
223
+ | `--docker-test` | Test mods in Docker server | off |
224
+ | `--docker-timeout` | Docker timeout (seconds) | 180 |
225
+ | `--output-report` | Save report to file | -- |
226
+ | `--log-file` | Write log to file | -- |
227
+ | `--no-share` | Skip anonymous data sharing (sharing is not live yet) | off |
228
+
229
+ ### `modkeel search`
230
+
231
+ | Option | Description | Default |
232
+ |--------|-------------|---------|
233
+ | `query` | Mod name to search | (required) |
234
+ | `-m`, `--mc-version` | Minecraft version | (required) |
235
+ | `-l`, `--loader` | Mod loader | `neoforge` |
236
+ | `-t`, `--github-token` | GitHub API token | -- |
237
+
238
+ ### `modkeel status`
239
+
240
+ No arguments. Shows version, config, cached loaders, and known NeoForge versions.
241
+
242
+ ## GitHub API Token
243
+
244
+ Without a token you're limited to 60 API requests/hour. With a token: 5,000/hour.
245
+
246
+ 1. Go to [github.com/settings/tokens](https://github.com/settings/tokens)
247
+ 2. **Generate new token** > **Tokens (classic)**
248
+ 3. Select scope: **`public_repo`** (only permission needed)
249
+ 4. Save the token: `echo "ghp_xxx..." > github_token.txt`
250
+
251
+ ## Troubleshooting
252
+
253
+ | Error | Cause | Fix |
254
+ |-------|-------|-----|
255
+ | `gradle.properties not found` | Branch lacks Gradle files | Try a different branch |
256
+ | `Compilation timeout (>10 min)` | Large mod or slow machine | Build manually with `./gradlew build` |
257
+ | `GitHub API rate limit exceeded` | No token or too many requests | Use `--github-token` |
258
+ | `minecraft_version is X, expected Y` | Branch targets wrong version | Expected -- Modkeel tries the next branch |
259
+ | `No JAR file found in build/libs` | Unusual output path | Check `build.gradle` |
260
+ | `JAR declares incompatible MC version` | Misconfigured `mods.toml` | Try another branch |
261
+
262
+ ## Contributing
263
+
264
+ See [CONTRIBUTING.md](https://github.com/Modkeel/modkeel/blob/main/CONTRIBUTING.md) for development setup, testing, and PR guidelines.
265
+
266
+ ## License
267
+
268
+ MIT License -- see [LICENSE](https://github.com/Modkeel/modkeel/blob/main/LICENSE) for details.