#!/usr/bin/env bash # Publish Godogen runtime files into a target game repo. # # Usage: # ./publish.sh --engine godot|bevy|babylon --agent claude|codex --out [--force] # ./publish.sh --engine godot|bevy|babylon --agent claude|codex [--force] # # A published repo carries only docs: the runtime manifest (CLAUDE.md / AGENTS.md), # a per-engine guide (.md), and the asset-gen skill. The agent scaffolds # the game itself from the engine guide — no project scaffold is shipped. set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")" && pwd)" HELPERS="$REPO_ROOT/scripts" ENGINE="" AGENT="" OUT="" FORCE=0 usage() { sed -n '1,10p' "$0" >&2 } while [ $# -gt 0 ]; do case "$1" in --engine) ENGINE="${2:-}"; shift 2 ;; --agent) AGENT="${2:-}"; shift 2 ;; --out) OUT="${2:-}"; shift 2 ;; --force) FORCE=1; shift ;; -h|--help) usage; exit 0 ;; -*) echo "error: unknown option $1" >&2; usage; exit 1 ;; *) if [ -n "$OUT" ]; then echo "error: target specified more than once" >&2 exit 1 fi OUT="$1" shift ;; esac done case "$ENGINE" in godot) ENGINE_DISPLAY="Godot" ;; bevy) ENGINE_DISPLAY="Bevy" ;; babylon) ENGINE_DISPLAY="Babylon.js" ;; *) echo "error: --engine must be godot, bevy, or babylon" >&2; usage; exit 1 ;; esac # Root for runtime-loaded generated assets, substituted into the asset docs. case "$ENGINE" in babylon) RUNTIME_ASSET_DIR="src/assets" ;; *) RUNTIME_ASSET_DIR="assets" ;; esac case "$AGENT" in claude) MANIFEST="CLAUDE.md" SKILLS_DIR_REL=".claude/skills" AGENT_NAME="Claude" ASSET_SKILL_COMMAND="/asset-gen" ;; codex) MANIFEST="AGENTS.md" SKILLS_DIR_REL=".agents/skills" AGENT_NAME="Codex" ASSET_SKILL_COMMAND="\$asset-gen" ;; *) echo "error: --agent must be claude or codex" >&2; usage; exit 1 ;; esac ASSET_GEN_SKILL_DIR="$SKILLS_DIR_REL/asset-gen" ENGINE_GUIDE_FILE="$ENGINE.md" if [ -z "$OUT" ]; then echo "error: --out is required" >&2 usage exit 1 fi TARGET="$(cd "$OUT" 2>/dev/null && pwd || (mkdir -p "$OUT" && cd "$OUT" && pwd))" if [ "$FORCE" -eq 1 ] && [ -d "$TARGET" ]; then echo "Force: cleaning $TARGET" rm -rf "${TARGET:?}" mkdir -p "$TARGET" fi TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT echo "Publishing $ENGINE/$AGENT to: $TARGET" # --- Skills: only the asset-gen skill --- mkdir -p "$TMP/skills/asset-gen" rsync -a --delete --exclude='__pycache__/' "$REPO_ROOT/asset-gen/" "$TMP/skills/asset-gen/" python3 "$HELPERS/render_dir.py" "$TMP/skills" \ "AGENT_NAME=$AGENT_NAME" \ "ASSET_GEN_SKILL_DIR=$ASSET_GEN_SKILL_DIR" \ "ASSET_SKILL_COMMAND=$ASSET_SKILL_COMMAND" \ "RUNTIME_ASSET_DIR=$RUNTIME_ASSET_DIR" if [ "$AGENT" = "codex" ]; then python3 "$HELPERS/generate_codex_metadata.py" "$TMP/skills" fi mkdir -p "$TARGET/$SKILLS_DIR_REL" rsync -a --delete "$TMP/skills/" "$TARGET/$SKILLS_DIR_REL/" echo "Installed asset-gen skill" # --- Manifest: the runtime process doc --- MANIFEST_TMP="$TMP/manifest" mkdir -p "$MANIFEST_TMP" cp "$REPO_ROOT/prompts/runtime.md" "$MANIFEST_TMP/$MANIFEST" python3 "$HELPERS/render_dir.py" "$MANIFEST_TMP" \ "ENGINE_NAME=$ENGINE_DISPLAY" \ "ENGINE_GUIDE_FILE=$ENGINE_GUIDE_FILE" \ "ASSET_SKILL_COMMAND=$ASSET_SKILL_COMMAND" cp "$MANIFEST_TMP/$MANIFEST" "$TARGET/$MANIFEST" echo "Created $MANIFEST" # --- Per-engine guide (literal markdown) --- cp "$REPO_ROOT/engines/$ENGINE.md" "$TARGET/$ENGINE_GUIDE_FILE" echo "Created $ENGINE_GUIDE_FILE" # --- .gitignore (published instruction files are regenerated by publish.sh) --- if [ ! -f "$TARGET/.gitignore" ]; then { if [ "$AGENT" = "claude" ]; then printf '.claude\nCLAUDE.md\n' else printf '.agents\nAGENTS.md\n.codex\n' fi printf '%s\n' "$ENGINE_GUIDE_FILE" case "$ENGINE" in godot) printf 'assets\nscreenshots\n.godot\n*.import\nbin/\nobj/\n' ;; bevy) printf '/target\n/screenshots\n' ;; babylon) printf '/node_modules\n/dist\n/screenshots\n' ;; esac } > "$TARGET/.gitignore" echo "Created .gitignore" fi git -C "$TARGET" init -q 2>/dev/null || true echo "Done."