Skip to content

Guide Cartkit

bryanthaboi edited this page Aug 24, 2026 · 1 revision

The cartkit CLI

tools/cartkit.py is the cart-author command line: scaffold, validate, pin, pack, add-release-workflow, selftest. Python 3, no other dependencies -- unlike modkit it never drives the engine, so it runs anywhere.

python3 tools/cartkit.py <subcommand> [args]

Global flags: --repo PATH (repo root override), --json, --quiet.

A cart repo holds cart.json, label.png, a README.md, a CHANGELOG.md and a release workflow. The built .g1rcart is an output, not a source file -- see Custom Carts for what that file is.

scaffold

python3 tools/cartkit.py scaffold my_cart
python3 tools/cartkit.py scaffold my_cart --base gold --seal sealed+ \
  --title "My Cart" --author you --github owner/my-cart

Writes a new cart repo with a cart.json carrying a placeholder pin, a generated label.png in the shell colour, and the release workflow. Flags: --title (1..48 characters), --author, --summary (<=120), --base red|blue|yellow|gold|silver|crystal, --shell "#rrggbb", --seal sealed|sealed+|open, --github owner/repo, --into DIR, --force.

Replace the placeholder pin with a real one before validating; pin drops it for you.

pin

python3 tools/cartkit.py pin my_cart owner/repo@1.2.3
python3 tools/cartkit.py pin my_cart https://gamebanana.com/mods/512345
python3 tools/cartkit.py pin my_cart owner/repo@1.2.3 --option difficulty=hard

Adds or updates one pin and resolves its hash for you. For a GitHub spec it reads the release's own sha256sums.txt and records the hash of the archive that release actually publishes. Do not copy a hash from a locally packed zip: a mod's release workflow rewrites the version inside the archive, so a local build of the same version hashes differently and the pin will never verify.

Flags: --id ID (the mod's id in the cart, if it cannot be derived), --file FILE (GameBanana file id when the mod publishes several), --option KEY=VALUE (freeze an option; true/false and numbers are parsed), --clear-options.

Frozen options ride the cart and are part of its hash, so a cart's mods arrive configured.

validate

python3 tools/cartkit.py validate my_cart
python3 tools/cartkit.py validate my_cart --online --strict

Offline by default: shape, vocabulary and internal consistency. --online resolves every pin against GitHub and GameBanana and checks the recorded hashes against what those releases publish, which is the check that catches a stale pin.

Rule Refuses
CK001 no cart.json in that directory
CK002 a field missing, malformed, or outside its vocabulary
CK003 label art that is not a .png
CK004 a mods list that is not 1 to 64 pins
CK005 a load_order that does not match the pins
CK100 a GitHub pin that does not resolve to a release
CK101 a GitHub pin whose sha256 disagrees with the published archive
CK110 a GameBanana pin that does not resolve
CK111 a GameBanana pin whose md5 disagrees with the published file

--no-download stops validate fetching a release asset purely to hash it; a pin it could not check is reported as unchecked rather than passed.

cartkit packs only the fields it documents, so an unknown field warns rather than being silently carried. If you add a field to the cart format, teach cartkit about it in the same change or it will be dropped from every packed cart.

pack

python3 tools/cartkit.py pack my_cart

Writes <id>-<version>.g1rcart into the working directory: the manifest, the pins and the label PNG base64'd inline. --online and --no-download behave as they do for validate.

add-release-workflow

python3 tools/cartkit.py add-release-workflow my_cart

Copies in .github/workflows/release.yml, which on every push to main fetches cartkit, runs selftest, validates the cart --online --strict, packs it, and publishes the .g1rcart with a sha256sums.txt. A version that already has a release is skipped rather than rebuilt, so cutting a release is bumping version in cart.json.

The workflow fetches cartkit from the engine repo at CARTKIT_REPO/CARTKIT_REF, which is how a cart repo stays in step with the format without vendoring the tool.

selftest

python3 tools/cartkit.py selftest

Runs cartkit's own checks, including a scaffold/validate/pack round trip and a determinism check on the generated label. The release workflow runs this before it trusts a fetched copy of the tool.

A cart from nothing

python3 tools/cartkit.py scaffold my_cart --base red --author you \
  --github owner/my-cart
python3 tools/cartkit.py pin my_cart owner/some-mod@1.0.0
python3 tools/cartkit.py validate my_cart --online
python3 tools/cartkit.py pack my_cart

Then push the repo, let the workflow cut the release, and add an entry under carts/ in the mod index. The index entry's repo names the cart's repo, not the repo of a mod it pins.

Clone this wiki locally