chore: bump plugin version to 1.4.3 #50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Convert to Codex Plugin | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["v*"] | |
| permissions: | |
| contents: write | |
| jobs: | |
| convert: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout main | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Convert to Codex plugin format | |
| shell: python3 {0} | |
| run: | | |
| import json, re, os, stat, textwrap | |
| from pathlib import Path | |
| def info(msg): print(f"[OK] {msg}") | |
| print("=== Converting Claude Code plugin → Codex CLI plugin ===\n") | |
| # ── 1. Manifest: .claude-plugin/ → .codex-plugin/ ─────────── | |
| if Path(".claude-plugin").is_dir(): | |
| Path(".codex-plugin").mkdir(exist_ok=True) | |
| data = json.loads(Path(".claude-plugin/plugin.json").read_text()) | |
| data.setdefault("skills", "./skills/") | |
| if "description" in data: | |
| data["description"] = data["description"].replace("Claude Code", "Codex CLI") | |
| Path(".codex-plugin/plugin.json").write_text( | |
| json.dumps(data, indent=2, ensure_ascii=False) + "\n" | |
| ) | |
| import shutil; shutil.rmtree(".claude-plugin") | |
| info(".claude-plugin/ → .codex-plugin/") | |
| # ── 2. CLAUDE.md → AGENTS.md ──────────────────────────────── | |
| if Path("CLAUDE.md").is_file(): | |
| text = Path("CLAUDE.md").read_text() | |
| for old, new in [ | |
| ("Claude Code", "Codex CLI"), | |
| ("claude.ai/code", "codex.com"), | |
| ("CLAUDE.md", "AGENTS.md"), | |
| ("${CLAUDE_PLUGIN_ROOT}", "${CODEX_PLUGIN_ROOT}"), | |
| (".claude-plugin/plugin.json Plugin manifest", | |
| ".codex-plugin/plugin.json Plugin manifest"), | |
| ("commands/*.md Slash command definitions", | |
| "(commands converted to skills)"), | |
| ("Slash commands (commands/*.md)", | |
| "Skills (converted from commands)"), | |
| ("Slash commands in `commands/`", | |
| "Skills in `skills/`"), | |
| ]: | |
| text = text.replace(old, new) | |
| text = re.sub(r'(?<![/\w])(/unity-cli-)', r'$unity-cli-', text) | |
| Path("AGENTS.md").write_text(text) | |
| Path("CLAUDE.md").unlink() | |
| info("CLAUDE.md → AGENTS.md") | |
| # ── 3. Convert commands/*.md → skills/*/SKILL.md ──────────── | |
| cmd_dir = Path("commands") | |
| if cmd_dir.is_dir(): | |
| for cmd_file in sorted(cmd_dir.glob("*.md")): | |
| name = cmd_file.stem | |
| content = cmd_file.read_text() | |
| fm_match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL) | |
| if not fm_match: | |
| body, description = content, f"{name} skill" | |
| else: | |
| body = content[fm_match.end():] | |
| dm = re.search(r'description:\s*["\']?(.*?)["\']?\s*$', | |
| fm_match.group(1), re.MULTILINE) | |
| description = dm.group(1).strip("\"' ") if dm else f"{name} skill" | |
| skill_dir = Path(f"skills/{name}") | |
| skill_dir.mkdir(parents=True, exist_ok=True) | |
| (skill_dir / "SKILL.md").write_text( | |
| f"---\nname: {name}\ndescription: >\n {description}\n---\n\n{body}" | |
| ) | |
| print(f" {name}.md → skills/{name}/SKILL.md") | |
| shutil.rmtree(cmd_dir) | |
| info("commands/ → skills/ (converted)") | |
| # ── 4. Global text replacements ───────────────────────────── | |
| # Codex CLI does NOT support ${CODEX_PLUGIN_ROOT} as a template variable | |
| # (unlike Claude Code's ${CLAUDE_PLUGIN_ROOT}). Replace with a workspace- | |
| # relative path. Codex CWD is always the workspace root. | |
| PLUGIN_ROOT_EXPR = './plugins/unity-cli-plugin' | |
| for p in Path(".").rglob("*"): | |
| if not p.is_file() or ".git" in p.parts: | |
| continue | |
| if p.suffix not in (".md", ".py"): | |
| continue | |
| text = p.read_text(errors="replace") | |
| updated = text.replace("${CLAUDE_PLUGIN_ROOT}", PLUGIN_ROOT_EXPR) | |
| updated = updated.replace("CLAUDE_PLUGIN_ROOT", "CODEX_PLUGIN_ROOT") | |
| if p.suffix == ".md": | |
| updated = updated.replace("`/unity-cli-", "`$unity-cli-") | |
| updated = updated.replace("> /unity-cli-", "> $unity-cli-") | |
| updated = updated.replace(" /unity-cli-", " $unity-cli-") | |
| if updated != text: | |
| p.write_text(updated) | |
| # ── 5. .gitignore ─────────────────────────────────────────── | |
| gi = Path(".gitignore") | |
| if gi.is_file(): | |
| gi.write_text(gi.read_text().replace(".claude/\n", ".codex/\n")) | |
| # ── 6. Generate install.sh ────────────────────────────────── | |
| install_script = textwrap.dedent(r''' | |
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| PLUGIN_NAME="unity-cli-plugin" | |
| PLUGIN_REPO="https://github.com/niqibiao/unity-cli-plugin.git" | |
| PLUGIN_BRANCH="codex-plugin" | |
| die() { echo "ERROR: $*" >&2; exit 1; } | |
| info() { echo " [*] $*"; } | |
| ok() { echo " [OK] $*"; } | |
| # Detect working python (python3 may be a Windows Store stub) | |
| PY="" | |
| for cmd in python3 python; do | |
| if "$cmd" -c "import sys; sys.exit(0)" 2>/dev/null; then PY="$cmd"; break; fi | |
| done | |
| [ -n "$PY" ] || die "Python 3 is required but not found." | |
| if [ $# -lt 1 ]; then | |
| echo "Usage: bash install.sh <workspace-dir>" | |
| echo "" | |
| echo " Installs ${PLUGIN_NAME} into the given workspace." | |
| echo "" | |
| echo " <workspace-dir> Root of your project/repo." | |
| echo " Plugin -> <dir>/plugins/${PLUGIN_NAME}/" | |
| echo " Market -> <dir>/.agents/plugins/marketplace.json" | |
| exit 1 | |
| fi | |
| TARGET_DIR="$(cd "$1" && pwd)" || die "Directory '$1' does not exist." | |
| PLUGIN_DIR="${TARGET_DIR}/plugins/${PLUGIN_NAME}" | |
| MARKETPLACE_DIR="${TARGET_DIR}/.agents/plugins" | |
| MARKETPLACE_FILE="${MARKETPLACE_DIR}/marketplace.json" | |
| echo "" | |
| echo "=== Installing ${PLUGIN_NAME} into ${TARGET_DIR} ===" | |
| echo "" | |
| # ── 1. Clone or update plugin ────────────────────────────── | |
| PLUGIN_TMP="${PLUGIN_DIR}.tmp.$$" | |
| if [ -d "${PLUGIN_DIR}/.codex-plugin" ]; then | |
| info "Plugin already exists, updating..." | |
| mkdir -p "$(dirname "${PLUGIN_DIR}")" | |
| git clone --depth=1 --branch "${PLUGIN_BRANCH}" --single-branch \ | |
| "${PLUGIN_REPO}" "${PLUGIN_TMP}" --quiet \ | |
| || { rm -rf "${PLUGIN_TMP}"; die "Clone failed — existing plugin left intact."; } | |
| rm -rf "${PLUGIN_TMP}/.git" | |
| rm -rf "${PLUGIN_DIR}" | |
| mv "${PLUGIN_TMP}" "${PLUGIN_DIR}" | |
| ok "Updated ${PLUGIN_DIR}" | |
| elif [ ! -d "${PLUGIN_DIR}" ]; then | |
| info "Cloning ${PLUGIN_REPO} (branch: ${PLUGIN_BRANCH})..." | |
| mkdir -p "$(dirname "${PLUGIN_DIR}")" | |
| git clone --depth=1 --branch "${PLUGIN_BRANCH}" --single-branch \ | |
| "${PLUGIN_REPO}" "${PLUGIN_DIR}" --quiet | |
| rm -rf "${PLUGIN_DIR}/.git" | |
| ok "Cloned into ${PLUGIN_DIR}" | |
| fi | |
| # ── 2. Create or update marketplace.json ─────────────────── | |
| mkdir -p "${MARKETPLACE_DIR}" | |
| export MARKETPLACE_FILE | |
| $PY << 'PYEOF' | |
| import json, os | |
| plugin_name = "unity-cli-plugin" | |
| mf = os.environ["MARKETPLACE_FILE"] | |
| entry = { | |
| "name": plugin_name, | |
| "source": {"source": "local", "path": f"./plugins/{plugin_name}"}, | |
| "policy": {"installation": "INSTALLED_BY_DEFAULT", "authentication": "ON_INSTALL"}, | |
| "category": "Productivity" | |
| } | |
| if os.path.isfile(mf): | |
| with open(mf) as f: | |
| data = json.load(f) | |
| plugins = data.setdefault("plugins", []) | |
| idx = next((i for i, p in enumerate(plugins) if p.get("name") == plugin_name), None) | |
| if idx is not None: | |
| plugins[idx] = entry | |
| print(f" [OK] Updated existing {plugin_name} entry in marketplace.json") | |
| else: | |
| plugins.append(entry) | |
| print(f" [OK] Added {plugin_name} to marketplace.json") | |
| else: | |
| data = {"name": "local-workspace", "plugins": [entry]} | |
| print(" [OK] Created marketplace.json") | |
| with open(mf, "w") as f: | |
| json.dump(data, f, indent=2, ensure_ascii=False) | |
| f.write("\n") | |
| PYEOF | |
| echo "" | |
| echo "=== Installation complete ===" | |
| echo "" | |
| echo "Next steps:" | |
| echo " 1. Open your Unity project in Unity Editor" | |
| echo " 2. Start Codex CLI in ${TARGET_DIR}" | |
| echo ' 3. Run: $unity-cli-setup (installs the Unity C# Console package)' | |
| echo ' 4. Run: $unity-cli-status (verify connection)' | |
| echo "" | |
| ''').lstrip('\n') | |
| Path("install.sh").write_text(install_script, newline='\n') | |
| os.chmod("install.sh", os.stat("install.sh").st_mode | stat.S_IEXEC) | |
| info("install.sh generated") | |
| # ── 7. Cleanup ────────────────────────────────────────────── | |
| shutil.rmtree(".claude-plugin", ignore_errors=True) | |
| shutil.rmtree(".github", ignore_errors=True) | |
| print("\n=== Conversion complete ===") | |
| - name: Push to codex-plugin branch | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| SOURCE_SHA=$(git rev-parse --short HEAD) | |
| SOURCE_MSG=$(git log -1 --format='%s') | |
| git checkout --orphan codex-plugin-tmp | |
| git add -A | |
| git commit -m "sync: ${SOURCE_MSG} (from main@${SOURCE_SHA})" | |
| git push origin codex-plugin-tmp:codex-plugin --force | |
| echo "Pushed to codex-plugin branch" | |
| # If triggered by a tag push, create the same tag with -codex suffix | |
| # AND publish a GitHub Release for it (the GITHUB_TOKEN-pushed tag | |
| # alone won't trigger release.yml — anti-recursion safety). | |
| if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then | |
| TAG_NAME="${GITHUB_REF#refs/tags/}" | |
| CODEX_TAG="${TAG_NAME}-codex" | |
| git tag "$CODEX_TAG" | |
| git push origin "$CODEX_TAG" --force | |
| echo "Tagged codex-plugin branch as ${CODEX_TAG}" | |
| VERSION="${TAG_NAME#v}" | |
| if [ -f CHANGELOG.md ]; then | |
| awk -v ver="$VERSION" ' | |
| /^## / { if (found) exit; if (index($0, "[" ver "]")) { found=1; next } } | |
| found { print } | |
| ' CHANGELOG.md > release-notes.md | |
| fi | |
| if [ -s release-notes.md ] && grep -q '[^[:space:]]' release-notes.md; then | |
| gh release create "$CODEX_TAG" --title "$CODEX_TAG" --target codex-plugin --notes-file release-notes.md | |
| else | |
| gh release create "$CODEX_TAG" --title "$CODEX_TAG" --target codex-plugin --generate-notes | |
| fi | |
| echo "Released ${CODEX_TAG}" | |
| fi |