Skip to content

v0.4.3 - Germany Update #14

v0.4.3 - Germany Update

v0.4.3 - Germany Update #14

Workflow file for this run

name: Build Claude.ai Zips
on:
workflow_dispatch:
release:
types: [created]
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build zips
run: |
mkdir -p dist
# Stage 1: Per-region zips (existing structure)
for region in africa americas asia europe oceania; do
zip -r dist/cultures-${region}.zip regions/${region}/
done
# Stage 2: Full cultures zip -- built as flat zip in Stage 5 (Python block below)
# Stage 3: Per-engine platform zips (shared stack + platform-specific instructions)
for platform in claude copilot gemini; do
zip -j dist/engine_${platform}.zip \
engine/${platform}/instructions.md \
engine/stack.md \
engine/process_world_is_spinning.md
done
# Stage 4: Culture-specific zips for Claude consumption (new)
# Auto-detect complete cultures (has position.md + personas + place + piece + README)
# and create flat zips with all culture files + engine files
python3 << 'PYTHON_EOF'
import os
import re
import zipfile
import shutil
from pathlib import Path
def is_complete_culture(culture_dir):
"""Check if culture folder has all required components."""
try:
culture_path = Path(culture_dir)
# Must have position file
has_position = any(culture_path.glob('culture_*_position.md'))
# Must have at least 2 personas
personas = list(culture_path.glob('culture_*_persona_*.md')) + list(culture_path.glob('persona_*.md'))
has_personas = len(personas) >= 2
# Must have at least 1 place
places = list(culture_path.glob('culture_*_place_*.md'))
has_places = len(places) >= 1
# Must have at least 1 piece
pieces = list(culture_path.glob('culture_*_piece_*.md'))
has_pieces = len(pieces) >= 1
# Must have README
has_readme = (culture_path / 'README.md').exists()
return has_position and has_personas and has_places and has_pieces and has_readme
except:
return False
def is_region_manifest(region_dir):
"""Check if region folder is a manifest (has README.md at root)."""
return (region_dir / 'README.md').exists()
def rewrite_links_for_flat_zip(content):
"""Rewrite links from nested folder structure to flat structure.
Culture files are stored in regions/europe/germany/ etc. (nested depth varies).
Their links reference other files using relative paths like:
- ../../engine/stack.md (traverses up to repo root, then into engine/)
- ../../../engine/instructions.md (varies by depth)
In the flat zip structure (culture/ subfolder), all culture files are together.
Links should be simplified to just filenames:
- ../../engine/stack.md → stack.md
- Removes all ../ traversal and keeps just the filename
"""
import re
# Strip all ../ or ..\\ traversal and keep just the filename
# Pattern: [text](../ or .../ traversal/path/filename.md) → [text](filename.md)
content = re.sub(
r'\]\((?:\.\./)+([^)]+\.md)\)',
lambda m: f']({m.group(1).split("/")[-1]})',
content
)
return content
def create_culture_zip(staging_dir, zip_name):
"""Helper to create a single culture zip from staging directory."""
zip_path = Path(f'dist/{zip_name}.zip')
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
# Add known root files
for root_file in ['LICENSE', 'README.md', 'REFERENCES.md']:
file_path = staging_dir / root_file
if file_path.exists():
zf.write(file_path, arcname=root_file)
# Add any hofstede_* files at root (country zips)
for hofstede_file in sorted(staging_dir.glob('hofstede_*')):
zf.write(hofstede_file, arcname=hofstede_file.name)
# Add hofstede/ subfolder files (regional zips)
hofstede_dir = staging_dir / 'hofstede'
if hofstede_dir.exists():
for hofstede_file in sorted(hofstede_dir.glob('*')):
zf.write(hofstede_file, arcname=f'hofstede/{hofstede_file.name}')
# Add engine/ subdirectory files
for engine_file in (staging_dir / 'engine').glob('*.md'):
zf.write(engine_file, arcname=f'engine/{engine_file.name}')
# Add culture/ subdirectory files
culture_files = list((staging_dir / 'culture').glob('*.md'))
for culture_file in culture_files:
zf.write(culture_file, arcname=f'culture/{culture_file.name}')
return len(culture_files)
def stage_culture_files(staging_dir, culture_dir, culture_name):
"""Stage culture files into the staging directory."""
engine_path = Path('engine')
# Copy instructions.md from claude-specific or fallback to generic
claude_instructions = engine_path / 'claude' / 'instructions.md'
if claude_instructions.exists():
shutil.copy2(claude_instructions, staging_dir / 'engine' / 'instructions.md')
# Copy stack.md
stack_src = engine_path / 'stack.md'
if stack_src.exists():
shutil.copy2(stack_src, staging_dir / 'engine' / 'stack.md')
# Copy culture/ files (all culture_*.md files from country dir)
culture_count = 0
for culture_file in culture_dir.glob('culture_*.md'):
shutil.copy2(culture_file, staging_dir / 'culture' / culture_file.name)
culture_count += 1
# Rewrite links in CULTURE FILES ONLY (strip ../ traversal for flat structure)
for markdown_file in (staging_dir / 'culture').glob('*.md'):
try:
content = markdown_file.read_text(encoding='utf-8')
content = rewrite_links_for_flat_zip(content)
markdown_file.write_text(content, encoding='utf-8')
except:
pass
return culture_count
# Stage 4a: Individual country culture zips
regions_path = Path('regions')
for region_dir in regions_path.iterdir():
if not region_dir.is_dir():
continue
for country_dir in region_dir.iterdir():
if not country_dir.is_dir():
continue
if is_complete_culture(country_dir):
# Determine culture name from folder
culture_name = country_dir.name
# Create temp staging directory with structure
staging_dir = Path(f'dist/staging-{culture_name}')
if staging_dir.exists():
shutil.rmtree(staging_dir)
staging_dir.mkdir(parents=True, exist_ok=True)
# Create subdirectories: engine/, culture/
(staging_dir / 'engine').mkdir(exist_ok=True)
(staging_dir / 'culture').mkdir(exist_ok=True)
# Copy root scaffold files (LICENSE, README, REFERENCES)
if Path('LICENSE').exists():
shutil.copy2('LICENSE', staging_dir / 'LICENSE')
# Copy README from country dir (baseline for each culture)
readme_src = country_dir / 'README.md'
if readme_src.exists():
readme_content = readme_src.read_text(encoding='utf-8')
# Strip Download section: remove everything up to and including first --- divider
# This keeps Install + Content sections
if '---' in readme_content:
parts = readme_content.split('---', 2) # Split on first two occurrences
if len(parts) >= 3:
readme_content = '---'.join(parts[1:]).lstrip('\n') # Keep from second --- onward
# Rewrite links (flatten nested paths)
readme_content = rewrite_links_for_flat_zip(readme_content)
(staging_dir / 'README.md').write_text(readme_content, encoding='utf-8')
# Copy REFERENCES from country dir (baseline for each culture)
references_src = country_dir / 'REFERENCES.md'
if references_src.exists():
shutil.copy2(references_src, staging_dir / 'REFERENCES.md')
# Copy hofstede files from country dir to zip root
for hofstede_file in country_dir.glob('hofstede_*'):
shutil.copy2(hofstede_file, staging_dir / hofstede_file.name)
# Stage engine + culture files
culture_count = stage_culture_files(staging_dir, country_dir, culture_name)
# Create structured zip
create_culture_zip(staging_dir, culture_name)
# Cleanup staging
shutil.rmtree(staging_dir)
print(f'Created {culture_name}.zip with structured layout (LICENSE + README + REFERENCES + engine/ + culture/{culture_count} files)')
# Stage 4b: Regional combined zips (manifests that combine multiple countries)
for region_dir in regions_path.iterdir():
if not region_dir.is_dir():
continue
if is_region_manifest(region_dir):
region_name = region_dir.name
# Find all complete cultures in this region
complete_cultures = []
for country_dir in region_dir.iterdir():
if country_dir.is_dir() and is_complete_culture(country_dir):
complete_cultures.append(country_dir)
# Only create regional zip if there are complete cultures
if complete_cultures:
# Create staging directory for combined regional zip
staging_dir = Path(f'dist/staging-{region_name}')
if staging_dir.exists():
shutil.rmtree(staging_dir)
staging_dir.mkdir(parents=True, exist_ok=True)
# Create subdirectories
(staging_dir / 'engine').mkdir(exist_ok=True)
(staging_dir / 'culture').mkdir(exist_ok=True)
(staging_dir / 'hofstede').mkdir(exist_ok=True)
# Copy root files
if Path('LICENSE').exists():
shutil.copy2('LICENSE', staging_dir / 'LICENSE')
# Copy region README (manifest)
region_readme = region_dir / 'README.md'
if region_readme.exists():
readme_content = region_readme.read_text(encoding='utf-8')
readme_content = rewrite_links_for_flat_zip(readme_content)
(staging_dir / 'README.md').write_text(readme_content, encoding='utf-8')
# Copy engine files
engine_path = Path('engine')
claude_instructions = engine_path / 'claude' / 'instructions.md'
if claude_instructions.exists():
shutil.copy2(claude_instructions, staging_dir / 'engine' / 'instructions.md')
stack_src = engine_path / 'stack.md'
if stack_src.exists():
shutil.copy2(stack_src, staging_dir / 'engine' / 'stack.md')
# Combine all culture files from all complete countries
total_culture_count = 0
for country_dir in complete_cultures:
for culture_file in country_dir.glob('culture_*.md'):
dest_name = culture_file.name
shutil.copy2(culture_file, staging_dir / 'culture' / dest_name)
total_culture_count += 1
# Copy hofstede files with country prefix: hofstede_{country}_{suffix}
for hofstede_file in sorted(country_dir.glob('hofstede_*')):
suffix = hofstede_file.name[len('hofstede_'):]
dest_name = f'hofstede_{country_dir.name}_{suffix}'
shutil.copy2(hofstede_file, staging_dir / 'hofstede' / dest_name)
# Rewrite links in all culture files
for markdown_file in (staging_dir / 'culture').glob('*.md'):
try:
content = markdown_file.read_text(encoding='utf-8')
content = rewrite_links_for_flat_zip(content)
markdown_file.write_text(content, encoding='utf-8')
except:
pass
# Create regional combined zip
culture_count = create_culture_zip(staging_dir, region_name)
# Cleanup staging
shutil.rmtree(staging_dir)
print(f'Created {region_name}.zip as regional combined manifest ({len(complete_cultures)} countries, {total_culture_count} culture files)')
# Stage 5: World flat zip (all complete cultures across all regions)
staging_dir = Path('dist/staging-world')
if staging_dir.exists():
shutil.rmtree(staging_dir)
staging_dir.mkdir(parents=True, exist_ok=True)
(staging_dir / 'engine').mkdir(exist_ok=True)
(staging_dir / 'culture').mkdir(exist_ok=True)
(staging_dir / 'hofstede').mkdir(exist_ok=True)
# Root files
if Path('LICENSE').exists():
shutil.copy2('LICENSE', staging_dir / 'LICENSE')
if Path('README.md').exists():
readme_content = Path('README.md').read_text(encoding='utf-8')
readme_content = rewrite_links_for_flat_zip(readme_content)
(staging_dir / 'README.md').write_text(readme_content, encoding='utf-8')
# Engine files
claude_instructions = Path('engine') / 'claude' / 'instructions.md'
if claude_instructions.exists():
shutil.copy2(claude_instructions, staging_dir / 'engine' / 'instructions.md')
stack_src = Path('engine') / 'stack.md'
if stack_src.exists():
shutil.copy2(stack_src, staging_dir / 'engine' / 'stack.md')
# All complete cultures across all regions
world_culture_count = 0
world_hofstede_count = 0
for region_dir in regions_path.iterdir():
if not region_dir.is_dir():
continue
for country_dir in region_dir.iterdir():
if not country_dir.is_dir() or not is_complete_culture(country_dir):
continue
for culture_file in country_dir.glob('culture_*.md'):
shutil.copy2(culture_file, staging_dir / 'culture' / culture_file.name)
world_culture_count += 1
for hofstede_file in sorted(country_dir.glob('hofstede_*')):
suffix = hofstede_file.name[len('hofstede_'):]
dest_name = f'hofstede_{country_dir.name}_{suffix}'
shutil.copy2(hofstede_file, staging_dir / 'hofstede' / dest_name)
world_hofstede_count += 1
# Rewrite links in all culture files
for markdown_file in (staging_dir / 'culture').glob('*.md'):
try:
content = markdown_file.read_text(encoding='utf-8')
content = rewrite_links_for_flat_zip(content)
markdown_file.write_text(content, encoding='utf-8')
except:
pass
create_culture_zip(staging_dir, 'cultures-full')
shutil.rmtree(staging_dir)
print(f'Created cultures-full.zip as world flat zip ({world_culture_count} culture files, {world_hofstede_count} hofstede files)')
PYTHON_EOF
- name: Upload to release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: dist/*.zip
- name: Upload as artifacts
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
with:
name: cultures-zips
path: dist/*.zip
retention-days: 7