#!/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."