This skill should be used when you need to create filtered subsets of the card_blueprint_database.json file to resolve slug collisions during card mapping...
The card blueprint database (card_blueprint_database.json) contains all SWCCG card data with slugified names for mapping. However, slug collisions occur when:
This skill provides jq-based filtering to create side-specific databases that exclude defensive shields, resolving these collisions during cube building.
Use this skill when:
Each card in the database has these relevant fields:
{
"cardId": "101_2",
"title": "Luke",
"slug": "luke",
"slugWithSetName": "luke-premiere-introductory-two-player-game",
"side": "LIGHT",
"cardCategory": "CHARACTER",
"cardTypes": ["REBEL"]
}
Key fields for filtering:
side - Either "LIGHT" or "DARK"cardCategory - Card category, including "DEFENSIVE_SHIELD"cardTypes - Array of types, including "DEFENSIVE_SHIELD"Create a light side only database:
jq '[.[] | select(.side == "LIGHT" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
Create a dark side only database:
jq '[.[] | select(.side == "DARK" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
Create database without any defensive shields:
jq '[.[] | select(.cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_no_shields.json
Check card counts after filtering:
# Original database count
jq 'length' src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# Light side count
jq 'length' src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
# Dark side count
jq 'length' src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
# Verify no defensive shields
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
# Should output: 0
When building cubes with slug collisions, modify the map_card_names_to_ids script to use filtered databases:
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json
Step 1: Create filtered databases
# Light side database (no defensive shields)
jq '[.[] | select(.side == "LIGHT" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
# Dark side database (no defensive shields)
jq '[.[] | select(.side == "DARK" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
Step 2: Modify map_card_names_to_ids script to accept database path
The script needs to accept an optional third parameter for the database path:
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
Step 3: Map with appropriate database
CUBE_DIR="src/gemp-swccg-server/src/main/resources/draft/cube_building/worlds_2025"
# Map light side with light database
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
# Map dark side with dark database
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_darkside.csv \
$CUBE_DIR/cube_worlds_darkside_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
# Default cards can use either (or no-shields version if needed)
./bin/map_card_names_to_ids \
$CUBE_DIR/default_cards.csv \
$CUBE_DIR/default_cards_mapped.json
# 1. Regenerate main card database
./bin/gemp export-cards src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# 2. Create filtered databases
jq '[.[] | select(.side == "LIGHT" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
jq '[.[] | select(.side == "DARK" and .cardCategory != "DEFENSIVE_SHIELD")]' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json \
> src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
# 3. Verify filtering worked
echo "Light side cards (no shields):"
jq 'length' src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
echo "Dark side cards (no shields):"
jq 'length' src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
# 4. Map cards with filtered databases
CUBE_DIR="src/gemp-swccg-server/src/main/resources/draft/cube_building/worlds_2025"
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_darkside.csv \
$CUBE_DIR/cube_worlds_darkside_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
./bin/map_card_names_to_ids \
$CUBE_DIR/default_cards.csv \
$CUBE_DIR/default_cards_mapped.json
# 5. Verify mapping success
echo "Light side mapping:"
cat $CUBE_DIR/cube_worlds_lightside_mapped_log.json | jq '.summary'
echo "Dark side mapping:"
cat $CUBE_DIR/cube_worlds_darkside_mapped_log.json | jq '.summary'
# 6. Generate cube configuration
python3 bin/create_cube $CUBE_DIR src/gemp-swccg-server/src/main/resources/draft/cubeWorlds2025.json
# 7. Deploy
./bin/gemp reload-fast
Solution: Install jq via Homebrew:
brew install jq
Verification:
# Check sides in filtered database
jq '[.[] | .side] | unique' card_blueprint_database_light.json
# Should output: ["LIGHT"]
# Check for defensive shields
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' card_blueprint_database_light.json
# Should output: 0
Cause: Script still using main database instead of filtered one
Solution: Verify third parameter is being passed and used in the script:
# Check if script accepts database path parameter
grep -n "sys.argv\[3\]" bin/map_card_names_to_ids
If the script doesn't support the third parameter yet, it needs to be modified to accept it.
Investigation:
# Find cards with same slug in filtered database
jq -r '.[] | .slug' card_blueprint_database_light.json | sort | uniq -d
# For each duplicate slug, check which cards have it
jq '.[] | select(.slug == "duplicate-slug-here")' card_blueprint_database_light.json
Common causes:
Solution: Use slugWithSetName for disambiguation or add set identifier to CSV.
After successfully building the cube, you can optionally remove the temporary filtered databases:
rm src/gemp-swccg-cards/src/main/resources/card_blueprint_database_light.json
rm src/gemp-swccg-cards/src/main/resources/card_blueprint_database_dark.json
rm src/gemp-swccg-cards/src/main/resources/card_blueprint_database_no_shields.json
However, keeping them can speed up future cube builds with the same filtering requirements.
gemp-swccg/
āāā src/gemp-swccg-cards/src/main/resources/
ā āāā card_blueprint_database.json # Main database
ā āāā card_blueprint_database_light.json # Light side filtered (temporary)
ā āāā card_blueprint_database_dark.json # Dark side filtered (temporary)
ā āāā card_blueprint_database_no_shields.json # No shields (temporary)
ā
āāā bin/
ā āāā map_card_names_to_ids # Mapping script (needs modification)
ā
āāā src/gemp-swccg-server/src/main/resources/draft/cube_building/
āāā <cube_name>/
āāā cube_worlds_lightside.csv # Light side cards
āāā cube_worlds_darkside.csv # Dark side cards
āāā default_cards.csv # Core cards
Combine filters for complex requirements:
# Light side characters only (no defensive shields)
jq '[.[] | select(.side == "LIGHT" and .cardCategory == "CHARACTER")]' \
card_blueprint_database.json > light_characters.json
# Dark side locations only (no defensive shields)
jq '[.[] | select(.side == "DARK" and .cardCategory == "LOCATION")]' \
card_blueprint_database.json > dark_locations.json
# Specific set only
jq '[.[] | select(.expansionSet == "PREMIERE")]' \
card_blueprint_database.json > premiere_only.json
# Count cards by category for light side
jq '[.[] | select(.side == "LIGHT")] | group_by(.cardCategory) | map({category: .[0].cardCategory, count: length})' \
card_blueprint_database.json
# Count defensive shields
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
card_blueprint_database.json
# Find duplicate slugs across sides
jq -r '.[] | .slug' card_blueprint_database.json | sort | uniq -d > duplicate_slugs.txt
# For each duplicate, show which sides/categories have it
while read slug; do
echo "Slug: $slug"
jq ".[] | select(.slug == \"$slug\") | {cardId, title, side, cardCategory}" \
card_blueprint_database.json
echo "---"
done < duplicate_slugs.txt
See references/ directory for detailed jq filter examples and common slug collision patterns.