Reads packwiz pack (pack.toml + index.toml + per-file .pw.toml) and emits a simple-mod-sync sync_version=3 manifest. Drops server-only mods, skips CurseForge metadata-mode entries with a warning, maps content type from the parent directory of each metafile. Optional --bundle-non-mods zips config/, options.txt etc into one archive served as a 'packed' entry — covers the gap where simple-mod-sync only ships zip-extractable content for non-mods. 15 tests, includes integration against upstream packwiz-example-pack.
packwiz-to-sms
Convert a packwiz pack to a simple-mod-sync manifest (sync_version: 3).
Why
Use packwiz to author the modpack (Modrinth/CurseForge integration, git-friendly TOML, optional/side-aware mods, .mrpack export for free), and simple-mod-sync for delivery to clients that can't or won't use Prism/MMC pre-launch hooks (vanilla launcher, TLauncher, cracked players).
One source of truth, two distribution channels — .mrpack export and simple-mod-sync manifest are both produced from the same packwiz repo.
Install
Requires Python 3.11+ (uses tomllib). Zero runtime deps.
git clone https://git.timemachine.center/Timemachine/packwiz-to-sms.git
cd packwiz-to-sms
python3 packwiz_to_sms.py --help
Usage
# Minimal — emit manifest to stdout
python3 packwiz_to_sms.py /path/to/packwiz/pack
# Write to file
python3 packwiz_to_sms.py /path/to/packwiz/pack -o manifest.json
# Bundle non-mod files (config/, options.txt, servers.dat) into a zip
# and add a 'packed' entry pointing at where you'll host it
python3 packwiz_to_sms.py /path/to/packwiz/pack \
-o manifest.json \
--bundle-non-mods overrides.zip \
--bundle-url https://packs.example.com/overrides.zip
What gets emitted
| Packwiz path | Becomes simple-mod-sync type |
|---|---|
mods/*.pw.toml (side=client or both) |
mod |
mods/*.pw.toml (side=server) |
dropped |
resourcepacks/*.pw.toml |
resourcepack |
shaderpacks/*.pw.toml |
shader |
**/datapacks/*.pw.toml |
datapack |
Any non-metafile (e.g. options.txt, config/*) |
bundled into zip via --bundle-non-mods, emitted as one packed entry pointing at directory: "." |
CurseForge mods that use mode = "metadata:curseforge" (no direct URL) are skipped with a warning. Either switch to Modrinth equivalents or run packwiz cf reexport first to inline resolved URLs.
What's not handled
- Optional mods — simple-mod-sync has no per-client toggle UI. All non-server mods are emitted unconditionally. Ship two manifests (with/without optional) if you need this.
- Mod removal — simple-mod-sync's
modify/removeis not auto-populated. Convert by hand if you're dropping mods. - Rename / regex transforms — packwiz has no equivalent concept, so we don't generate
modify.renameentries.
How it works
- Reads
pack.toml+index.toml. - For each entry marked
metafile = true: reads the.pw.toml, pullsdownload.url, picks the simple-mod-synctypefrom the parent directory. - Drops
side = "server". - Drops entries where
download.urlis missing (CF metadata mode). - Optionally zips non-metafile files into a single archive for
packeddistribution.
Output schema matches sync_version: 3 exactly (see simple-mod-sync DOCS.md).
Tests
python3 -m pytest tests/
Covers conversion logic + CLI + bundle pipeline + a network integration test that converts the official packwiz-example-pack (auto-skipped if offline).
Upstream contribution
This tool fits the model used by other simple-mod-sync translators. It can be PR'd upstream as translators/packwiz.py.
License
MIT.