From 7a719afdaa88d20de4baba943eb49a65cd749c80 Mon Sep 17 00:00:00 2001 From: Enzo Esat Emre Demirel Date: Fri, 6 Mar 2026 16:12:26 +0100 Subject: [PATCH 1/2] feat: port the unity bridge to codex --- .TestProject/.gitignore | 3 +- .TestProject/Packages/manifest.json | 4 +- .TestProject/README.md | 241 +++------ .TestProject/VALIDATION.md | 239 +++------ .github/CODEOWNERS | 11 +- .github/workflows/test-skill.yml | 8 +- .gitignore | 1 + .pre-commit-config.yaml | 4 +- AGENTS.md | 84 +-- CLAUDE.md | 53 +- CONTRIBUTING.md | 13 +- README.md | 120 +++-- SECURITY.md | 16 +- codecov.yml | 2 +- docs/ARCHITECTURE.md | 109 ++-- docs/INSTALLATION.md | 164 ++---- docs/USAGE.md | 139 ++--- install.ps1 | 18 +- install.sh | 22 +- package/CHANGELOG.md | 165 +----- .../{ClaudeBridge.cs => CodexBridge.cs} | 52 +- ...audeBridge.cs.meta => CodexBridge.cs.meta} | 0 package/Editor/Commands/CompileCommand.cs | 10 +- package/Editor/Commands/EditorPlayMode.cs | 2 +- .../Editor/Commands/GetConsoleLogsCommand.cs | 14 +- package/Editor/Commands/GetStatusCommand.cs | 6 +- package/Editor/Commands/ICommand.cs | 4 +- package/Editor/Commands/IEditorPlayMode.cs | 2 +- package/Editor/Commands/PauseCommand.cs | 8 +- package/Editor/Commands/PlayCommand.cs | 8 +- package/Editor/Commands/RefreshCommand.cs | 10 +- package/Editor/Commands/RunTestsCommand.cs | 10 +- package/Editor/Commands/StepCommand.cs | 8 +- package/Editor/Models/CommandRequest.cs | 2 +- package/Editor/Models/CommandResponse.cs | 2 +- ...dge.asmdef => ProdFact.CodexBridge.asmdef} | 4 +- ....meta => ProdFact.CodexBridge.asmdef.meta} | 0 ...audeBridgeTests.cs => CodexBridgeTests.cs} | 10 +- ...Tests.cs.meta => CodexBridgeTests.cs.meta} | 0 .../Editor/Commands/CompileCommandTests.cs | 12 +- .../Commands/GetConsoleLogsCommandTests.cs | 6 +- .../Editor/Commands/GetStatusCommandTests.cs | 6 +- .../Editor/Commands/PauseCommandTests.cs | 6 +- .../Tests/Editor/Commands/PlayCommandTests.cs | 6 +- .../Editor/Commands/RefreshCommandTests.cs | 6 +- .../Editor/Commands/RunTestsCommandTests.cs | 6 +- .../Tests/Editor/Commands/StepCommandTests.cs | 6 +- .../Editor/Models/CommandRequestTests.cs | 4 +- .../Editor/Models/CommandResponseTests.cs | 4 +- ... ProdFact.CodexBridge.Tests.Editor.asmdef} | 6 +- ...Fact.CodexBridge.Tests.Editor.asmdef.meta} | 0 .../Editor/TestHelpers/CommandTestFixture.cs | 4 +- .../Editor/TestHelpers/ResponseCapture.cs | 4 +- package/package.json | 18 +- skill/SKILL.md | 490 ++---------------- skill/TESTING.md | 18 +- skill/pyproject.toml | 13 +- skill/references/COMMANDS.md | 116 ++--- skill/references/EXTENDING.md | 88 ++-- skill/src/claude_unity_bridge/__init__.py | 3 - skill/src/claude_unity_bridge/skill/SKILL.md | 478 ----------------- skill/src/codex_unity_bridge/__init__.py | 3 + .../cli.py | 51 +- skill/src/codex_unity_bridge/skill/SKILL.md | 94 ++++ .../skill/references/COMMANDS.md | 342 ++++++++++-- .../skill/references/EXTENDING.md | 88 ++-- skill/tests/test_cli.py | 344 ++++++------ 67 files changed, 1424 insertions(+), 2366 deletions(-) rename package/Editor/{ClaudeBridge.cs => CodexBridge.cs} (84%) rename package/Editor/{ClaudeBridge.cs.meta => CodexBridge.cs.meta} (100%) rename package/Editor/{ClaudeBridge.asmdef => ProdFact.CodexBridge.asmdef} (82%) rename package/Editor/{ClaudeBridge.asmdef.meta => ProdFact.CodexBridge.asmdef.meta} (100%) rename package/Tests/Editor/{ClaudeBridgeTests.cs => CodexBridgeTests.cs} (97%) rename package/Tests/Editor/{ClaudeBridgeTests.cs.meta => CodexBridgeTests.cs.meta} (100%) rename package/Tests/Editor/{MXR.ClaudeBridge.Tests.Editor.asmdef => ProdFact.CodexBridge.Tests.Editor.asmdef} (78%) rename package/Tests/Editor/{MXR.ClaudeBridge.Tests.Editor.asmdef.meta => ProdFact.CodexBridge.Tests.Editor.asmdef.meta} (100%) delete mode 100644 skill/src/claude_unity_bridge/__init__.py delete mode 100644 skill/src/claude_unity_bridge/skill/SKILL.md create mode 100644 skill/src/codex_unity_bridge/__init__.py rename skill/src/{claude_unity_bridge => codex_unity_bridge}/cli.py (95%) create mode 100644 skill/src/codex_unity_bridge/skill/SKILL.md rename skill/src/{claude_unity_bridge => codex_unity_bridge}/skill/references/COMMANDS.md (72%) rename skill/src/{claude_unity_bridge => codex_unity_bridge}/skill/references/EXTENDING.md (91%) diff --git a/.TestProject/.gitignore b/.TestProject/.gitignore index d317ae9..2f21fa8 100644 --- a/.TestProject/.gitignore +++ b/.TestProject/.gitignore @@ -19,5 +19,6 @@ ProjectSettings/*.meta !Packages/ !Packages/manifest.json -# .unity-bridge/ is created at runtime by Unity Bridge - always ignore +# Bridge runtime directories are created at runtime - always ignore .unity-bridge/ +.codex-unity-bridge/ diff --git a/.TestProject/Packages/manifest.json b/.TestProject/Packages/manifest.json index 0a8b935..d788b89 100644 --- a/.TestProject/Packages/manifest.json +++ b/.TestProject/Packages/manifest.json @@ -1,6 +1,6 @@ { "dependencies": { - "com.mxr.claude-bridge": "file:../../package", + "com.prodfact.codex-bridge": "file:../../package", "com.unity.ai.navigation": "2.0.9", "com.unity.collab-proxy": "2.9.3", "com.unity.ide.rider": "3.0.38", @@ -53,6 +53,6 @@ } ], "testables": [ - "com.mxr.claude-bridge" + "com.prodfact.codex-bridge" ] } diff --git a/.TestProject/README.md b/.TestProject/README.md index ee2b6f0..14de8a0 100644 --- a/.TestProject/README.md +++ b/.TestProject/README.md @@ -1,226 +1,127 @@ -# Unity Bridge - Integration Test Project +# Unity Bridge Integration Project -This is a minimal Unity 2021.3+ project used for end-to-end integration testing of the Claude Unity Bridge package. - -**Note**: This directory is named `.TestProject` (hidden) to prevent it from appearing in the Package Manager when users install this package. It's excluded via `.npmignore` and only used for local development and testing. +`.TestProject/` is the local Unity project used to validate the package in a real editor session. It stays hidden so it does not show up in normal package browsing and is excluded from the published package. ## Purpose -The `.TestProject/` directory enables: - -1. **Package Development** - Test Unity Bridge changes in a real Unity project -2. **End-to-End Validation** - Verify the bridge protocol works correctly -3. **Agent Testing** - Allow AI agents to validate their Unity code changes -4. **Integration Testing** - Test Python skill script against actual Unity Editor +- Validate the Unity package against a real project, not only isolated tests +- Exercise the file-based bridge end to end +- Give contributors a repeatable place to run manual Unity checks +- Provide a local target for the package test assembly ## Structure -``` +```text .TestProject/ -├── Assets/ # Unity assets (gitignored, created by Unity) -├── Packages/ # Package dependencies -│ └── manifest.json # References parent directory as local package -├── ProjectSettings/ # Unity project settings (committed to git) -├── .unity-bridge/ # Bridge protocol directory (gitignored, created at runtime) -├── .gitignore # Ignores everything except ProjectSettings & README -└── README.md # This file +├── Assets/ # Unity assets (mostly gitignored) +├── Packages/ +│ └── manifest.json # Local package reference + testables +├── ProjectSettings/ # Committed Unity project settings +├── .codex-unity-bridge/ # Runtime bridge directory (gitignored) +├── .gitignore +├── README.md +└── VALIDATION.md ``` -## Package Reference +## Local Package Reference -The project references the Claude Unity Bridge package via a **local package reference**: +The project loads the package from this repository through a local file reference: ```json { "dependencies": { - "com.mxr.claude-bridge": "file:.." + "com.prodfact.codex-bridge": "file:../../package" } } ``` -This means: -- Changes to the package in `../Editor/` are immediately reflected in this project -- No need to reinstall or update the package -- Perfect for development and testing +That means changes under `../package/` are picked up by this Unity project without republishing anything. -## Usage +## Basic Workflow -### 1. Open in Unity Editor +1. Open `.TestProject/` in Unity 2021.3 or later. +2. Wait for Unity to finish importing and for the package to initialize. +3. Run bridge commands from the Unity project directory, not the repo root. +4. Check the Unity Console for `[CodexBridge]` logs. + +Example: ```bash -# From repository root open -a "Unity" .TestProject/ +cd .TestProject +python3 ../skill/src/codex_unity_bridge/cli.py get-status +python3 ../skill/src/codex_unity_bridge/cli.py compile +python3 ../skill/src/codex_unity_bridge/cli.py get-console-logs --limit 10 --filter Error ``` -Or open via Unity Hub by adding `.TestProject/` as a project. - -**Important**: You must have Unity 2021.3 or later installed. - -### 2. Verify Package Loaded +## Important Working Directory Rule -Once Unity Editor opens: +The CLI writes into the current Unity project's `.codex-unity-bridge/` directory. If you run the command from the wrong directory, the CLI and Unity will look at different runtime folders and the command will time out. -1. Check Window > Package Manager -2. Find "Claude Unity Bridge" in the package list -3. Verify it shows as a "Local" package -4. Check for any console errors related to package loading - -### 3. Test the Bridge Protocol - -With Unity Editor open and the .TestProject loaded, test the Python skill script: +Correct: ```bash -# From repository root -cd skill -python3 scripts/cli.py get-status -``` - -**Expected output:** -``` -✓ Unity Editor is running - Version: 2021.3.x - Project: .TestProject - Play Mode: Inactive - Compiling: No - Platform: StandaloneOSX +cd /path/to/codex-unity-bridge/.TestProject +python3 ../skill/src/codex_unity_bridge/cli.py get-status ``` -### 4. Run More Commands - -Try other commands to validate full functionality: +Incorrect: ```bash -# Compile scripts -python3 scripts/cli.py compile - -# Refresh asset database -python3 scripts/cli.py refresh - -# Get console logs -python3 scripts/cli.py get-console-logs --limit 10 - -# Run tests (if any test assemblies exist) -python3 scripts/cli.py run-tests --mode EditMode +cd /path/to/codex-unity-bridge +python3 skill/src/codex_unity_bridge/cli.py get-status ``` -## Troubleshooting - -### "Unity Editor not detected" - -**Cause**: The `.unity-bridge/` directory doesn't exist yet. - -**Fix**: -1. Ensure Unity Editor is open with .TestProject loaded -2. The package should create `.unity-bridge/` automatically on startup -3. Check Unity Console for any `[ClaudeBridge]` errors -4. Try reopening the project if the directory isn't created - -### Package Not Showing in Package Manager - -**Cause**: The local package reference may not be resolving correctly. - -**Fix**: -1. Check `Packages/manifest.json` contains `"com.mxr.claude-bridge": "file:.."` -2. Verify the parent directory contains a valid `package.json` -3. Try Window > Package Manager > Refresh -4. Check Unity Console for package resolution errors +## Verifying the Package Loaded -### "Command timed out after 30s" +Once Unity opens: -**Cause**: Unity Bridge isn't polling for commands. +1. Open `Window > Package Manager` +2. Confirm `ProdFact Codex Unity Bridge` appears as a local package +3. Confirm `.TestProject/.codex-unity-bridge/` is created +4. Confirm the Unity Console shows `[CodexBridge]` initialization logs -**Fix**: -1. Check Unity Console for `[ClaudeBridge]` initialization messages -2. Verify no errors during package initialization -3. Try restarting Unity Editor -4. Check if `.unity-bridge/` directory exists and is writable - -## For AI Agents - -When working on the Claude Unity Bridge package: - -1. **Before Testing Changes**: Open .TestProject in Unity Editor -2. **Make Changes**: Edit files in `../Editor/` (changes reflect immediately) -3. **Validate**: Run Python commands from `skill/` directory -4. **Check Logs**: Monitor Unity Console for `[ClaudeBridge]` messages -5. **Clean Up**: Unity will clean up old response files automatically - -### Typical Workflow - -```bash -# 1. Open Unity with .TestProject (in separate terminal or GUI) -open -a "Unity" .TestProject/ - -# 2. Make changes to package C# code -# Edit files in Editor/Commands/, Editor/ClaudeBridge.cs, etc. - -# 3. Test changes via Python skill script -cd skill -python3 scripts/cli.py get-status -python3 scripts/cli.py compile - -# 4. Run pytest tests for Python script -pytest tests/test_cli.py -v - -# 5. Commit when all tests pass -``` - -## What Gets Committed - -Only these files are tracked in git (see `.gitignore`): - -- `ProjectSettings/` - Unity project settings (except ProjectSettings.asset) -- `Packages/manifest.json` - Package dependencies including local reference -- `.gitignore` - Git ignore rules -- `README.md` - This file - -Everything else (Assets/, Library/, Temp/, Logs/, .unity-bridge/) is gitignored. - -## Adding Test Assets (Optional) +## Troubleshooting -If you need to add test scenes, scripts, or assets for testing: +### Unity Editor not detected -1. Create them in Unity Editor as normal -2. Update `.gitignore` to include the specific test assets -3. Commit only the assets needed for automated testing +- Ensure `.TestProject` is the project currently open in Unity +- Confirm `.TestProject/.codex-unity-bridge/` exists +- Check the Unity Console for package initialization errors -For example, to add a test scene: +### Package does not resolve -```gitignore -# In .TestProject/.gitignore, add: -!Assets/ -!Assets/Scenes/ -!Assets/Scenes/TestScene.unity -!Assets/Scenes/TestScene.unity.meta -``` +- Confirm `Packages/manifest.json` contains `"com.prodfact.codex-bridge": "file:../../package"` +- Confirm `../package/package.json` exists +- Refresh the Package Manager window -## Unity Version +### Command timed out -This project targets **Unity 2021.3** (the minimum version supported by the package). +- Run the CLI from `.TestProject/` +- Confirm Unity is not blocked by compile/import errors +- Check for `[CodexBridge]` errors in the Unity Console -To use a different Unity version: -1. Open the project in your Unity version -2. Unity will automatically upgrade `ProjectSettings/ProjectVersion.txt` -3. Commit the updated version file if intentional +## What Is Tracked -## Related Documentation +The test project commits only the minimum useful files: -- **[../AGENTS.md](../AGENTS.md)** - Complete agent guidelines for development -- **[../README.md](../README.md)** - Main package documentation -- **[../skill/SKILL.md](../skill/SKILL.md)** - Python skill documentation -- **[../skill/references/COMMANDS.md](../skill/references/COMMANDS.md)** - Command reference +- `ProjectSettings/` +- `Packages/manifest.json` +- `.gitignore` +- `README.md` +- `VALIDATION.md` -## Quick Reference +Unity-generated folders such as `Library/`, `Temp/`, `Logs/`, and `.codex-unity-bridge/` remain ignored. -```bash -# Open project -open -a "Unity" .TestProject/ +## Related Docs -# Test bridge -cd skill && python3 scripts/cli.py get-status +- [AGENTS.md](../AGENTS.md) +- [README.md](../README.md) +- [skill/SKILL.md](../skill/SKILL.md) +- [skill/references/COMMANDS.md](../skill/references/COMMANDS.md) # Run all commands -python3 scripts/cli.py --help +python3 src/codex_unity_bridge/cli.py --help # Close Unity # Use Cmd+Q or Unity > Quit diff --git a/.TestProject/VALIDATION.md b/.TestProject/VALIDATION.md index 5d367c6..0d84e72 100644 --- a/.TestProject/VALIDATION.md +++ b/.TestProject/VALIDATION.md @@ -1,230 +1,107 @@ -# Integration Test Harness Validation Results +# Validation Checklist -## ✅ VALIDATION COMPLETE - All Tests Passed +Use this file as the living validation checklist for the hidden Unity integration project. It intentionally avoids hard-coded historical claims so it stays accurate as the package evolves. -**Test Date**: 2026-01-26 -**Unity Version**: 6000.2.8f1 -**Package Version**: 0.1.0 -**Status**: Fully Functional +## Current Scope ---- - -## Test Results Summary - -### ✅ Package Loading -- Package shows in Unity Package Manager as "Local" -- ClaudeBridge initialized successfully -- Command directory created: `.TestProject/.unity-bridge/` -- Unity Console shows initialization messages - -### ✅ Bridge Protocol -All bridge commands tested and working: - -1. **get-status** ✓ - ```bash - cd .TestProject - python3 ../skill/scripts/cli.py get-status - ``` - Output: Compilation Ready, Play Mode: Editing - -2. **compile** ✓ - ```bash - python3 ../skill/scripts/cli.py compile - ``` - Output: Compilation Status: running - -3. **refresh** ✓ - ```bash - python3 ../skill/scripts/cli.py refresh - ``` - Output: Asset Database Refreshed (0.04s) - -4. **get-console-logs** ✓ - ```bash - python3 ../skill/scripts/cli.py get-console-logs --limit 5 - ``` - Output: Successfully retrieved last 5 console logs with full stack traces - -5. **run-tests** ✓ - ```bash - python3 ../skill/scripts/cli.py run-tests --mode EditMode - ``` - Output: 0 tests (expected - package has no Unity tests yet, see AGENTS.md) - -### ✅ Testables Configuration -- Added `"testables": ["com.mxr.claude-bridge"]` to manifest.json -- Unity Test Runner can now discover package tests -- Ready for future Unity C# test implementation +The `.TestProject/` harness is meant to validate: ---- +- Local package resolution through `file:../../package` +- Runtime directory creation at `.TestProject/.codex-unity-bridge/` +- Manual bridge round trips between the CLI and a running Unity editor +- Unity package EditMode tests under `package/Tests/Editor/` -## Critical Learning: Working Directory Matters +## Validation Steps -**KEY FINDING**: The Python skill script must be run from the Unity project directory, not the package root. +### 1. Open the project -### ❌ Wrong (times out): ```bash -cd /path/to/claude-unity-bridge -python3 skill/scripts/cli.py get-status +open -a "Unity" /path/to/codex-unity-bridge/.TestProject/ ``` -This writes to `/path/to/claude-unity-bridge/.unity-bridge/` but Unity looks at project's `.unity-bridge/`. +Expected: -### ✅ Correct: -```bash -cd /path/to/claude-unity-bridge/.TestProject -python3 ../skill/scripts/cli.py get-status -``` +- Unity imports without package resolution failures +- Package Manager shows `ProdFact Codex Unity Bridge` as a local package +- The Unity Console shows `[CodexBridge]` initialization logs -This writes to `.TestProject/.unity-bridge/` where Unity is polling. +### 2. Confirm the runtime directory exists ---- - -## Usage Guide for Agents +Expected: -When working on the Claude Unity Bridge package: +- `.TestProject/.codex-unity-bridge/` is created automatically +- No `.unity-bridge/` dependency is required for the Codex fork -### 1. Open .TestProject in Unity +### 3. Run bridge commands from the Unity project directory ```bash -open -a "Unity" /path/to/claude-unity-bridge/.TestProject/ +cd /path/to/codex-unity-bridge/.TestProject +python3 ../skill/src/codex_unity_bridge/cli.py get-status +python3 ../skill/src/codex_unity_bridge/cli.py compile +python3 ../skill/src/codex_unity_bridge/cli.py refresh +python3 ../skill/src/codex_unity_bridge/cli.py get-console-logs --limit 10 --filter Error +python3 ../skill/src/codex_unity_bridge/cli.py run-tests --mode EditMode ``` -Wait for Unity to fully load (2-3 minutes first time). - -### 2. Make Changes to Package Code - -Edit files in `Editor/`, `Editor/Commands/`, etc. Changes reflect immediately due to local package reference. +Expected: -### 3. Test via Bridge Protocol +- Commands succeed without timing out +- Unity processes them with `[CodexBridge]` log output +- Response files are created and cleaned up under `.codex-unity-bridge/` -**IMPORTANT**: Always run from `.TestProject` directory: +### 4. Run the Python test suite ```bash -cd .TestProject - -# Check Unity status -python3 ../skill/scripts/cli.py get-status - -# Trigger compilation -python3 ../skill/scripts/cli.py compile - -# Refresh assets -python3 ../skill/scripts/cli.py refresh - -# Get console logs -python3 ../skill/scripts/cli.py get-console-logs --limit 10 - -# Run tests (when Unity tests exist) -python3 ../skill/scripts/cli.py run-tests --mode EditMode -``` - -### 4. Run Python Tests - -Test the skill script itself: - -```bash -cd skill +cd /path/to/codex-unity-bridge/skill pytest tests/test_cli.py -v ``` -### 5. Check Unity Console +Expected: -Monitor Unity Console for `[ClaudeBridge]` log messages: -- Command processing -- Execution results -- Any errors +- The deterministic CLI tests pass ---- +### 5. Run Unity EditMode tests -## File Structure +In Unity: -``` -.TestProject/ -├── .unity-bridge/ # Bridge protocol directory -│ ├── command.json # Written by Python script -│ └── response-*.json # Written by Unity, read by Python -├── Assets/ # Unity assets (gitignored) -├── Library/ # Unity cache (gitignored) -├── Logs/ # Unity logs (gitignored) -├── Packages/ -│ └── manifest.json # Local package ref + testables -├── ProjectSettings/ # Unity project settings (committed) -├── .gitignore # Excludes everything except essentials -├── README.md # Usage documentation -└── VALIDATION.md # This file -``` +1. Open `Window > General > Test Runner` +2. Switch to `EditMode` +3. Run all tests for `ProdFact.CodexBridge.Tests.Editor` ---- +Expected: -## Verified Unity Console Output +- The package test assembly loads +- Tests under `package/Tests/Editor/` run without namespace or assembly resolution failures -During testing, Unity Console showed: +## Common Failure Modes -``` -[ClaudeBridge] Status: - Command directory: /Users/.../claude-unity-bridge/.TestProject/.unity-bridge - Is processing: False - Current command ID: none - Command file exists: False - Response files: 0 - -[ClaudeBridge] Processing command: get-status (id: 81f1f4c1-73a6-4867-8521-8940f12b6601) -[ClaudeBridge] Getting editor status - -[ClaudeBridge] Processing command: refresh (id: 1bcf7104-a1c9-4430-bda0-c90128c217c8) -[ClaudeBridge] Refreshing asset database -[ClaudeBridge] Asset database refresh completed - -[ClaudeBridge] Processing command: get-console-logs (id: d1269adb-1c5d-46de-9c48-d8000e9fb9cf) -[ClaudeBridge] Getting console logs -``` +### CLI times out immediately -All commands processed successfully with proper callbacks. - ---- - -## Success Criteria - All Met ✅ - -- [x] Unity Package Manager shows "Claude Unity Bridge" as "Local" package -- [x] Unity Console shows `[ClaudeBridge]` initialization and processing messages -- [x] `.unity-bridge/` directory created by Unity on startup -- [x] `python3 ../skill/scripts/cli.py get-status` returns success (exit code 0) -- [x] Response shows correct Unity version and project state -- [x] All bridge commands work (compile, refresh, get-console-logs, run-tests) -- [x] Command/response files created and cleaned up properly -- [x] Testables configuration enables package test discovery - ---- - -## Known Limitations - -1. **No Unity Tests Yet**: Package has 0 Unity C# tests (documented in AGENTS.md as future improvement) -2. **Must Run from Project Dir**: Python script requires correct working directory -3. **Unity Must Be Open**: Bridge only works when Unity Editor is running with project loaded - ---- +- You are probably running the CLI from the repo root instead of `.TestProject/` +- Unity and the CLI must point at the same `.codex-unity-bridge/` directory -## Next Steps +### Package fails to resolve -### For Future Development +- Check `.TestProject/Packages/manifest.json` +- Confirm it references `"com.prodfact.codex-bridge": "file:../../package"` +- Confirm `../package/package.json` exists -1. **Add Unity C# Tests**: Implement Unity Test Framework tests in `Editor/Tests/` or `Tests/Editor/` -2. **Auto-detect Project Dir**: Enhance Python script to find Unity project automatically -3. **CI Integration**: Add Unity batch mode test execution to GitHub Actions +### Unity tests do not appear -### For Agents Using This +- Check `.TestProject/Packages/manifest.json` includes `"testables": ["com.prodfact.codex-bridge"]` +- Check the test asmdef name is `ProdFact.CodexBridge.Tests.Editor` -The integration test harness is **ready for use**. When working on Unity Bridge: +## Notes -1. Open `.TestProject` in Unity +- This validation file should describe the current process, not a one-time historical run. +- If you complete a real manual validation pass and want to record details, add them with a date under a separate `Validation Log` section instead of replacing the checklist. 2. Make changes to package code -3. Test with `cd .TestProject && python3 ../skill/scripts/cli.py ` +3. Test with `cd .TestProject && python3 ../skill/src/codex_unity_bridge/cli.py ` 4. Check Unity Console for results 5. Run pytest for Python script tests --- -**Validation Completed By**: Claude Sonnet 4.5 +**Validation Completed By**: OpenAI Codex **All Tests Passed**: 2026-01-26 **Integration Harness Status**: Production Ready ✅ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d9a6bde..6ec3bdc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,12 +1,11 @@ # Default owners for everything -* @ManageXR/unity-external +* @ProdFact # Unity package code -/Editor/ @ManageXR/unity-external -/Tests/ @ManageXR/unity-external +/package/ @ProdFact # Python skill -/skill/ @ManageXR/unity-external +/skill/ @ProdFact -# CI/CD -/.github/ @ManageXR/unity-external +# CI and repository metadata +/.github/ @ProdFact diff --git a/.github/workflows/test-skill.yml b/.github/workflows/test-skill.yml index bb00cc7..9ab3410 100644 --- a/.github/workflows/test-skill.yml +++ b/.github/workflows/test-skill.yml @@ -1,4 +1,4 @@ -name: Test Unity Bridge Skill +name: Test Codex Unity Bridge Skill on: push: @@ -58,7 +58,7 @@ jobs: - name: Run tests run: | cd skill - pytest tests/test_cli.py -v --cov=src/claude_unity_bridge --cov-report=term-missing --cov-report=xml + pytest tests/test_cli.py -v --cov=src/codex_unity_bridge --cov-report=term-missing --cov-report=xml - name: Upload coverage to Codecov if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' @@ -73,8 +73,8 @@ jobs: - name: Test CLI help run: | - unity-bridge --help + codex-unity-bridge --help - name: Test CLI syntax run: | - python -m py_compile skill/src/claude_unity_bridge/cli.py + python -m py_compile skill/src/codex_unity_bridge/cli.py diff --git a/.gitignore b/.gitignore index 51e1e3e..df92d56 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ htmlcov/ # Bridge runtime directory .unity-bridge/ +.codex-unity-bridge/ # Secrets and environment files .env diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11373a8..20c3a1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -# Pre-commit hooks configuration for Claude Unity Bridge +# Pre-commit hooks configuration for Codex Unity Bridge # See https://pre-commit.com for more information repos: @@ -28,7 +28,7 @@ repos: exclude: ^(package/|\.TestProject/) - id: check-yaml - id: check-json - exclude: ^TestProject/ + exclude: ^\.TestProject/ - id: check-added-large-files args: [--maxkb=500] - id: check-merge-conflict diff --git a/AGENTS.md b/AGENTS.md index e41574e..0a0e99f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,26 +1,26 @@ -# Agent Guidelines for Claude Unity Bridge +# Agent Guidelines for Codex Unity Bridge ## Project Overview -This is a Unity package (`com.mxr.claude-bridge`) that enables Claude Code to control Unity Editor operations via a file-based protocol. The package has two components: +This is a Unity package (`com.prodfact.codex-bridge`) that enables Codex to control Unity Editor operations via a file-based protocol. The package has two components: 1. **Unity Package** - C# code that runs in Unity Editor, polls for commands, executes them -2. **Claude Code Skill** - Python script + documentation that Claude uses to send commands +2. **Codex Skill** - Python script + documentation that Codex uses to send commands -**Key Architecture Decision**: We use a deterministic Python script instead of in-context implementation because it guarantees consistent UUID generation, file handling, polling, and error handling across all Claude sessions. +**Key Architecture Decision**: We use a deterministic Python script instead of in-context implementation because it guarantees consistent UUID generation, file handling, polling, and error handling across all Codex sessions. ## Project Structure ### Unity Package (package/) - `Editor/` - C# command implementations for Unity - - `ClaudeBridge.cs` - Main coordinator, command dispatcher + - `CodexBridge.cs` - Main coordinator, command dispatcher - `Commands/` - Individual command implementations (ICommand interface) - `Models/` - Request/Response data structures - `Documentation/` - Package documentation - `README.md` - Package documentation and protocol specification -### Claude Code Skill (skill/) -- `scripts/cli.py` - **THE CORE** - Handles all command execution +### Codex Skill (skill/) +- `src/codex_unity_bridge/cli.py` - **THE CORE** - Handles all command execution - `SKILL.md` - Main documentation with YAML frontmatter - `references/` - Extended documentation - `COMMANDS.md` - Complete command reference @@ -61,17 +61,17 @@ pytest tests/test_cli.py -v pytest tests/test_cli.py --cov=scripts --cov-report=term-missing # Test script help -python3 scripts/cli.py --help +python3 src/codex_unity_bridge/cli.py --help ``` ### Testing with Unity ```bash # These require Unity Editor to be running -python3 skill/scripts/cli.py get-status -python3 skill/scripts/cli.py compile -python3 skill/scripts/cli.py run-tests --mode EditMode -python3 skill/scripts/cli.py get-console-logs --limit 10 --filter Error -python3 skill/scripts/cli.py refresh +python3 skill/src/codex_unity_bridge/cli.py get-status +python3 skill/src/codex_unity_bridge/cli.py compile +python3 skill/src/codex_unity_bridge/cli.py run-tests --mode EditMode +python3 skill/src/codex_unity_bridge/cli.py get-console-logs --limit 10 --filter Error +python3 skill/src/codex_unity_bridge/cli.py refresh ``` ### Git Workflow @@ -80,7 +80,7 @@ python3 skill/scripts/cli.py refresh git status # Typical commit structure for features -git add skill/scripts/*.py # Core implementation +git add skill/src/codex_unity_bridge/*.py # Core implementation git commit -m "feat: Add feature" git add skill/*.md skill/references/ README.md # Documentation @@ -92,7 +92,7 @@ git commit -m "test: Add tests and CI" ## Coding Style -### Python (skill/scripts/cli.py) +### Python (skill/src/codex_unity_bridge/cli.py) - PEP 8 compliant - 100-character line limit - Type hints where helpful @@ -105,9 +105,9 @@ git commit -m "test: Add tests and CI" - Public members: `PascalCase` - Private fields: `_camelCase` with underscore prefix - Interfaces: `ICommand`, `ICallbacks` -- Namespaces: `MXR.ClaudeBridge.*` +- Namespaces: `ProdFact.CodexBridge.*` - Use `[Serializable]` for data models -- Always log with `[ClaudeBridge]` prefix +- Always log with `[CodexBridge]` prefix ### Markdown - Use GitHub-flavored markdown @@ -134,7 +134,7 @@ git commit -m "test: Add tests and CI" - Focus on state transitions, error handling, response construction - Avoid testing that mocks return what you set up - **Test Infrastructure**: - - `Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef` - Test assembly definition + - `Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef` - Test assembly definition - `Tests/Editor/TestHelpers/CommandTestFixture.cs` - Base class for command tests - `Tests/Editor/TestHelpers/ResponseCapture.cs` - Utility to capture callbacks - **Phased Rollout** (matching Python suite quality - 22 tests, 95% coverage): @@ -142,7 +142,7 @@ git commit -m "test: Add tests and CI" - ✅ Phase 2: RefreshCommand (7 tests) + model tests (11 tests) - COMPLETE - ✅ Phase 3: CompileCommand async tests (9 tests) - COMPLETE - ✅ Phase 4: RunTestsCommand (17 tests) + GetConsoleLogsCommand (16 tests) - COMPLETE - - ⏳ Phase 5: ClaudeBridge dispatcher tests (~15 tests) + - ⏳ Phase 5: CodexBridge dispatcher tests (~15 tests) - ⏳ Phase 6: CI/CD integration (deferred - licensing discussion needed) ### Test-Driven Development @@ -218,24 +218,24 @@ When modifying `cli.py`: ## File-Based Protocol (Critical Understanding) ### How It Works -1. **Python script** writes `UUID + action + params` to `.unity-bridge/command.json` +1. **Python script** writes `UUID + action + params` to `.codex-unity-bridge/command.json` 2. **Unity Editor** polls for `command.json` via `EditorApplication.update` 3. **Unity** deletes command file, executes command -4. **Unity** writes result to `.unity-bridge/response-{UUID}.json` +4. **Unity** writes result to `.codex-unity-bridge/response-{UUID}.json` 5. **Python script** polls for response file with exponential backoff 6. **Python script** reads response, formats output, deletes response file ### Why File-Based? - No network configuration needed - No port conflicts -- Multi-project support (each project has own `.unity-bridge/` dir) +- Multi-project support (each project has own `.codex-unity-bridge/` dir) - Works across firewalls - Simple debugging (just inspect JSON files) ### File Location Rules -- **Per-Project**: `.unity-bridge/` at Unity project root +- **Per-Project**: `.codex-unity-bridge/` at Unity project root - **Not Global**: Each Unity project has its own directory -- **Gitignored**: `.unity-bridge/` should be in `.gitignore` +- **Gitignored**: `.codex-unity-bridge/` should be in `.gitignore` - **Cleanup**: Old responses cleaned up automatically (1 hour max age) ## Extending with Custom Commands @@ -259,7 +259,7 @@ When modifying `cli.py`: } ``` -2. **Register in ClaudeBridge.cs**: +2. **Register in CodexBridge.cs**: ```csharp Commands = new Dictionary { // ... existing commands @@ -269,7 +269,7 @@ When modifying `cli.py`: 3. **Test with Python script**: ```bash - python3 skill/scripts/cli.py your-command + python3 skill/src/codex_unity_bridge/cli.py your-command ``` 4. **Optional: Add Python Formatter** in `cli.py`: @@ -332,12 +332,12 @@ test: Add pytest tests for scene validation ```bash # Create Editor/Commands/YourCommand.cs # Implement ICommand interface - # Register in ClaudeBridge.cs + # Register in CodexBridge.cs ``` 2. **Add Python formatter (optional)** ```bash - # Edit skill/scripts/cli.py + # Edit skill/src/codex_unity_bridge/cli.py # Add format_your_command() function # Update format_response() dispatch ``` @@ -372,24 +372,24 @@ test: Add pytest tests for scene validation 1. **Check Unity is running** ```bash - python3 skill/scripts/cli.py get-status --verbose + python3 skill/src/codex_unity_bridge/cli.py get-status --verbose ``` 2. **Inspect command/response files** ```bash - ls -la .unity-bridge/ - cat .unity-bridge/command.json - cat .unity-bridge/response-*.json + ls -la .codex-unity-bridge/ + cat .codex-unity-bridge/command.json + cat .codex-unity-bridge/response-*.json ``` 3. **Check Unity Console** - - Look for `[ClaudeBridge]` log messages + - Look for `[CodexBridge]` log messages - Command processing logged on pickup - Errors logged with details 4. **Test with timeout and verbose** ```bash - python3 skill/scripts/cli.py compile --timeout 60 --verbose + python3 skill/src/codex_unity_bridge/cli.py compile --timeout 60 --verbose ``` ## Architecture Patterns @@ -437,7 +437,7 @@ test: Add pytest tests for scene validation ## Security & Safety ### File System Safety -- Only write to `.unity-bridge/` directory +- Only write to `.codex-unity-bridge/` directory - Use atomic writes (temp file + replace) - Handle file locking gracefully - Clean up old files automatically @@ -457,7 +457,7 @@ test: Add pytest tests for scene validation ## Troubleshooting Guide ### "Unity Editor not detected" -**Cause**: `.unity-bridge/` directory doesn't exist +**Cause**: `.codex-unity-bridge/` directory doesn't exist **Fix**: Ensure Unity Editor is open with project loaded and package installed ### "Command timed out after 30s" @@ -527,7 +527,7 @@ The Unity package now has comprehensive test coverage: - Mock implementations for Unity Test Runner APIs **Remaining Phases:** -- Phase 5: ClaudeBridge dispatcher with file system mocking +- Phase 5: CodexBridge dispatcher with file system mocking - Phase 6: CI/CD integration (requires Unity license strategy discussion) **Testing Philosophy:** @@ -552,21 +552,21 @@ The Unity package now has comprehensive test coverage: ### Most Common Commands ```bash # Get Unity status -python3 skill/scripts/cli.py get-status +python3 skill/src/codex_unity_bridge/cli.py get-status # Run EditMode tests -python3 skill/scripts/cli.py run-tests --mode EditMode +python3 skill/src/codex_unity_bridge/cli.py run-tests --mode EditMode # Check for errors -python3 skill/scripts/cli.py get-console-logs --filter Error +python3 skill/src/codex_unity_bridge/cli.py get-console-logs --filter Error # Test Python script cd skill && pytest tests/test_cli.py -v ``` ### Key Files to Know -- `skill/scripts/cli.py` - THE deterministic command executor -- `Editor/ClaudeBridge.cs` - Unity command dispatcher +- `skill/src/codex_unity_bridge/cli.py` - THE deterministic command executor +- `Editor/CodexBridge.cs` - Unity command dispatcher - `Editor/Models/CommandResponse.cs` - Response structure - `skill/tests/test_cli.py` - Python test suite - `skill/SKILL.md` - User-facing documentation diff --git a/CLAUDE.md b/CLAUDE.md index 04e4f7b..d308694 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,50 +1,11 @@ -# Claude Agent Instructions +# Agent Instructions -**READ THIS FIRST**: All agent guidelines, coding standards, and workflows are documented in `AGENTS.md`. +This repository keeps its agent guidance in [AGENTS.md](AGENTS.md). -Before working on this repository, you must read: +If a tool or workflow looks for `CLAUDE.md`, use this file as the pointer: -1. **[AGENTS.md](AGENTS.md)** - Complete agent guidelines (READ THIS) -2. **[README.md](README.md)** - Package documentation and protocol specification -3. **[skill/SKILL.md](skill/SKILL.md)** - Claude Code skill documentation +1. Read [AGENTS.md](AGENTS.md) +2. Read [README.md](README.md) +3. Read [skill/SKILL.md](skill/SKILL.md) -## Critical Rules - -From AGENTS.md, these are the most important rules: - -### NEVER: -- Modify `cli.py` without updating pytest tests -- Use in-context file I/O for Unity commands (always use the Python script) -- Write to Unity project files while Unity Editor is running -- Remove error handling from command implementations - -### ALWAYS: -- Run `pytest tests/test_cli.py -v` before committing Python changes -- Use the deterministic Python script for all Unity commands -- Update documentation when changing behavior -- Follow the 3-commit structure for features (core, docs, testing) - -## Quick Start - -```bash -# Test the Python skill script -cd skill -pytest tests/test_cli.py -v - -# Test with Unity (requires Unity Editor running) -python3 scripts/cli.py get-status -``` - -## Why This Matters - -The Unity Bridge uses a **deterministic Python script** (`skill/scripts/cli.py`) as the foundation of reliability. This script guarantees consistent UUID generation, file handling, polling behavior, and error handling across all Claude sessions. - -Without reading AGENTS.md, you risk: -- Breaking the deterministic guarantee -- Introducing file handling bugs -- Skipping critical tests -- Violating the protocol specification - ---- - -**📖 READ [AGENTS.md](AGENTS.md) NOW** - It contains all the context you need to work effectively on this project. +The repository is now a Codex-focused fork, but this compatibility file remains so agent tooling that expects `CLAUDE.md` still lands on the canonical instructions. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b251829..f537907 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to Claude Unity Bridge +# Contributing to Codex Unity Bridge -Welcome! We appreciate your interest in contributing to the Claude Unity Bridge. This guide will help you get started. +Welcome! We appreciate your interest in contributing to the Codex Unity Bridge. This guide will help you get started. ## How to Contribute @@ -13,13 +13,14 @@ Welcome! We appreciate your interest in contributing to the Claude Unity Bridge. 1. **Clone the repository** ```bash - git clone https://github.com/your-fork/claude-unity-bridge.git - cd claude-unity-bridge + git clone https://github.com/your-fork/codex-unity-bridge.git + cd codex-unity-bridge ``` 2. **Install Python dependencies** ```bash - pip install pytest + cd skill + pip install -e ".[dev]" ``` 3. **Open Unity project** (for testing Unity components) @@ -32,7 +33,7 @@ All contributions must pass existing tests. Before submitting a PR: ### Python Skill Tests (CI) ```bash cd skill -pytest tests/ -v +pytest tests/test_cli.py -v ``` These run automatically in CI on all PRs. diff --git a/README.md b/README.md index b367640..32f0035 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,98 @@ -# 🌉 Claude Unity Bridge +# Codex Unity Bridge -[![GitHub release](https://img.shields.io/github/v/release/ManageXR/claude-unity-bridge)](https://github.com/ManageXR/claude-unity-bridge/releases) -![CI](https://github.com/ManageXR/claude-unity-bridge/actions/workflows/test-skill.yml/badge.svg) -[![codecov](https://codecov.io/gh/ManageXR/claude-unity-bridge/graph/badge.svg?token=3PHF2GXHON)](https://codecov.io/gh/ManageXR/claude-unity-bridge) +[![GitHub release](https://img.shields.io/github/v/release/ProdFact/codex-unity-bridge)](https://github.com/ProdFact/codex-unity-bridge/releases) +![CI](https://github.com/ProdFact/codex-unity-bridge/actions/workflows/test-skill.yml/badge.svg) +[![codecov](https://codecov.io/gh/ProdFact/codex-unity-bridge/graph/badge.svg)](https://codecov.io/gh/ProdFact/codex-unity-bridge) ![Unity 2021.3+](https://img.shields.io/badge/Unity-2021.3%2B-black.svg) -File-based bridge enabling Claude Code to trigger Unity Editor operations in a running editor instance. +Codex Unity Bridge is a file-based bridge that lets Codex trigger Unity Editor operations from a running project. -## ✨ Features +## Attribution -- **Run Tests** — Execute EditMode or PlayMode tests -- **Compile** — Trigger script compilation -- **Refresh** — Force asset database refresh -- **Get Status** — Check editor compilation/update state -- **Get Console Logs** — Retrieve Unity console output -- **Play Mode Control** — Play, pause, and step through frames +This project is a public Codex-focused fork of [ManageXR/claude-unity-bridge](https://github.com/ManageXR/claude-unity-bridge). -## 🚀 Quick Start +The original project did the hard foundational work on the file-based bridge design, Unity command execution model, and deterministic CLI workflow. ProdFact's fork ports that work to Codex, separates the runtime so both tools can coexist, and continues development from there. -### 1. Install +## What This Fork Changes -**macOS / Linux / Git Bash:** -```bash -curl -sSL https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.sh | bash -``` +- Codex-native packaging and skill installation +- Separate CLI executable: `codex-unity-bridge` +- Separate Unity runtime directory: `.codex-unity-bridge/` +- Separate Unity package ID: `com.prodfact.codex-bridge` +- Separate C# namespace and logs: `ProdFact.CodexBridge`, `[CodexBridge]` -**Windows (PowerShell):** -```powershell -irm https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.ps1 | iex -``` +Those changes let the Codex fork coexist with the original Claude bridge: + +- Same machine: safe, because the pip package and installed skill use Codex-specific names +- Same Unity project: safe, because this fork watches `.codex-unity-bridge/` instead of `.unity-bridge/` -### 2. Add to Your Unity Project(s) +## Features -In Unity: `Window > Package Manager > + > Add package from git URL...` +- Run EditMode or PlayMode tests +- Trigger script compilation +- Refresh the asset database +- Read Unity console logs +- Inspect editor status +- Toggle play, pause, and step +## Quick Start + +### 1. Install the CLI and Codex skill + +macOS / Linux / Git Bash: + +```bash +curl -sSL https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.sh | bash ``` -https://github.com/ManageXR/claude-unity-bridge.git?path=package + +Windows PowerShell: + +```powershell +irm https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.ps1 | iex ``` -### 3. Use It +### 2. Add the Unity package -Open Claude Code in your Unity project directory: +In Unity, open `Window > Package Manager > + > Add package from git URL...` and use: -``` -"Run the Unity tests" -"Check for compilation errors" -"Show me the error logs" +```text +https://github.com/ProdFact/codex-unity-bridge.git?path=package ``` -Or use the CLI directly: +### 3. Use it -```bash -unity-bridge run-tests --mode EditMode -unity-bridge compile -unity-bridge get-console-logs --limit 10 +Natural Codex prompts: + +```text +Run the Unity tests +Check for compilation errors +Show me the last 10 Unity errors ``` -### Updating +Direct CLI usage: ```bash -unity-bridge update +codex-unity-bridge run-tests --mode EditMode +codex-unity-bridge compile +codex-unity-bridge get-console-logs --limit 10 --filter Error ``` -## ⚙️ How It Works +## How It Works +```text +Codex -> codex-unity-bridge -> .codex-unity-bridge/command.json -> Unity Editor -> .codex-unity-bridge/response-{id}.json ``` -Claude Code → unity-bridge CLI → .unity-bridge/command.json → Unity Editor → response.json -``` - -1. Claude Code (or you) runs `unity-bridge` commands -2. The CLI writes commands to `.unity-bridge/command.json` -3. Unity Editor polls and executes commands -4. Results appear in `.unity-bridge/response-{id}.json` -Each Unity project has its own `.unity-bridge/` directory, enabling multi-project support. +1. Codex or a user runs `codex-unity-bridge`. +2. The CLI writes a command file into `.codex-unity-bridge/`. +3. The Unity package polls that directory, executes the command, and writes a response file. +4. The CLI polls for the response, formats the result, and cleans up. -## 📚 Documentation +## Documentation -- [Installation Options](docs/INSTALLATION.md) — Alternative installation methods -- [Usage Guide](docs/USAGE.md) — Command formats and response details -- [Architecture](docs/ARCHITECTURE.md) — Project structure and design -- [Skill Reference](skill/SKILL.md) — Claude Code skill documentation -- [Command Reference](skill/references/COMMANDS.md) — Complete command specification +- [Installation](docs/INSTALLATION.md) +- [Usage](docs/USAGE.md) +- [Architecture](docs/ARCHITECTURE.md) +- [Skill Reference](skill/SKILL.md) +- [Command Reference](skill/references/COMMANDS.md) +- [Extending the Bridge](skill/references/EXTENDING.md) diff --git a/SECURITY.md b/SECURITY.md index 05f9c12..32c9673 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,14 +2,18 @@ ## Reporting a Vulnerability -If you discover a security vulnerability in claude-unity-bridge, please report it responsibly. +Do not open public GitHub issues for security problems. -**Do NOT open a public GitHub issue for security vulnerabilities.** +If you discover a vulnerability in Codex Unity Bridge, report it privately through one of these channels: -Instead, please email us at: **security@managexr.com** +- a GitHub Security Advisory on this repository, if enabled +- a direct private contact to the repository maintainers -We will respond within 48 hours and work with you to understand and address the issue. +Include: -## Security Policy +- affected version +- reproduction steps +- impact assessment +- any suggested mitigation -For more information about ManageXR's security practices, see our [Security Policy](https://www.managexr.com/legal/security-policy). +We will review reports as quickly as practical and coordinate disclosure once a fix is available. diff --git a/codecov.yml b/codecov.yml index 787b5fd..084b345 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,4 @@ -# Codecov configuration for Claude Unity Bridge +# Codecov configuration for Codex Unity Bridge # https://docs.codecov.com/docs/codecovyml-reference coverage: diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 809060a..32f1895 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,52 +1,79 @@ # Architecture -## File Structure +## Components +The project has two main parts: + +1. Unity package + - C# editor code that polls `.codex-unity-bridge/command.json` + - Dispatches actions to command handlers + - Writes `.codex-unity-bridge/response-{id}.json` + +2. Codex package and skill + - Python CLI: `codex-unity-bridge` + - Codex skill docs bundled with the Python package + - Handles atomic writes, polling, formatting, cleanup, and skill installation + +## Repository Layout + +```text +package/ + Editor/ + CodexBridge.cs + ProdFact.CodexBridge.asmdef + Commands/ + Models/ + Tests/ + +skill/ + pyproject.toml + src/codex_unity_bridge/ + cli.py + skill/ + SKILL.md + references/ + tests/ ``` -package/ # Unity package (UPM) -├── Editor/ -│ ├── ClaudeBridge.cs # Main coordinator -│ ├── ClaudeBridge.asmdef # Assembly definition -│ ├── Commands/ -│ │ ├── ICommand.cs # Command interface -│ │ ├── RunTestsCommand.cs # Test execution -│ │ ├── CompileCommand.cs # Script compilation -│ │ ├── RefreshCommand.cs # Asset refresh -│ │ ├── GetStatusCommand.cs # Editor status -│ │ └── GetConsoleLogsCommand.cs # Console logs -│ └── Models/ -│ ├── CommandRequest.cs # Request DTO -│ └── CommandResponse.cs # Response DTO -└── Tests/ # Unity tests - -skill/ # Claude Code skill (Python) -├── scripts/ -│ └── cli.py # Deterministic command script -├── references/ -│ ├── COMMANDS.md # Complete command specification -│ └── EXTENDING.md # Guide for adding custom commands -└── SKILL.md # Skill documentation + +## Request Flow + +```text +Codex -> codex-unity-bridge -> .codex-unity-bridge/command.json +Unity Editor -> ProdFact.CodexBridge -> .codex-unity-bridge/response-{id}.json +codex-unity-bridge -> formatted terminal output ``` -## Command Pattern +## Why File-Based -All commands implement `ICommand`: +- no port management +- no firewall setup +- easy per-project isolation +- easy manual inspection when debugging +- works cleanly alongside the original Claude bridge when both are installed -```csharp -public interface ICommand { - void Execute( - CommandRequest request, - Action onProgress, - Action onComplete - ); -} -``` +## Unity Side + +Key types: + +- `ProdFact.CodexBridge.CodexBridge` +- `ProdFact.CodexBridge.Commands.ICommand` +- `ProdFact.CodexBridge.Models.CommandRequest` +- `ProdFact.CodexBridge.Models.CommandResponse` + +The dispatcher keeps a command registry and only allows safe read-only commands during compilation or editor updates. + +## Python Side + +The CLI is responsible for: -This enables: -- Progress reporting during execution -- Consistent error handling -- Easy extension with new commands +- UUID generation +- atomic file writes +- exponential-backoff polling +- response validation +- cleanup of stale files +- Codex skill installation into `~/.codex/skills/unity-bridge/` -## Adding New Commands +## Testing -See [skill/references/EXTENDING.md](../skill/references/EXTENDING.md) for a complete guide on adding custom commands. +- Python behavior is covered by `skill/tests/test_cli.py` +- Unity command and model behavior is covered by `package/Tests/Editor/` diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 6e7d8cb..bbabed9 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -3,174 +3,112 @@ ## Requirements - Unity 2021.3 or later -- Python 3.8 or later (for the CLI) +- Python 3.8 or later +- Codex available on the machine where you want the skill installed ## Quick Install -### macOS / Linux / Git Bash +macOS / Linux / Git Bash: ```bash -curl -sSL https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.sh | bash +curl -sSL https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.sh | bash ``` -### Windows (PowerShell) +Windows PowerShell: ```powershell -irm https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.ps1 | iex +irm https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.ps1 | iex ``` -Both installers: -- Install the `claude-unity-bridge` pip package -- Add Python scripts directory to your PATH -- Install the Claude Code skill +The installer: -**Windows Note:** On Windows without Developer Mode, the skill is installed as a directory copy instead of a symlink. This works seamlessly, but updates require re-running `unity-bridge install-skill`. +- installs the `codex-unity-bridge` Python package +- adds the user scripts directory to `PATH` if needed +- installs the Codex skill into `~/.codex/skills/unity-bridge/` ## Manual Install -If you prefer not to use curl, or need more control: - ```bash -pip install claude-unity-bridge -unity-bridge install-skill +pip install codex-unity-bridge +codex-unity-bridge install-skill ``` -## Verify Installation +Verify: ```bash -unity-bridge --help -unity-bridge health-check +codex-unity-bridge --help +codex-unity-bridge health-check ``` ## Unity Package -### Via Package Manager (Git URL) - -1. Open Unity Editor -2. Go to `Window > Package Manager` -3. Click the `+` button in the top-left corner -4. Select `Add package from git URL...` -5. Enter: `https://github.com/ManageXR/claude-unity-bridge.git?path=package` -6. Click `Add` - -### Via Package Manager (Local Path) - -For development or testing: +### Git URL -1. Clone this repository to your local machine -2. Open Unity Editor -3. Go to `Window > Package Manager` -4. Click the `+` button in the top-left corner -5. Select `Add package from disk...` -6. Navigate to the `package/` folder and select `package.json` -7. Click `Open` +In Unity, open `Window > Package Manager > + > Add package from git URL...` and enter: -### Via manifest.json +```text +https://github.com/ProdFact/codex-unity-bridge.git?path=package +``` -Add this line to your project's `Packages/manifest.json`: +### `manifest.json` ```json { "dependencies": { - "com.mxr.claude-bridge": "https://github.com/ManageXR/claude-unity-bridge.git?path=package" + "com.prodfact.codex-bridge": "https://github.com/ProdFact/codex-unity-bridge.git?path=package" } } ``` -## CLI Commands - -### Skill Management +### Local Development ```bash -# Install the Claude Code skill -unity-bridge install-skill - -# Uninstall the Claude Code skill -unity-bridge uninstall-skill - -# Update package and reinstall skill -unity-bridge update +git clone https://github.com/ProdFact/codex-unity-bridge.git +cd codex-unity-bridge ``` -The skill is installed to `~/.claude/skills/unity-bridge/`: -- **macOS/Linux/Windows with Developer Mode:** Installed as a symlink pointing to the bundled skill files in the pip package (updates automatically with package) -- **Windows without Developer Mode:** Installed as a directory copy (requires re-running `unity-bridge install-skill` after updates) - -To enable symlinks on Windows 10/11, enable Developer Mode in Settings > Update & Security > For developers. +Then add the local `package/package.json` from Unity Package Manager. -### Unity Commands +## Coexistence with the Original Claude Bridge -```bash -unity-bridge run-tests --mode EditMode -unity-bridge compile -unity-bridge get-console-logs --limit 10 -unity-bridge get-status -unity-bridge refresh -unity-bridge health-check -``` +This fork is designed to coexist with the original [ManageXR/claude-unity-bridge](https://github.com/ManageXR/claude-unity-bridge): -## Updating - -```bash -unity-bridge update -``` +- The CLI executable is `codex-unity-bridge`, not `unity-bridge` +- The Codex skill installs under `~/.codex/skills/` +- The Unity package watches `.codex-unity-bridge/`, not `.unity-bridge/` -This runs `pip install --upgrade claude-unity-bridge` and reinstalls the skill to ensure the symlink points to the updated package. +That means both bridges can be installed on the same machine and in the same Unity project without sharing the same command channel. -Or manually: +## Common Commands ```bash -pip install --upgrade claude-unity-bridge -unity-bridge install-skill +codex-unity-bridge install-skill +codex-unity-bridge uninstall-skill +codex-unity-bridge update +codex-unity-bridge run-tests --mode EditMode +codex-unity-bridge compile +codex-unity-bridge get-console-logs --limit 10 --filter Error +codex-unity-bridge get-status ``` -## Uninstalling - -```bash -unity-bridge uninstall-skill -pip uninstall claude-unity-bridge -``` +## Troubleshooting -## Development Installation +### CLI not found -For contributing to the project: +Your Python user scripts directory may not be on `PATH`. Run the module directly if needed: ```bash -git clone https://github.com/ManageXR/claude-unity-bridge.git -cd claude-unity-bridge/skill -pip install -e ".[dev]" -unity-bridge install-skill +python -m codex_unity_bridge.cli install-skill ``` -## Troubleshooting - -### PATH Issues - -If `unity-bridge` is not found after pip install, the pip scripts directory may not be in your PATH. You can: +### Windows symlink fallback -1. Add the pip scripts directory to your PATH -2. Use the module directly: `python -m claude_unity_bridge.cli install-skill` +If Windows blocks symlink creation, the installer falls back to copying the skill directory. Re-run `codex-unity-bridge install-skill` after upgrades. -### Windows Symlink Permissions +### Health check fails -On Windows, creating symlinks requires either: -- Administrator privileges, or -- Developer Mode enabled +Make sure: -If you see `[WinError 1314] A required privilege is not held by the client`, the installer will automatically fall back to copying the skill directory. This works perfectly fine, but updates require re-running `unity-bridge install-skill`. - -**To enable symlinks (optional):** -1. Open Settings > Update & Security > For developers -2. Enable "Developer Mode" -3. Restart your terminal -4. Run `unity-bridge install-skill` again - -### Python Version - -Make sure you're using Python 3.8 or later: - -```bash -python --version -# or -python3 --version -``` +- the Unity project has `com.prodfact.codex-bridge` installed +- the Unity Editor is open with that project loaded +- the project root contains `.codex-unity-bridge/` after the package initializes diff --git a/docs/USAGE.md b/docs/USAGE.md index f341b6f..58565e2 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -1,124 +1,97 @@ # Usage -## How It Works +## Runtime Protocol -ClaudeBridge uses a file-based protocol for communication: +Codex Unity Bridge uses a file-based protocol rooted at `.codex-unity-bridge/` in the Unity project. -1. **Command File**: Claude Code writes commands to `.unity-bridge/command.json` -2. **Processing**: Unity Editor polls for commands and executes them -3. **Response File**: Results are written to `.unity-bridge/response-{id}.json` +Files: -This approach enables multi-project support - each Unity project has its own `.unity-bridge/` directory, allowing multiple agents to work on different projects simultaneously. +- command file: `.codex-unity-bridge/command.json` +- response files: `.codex-unity-bridge/response-{id}.json` -## Commands +The Python CLI writes commands, the Unity package polls for them, and Unity writes structured JSON responses back. -### Run Tests +## CLI Examples -```json -{ - "id": "test-001", - "action": "run-tests", - "params": { - "testMode": "EditMode", - "filter": "MXR.Tests" - } -} +```bash +codex-unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode PlayMode --filter "Gameplay" +codex-unity-bridge compile +codex-unity-bridge refresh +codex-unity-bridge get-status +codex-unity-bridge get-console-logs --limit 20 --filter Error +codex-unity-bridge play +codex-unity-bridge pause +codex-unity-bridge step +codex-unity-bridge health-check ``` -**Parameters:** -- `testMode` - `"EditMode"` or `"PlayMode"` -- `filter` (optional) - Test filter pattern +## Command Shape -### Compile Scripts +Example command file: ```json { - "id": "compile-001", - "action": "compile", - "params": {} -} -``` - -### Refresh Assets - -```json -{ - "id": "refresh-001", - "action": "refresh", - "params": {} -} -``` - -### Get Editor Status - -```json -{ - "id": "status-001", - "action": "get-status", - "params": {} -} -``` - -### Get Console Logs - -```json -{ - "id": "logs-001", - "action": "get-console-logs", + "id": "test-001", + "action": "run-tests", "params": { - "limit": 20, - "filter": "Error" + "testMode": "EditMode", + "filter": "Gameplay" } } ``` -**Parameters:** -- `limit` (optional) - Maximum number of logs to retrieve (default: 50) -- `filter` (optional) - Filter by log type: `"Log"`, `"Warning"`, or `"Error"` - -## Response Format - -All commands return a response in this format: +Example response: ```json { "id": "test-001", - "status": "success", "action": "run-tests", + "status": "success", "duration_ms": 1250, "result": { - "passed": 410, + "passed": 10, "failed": 0, "skipped": 0 } } ``` -**Status Values:** -- `running` - Command in progress -- `success` - Command completed successfully -- `failure` - Command completed with failures -- `error` - Command execution error +Status values: + +- `running` +- `success` +- `failure` +- `error` + +## Unity Menu + +The package adds these items under `Tools > Codex Bridge`: + +- `Show Status` +- `Cleanup Old Responses` +- `Reset Processing State` -## Tools Menu +## Using Through Codex -The package adds menu items under `Tools > Claude Bridge`: +Once the skill is installed, Codex can invoke the bridge from natural prompts such as: -- **Show Status** - Display current bridge status in console -- **Cleanup Old Responses** - Delete response files older than 1 hour -- **Reset Processing State** - Clear stuck processing state +- `Run the Unity tests in EditMode` +- `Check whether Unity is compiling` +- `Show the last 10 error logs from Unity` +- `Refresh the asset database` -## Using with Claude Code +## Notes on Coexistence -Once the skill is installed, simply ask Claude Code naturally: +This fork intentionally does not reuse the original Claude bridge runtime directory. If both packages are installed in one Unity project: -- "Run the Unity tests in EditMode" -- "Check if there are any compilation errors" -- "Show me the last 10 error logs from Unity" -- "Refresh the Unity asset database" +- Claude bridge uses `.unity-bridge/` +- Codex Unity Bridge uses `.codex-unity-bridge/` -See [skill/SKILL.md](../skill/SKILL.md) for complete documentation. +That separation prevents both bridges from reading or deleting each other's command files. -## Complete Command Reference +## More Detail -For detailed command specifications including all parameters, response formats, and error scenarios, see [skill/references/COMMANDS.md](../skill/references/COMMANDS.md). +- [Skill Reference](../skill/SKILL.md) +- [Command Reference](../skill/references/COMMANDS.md) +- [Architecture](ARCHITECTURE.md) diff --git a/install.ps1 b/install.ps1 index 8a1e445..98638f6 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,14 +1,14 @@ -# Claude Unity Bridge - Quick Installer (PowerShell) +# Codex Unity Bridge - Quick Installer (PowerShell) # # Usage: -# irm https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.ps1 | iex +# irm https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.ps1 | iex # # Or download and run: # .\install.ps1 $ErrorActionPreference = "Stop" -Write-Host "Installing Claude Unity Bridge..." -ForegroundColor Cyan +Write-Host "Installing Codex Unity Bridge..." -ForegroundColor Cyan Write-Host "" # Check Python @@ -37,7 +37,7 @@ Write-Host "Found Python: $pythonCmd" -ForegroundColor Green # Install via pip (user-site to avoid permission issues) Write-Host "" Write-Host "Installing pip package..." -ForegroundColor Cyan -& $pythonCmd -m pip install --user --upgrade claude-unity-bridge +& $pythonCmd -m pip install --user --upgrade codex-unity-bridge if ($LASTEXITCODE -ne 0) { Write-Host "Error: pip installation failed" -ForegroundColor Red exit 1 @@ -66,8 +66,8 @@ else { # Install skill Write-Host "" -Write-Host "Installing Claude Code skill..." -ForegroundColor Cyan -& $pythonCmd -m claude_unity_bridge.cli install-skill +Write-Host "Installing Codex skill..." -ForegroundColor Cyan +& $pythonCmd -m codex_unity_bridge.cli install-skill if ($LASTEXITCODE -ne 0) { Write-Host "Error: Skill installation failed" -ForegroundColor Red exit 1 @@ -79,10 +79,10 @@ Write-Host "" Write-Host "Next steps:" -ForegroundColor Cyan Write-Host " 1. Add the Unity package to your project:" Write-Host " Window > Package Manager > + > Add package from git URL..." -Write-Host " https://github.com/ManageXR/claude-unity-bridge.git?path=package" +Write-Host " https://github.com/ProdFact/codex-unity-bridge.git?path=package" Write-Host "" -Write-Host " 2. Open Claude Code in your Unity project directory" +Write-Host " 2. Open Codex in your Unity project directory" Write-Host "" -Write-Host " 3. Ask Claude naturally: " -NoNewline +Write-Host " 3. Ask Codex naturally: " -NoNewline Write-Host '"Run the Unity tests"' -ForegroundColor Yellow Write-Host "" diff --git a/install.sh b/install.sh index c10a2c7..aa83037 100755 --- a/install.sh +++ b/install.sh @@ -1,15 +1,15 @@ #!/bin/bash -# Claude Unity Bridge - Quick Installer +# Codex Unity Bridge - Quick Installer # # Usage: -# curl -sSL https://raw.githubusercontent.com/ManageXR/claude-unity-bridge/main/install.sh | bash +# curl -sSL https://raw.githubusercontent.com/ProdFact/codex-unity-bridge/main/install.sh | bash # # Or download and run: # ./install.sh set -e -echo "Installing Claude Unity Bridge..." +echo "Installing Codex Unity Bridge..." echo # Check Python @@ -20,13 +20,13 @@ fi # Install via pip echo "Installing pip package..." -python3 -m pip install --user --upgrade claude-unity-bridge +python3 -m pip install --user --upgrade codex-unity-bridge # Get Python user scripts directory PYTHON_BIN="$(python3 -m site --user-base)/bin" # Add to PATH if not already there -if ! command -v unity-bridge &> /dev/null; then +if ! command -v codex-unity-bridge &> /dev/null; then # Determine shell config file if [ -n "$ZSH_VERSION" ] || [ "$SHELL" = "/bin/zsh" ]; then SHELL_RC="$HOME/.zshrc" @@ -40,7 +40,7 @@ if ! command -v unity-bridge &> /dev/null; then else echo "Adding Python scripts to PATH in $SHELL_RC..." echo "" >> "$SHELL_RC" - echo "# Added by Claude Unity Bridge installer" >> "$SHELL_RC" + echo "# Added by Codex Unity Bridge installer" >> "$SHELL_RC" echo "export PATH=\"\$PATH:$PYTHON_BIN\"" >> "$SHELL_RC" fi @@ -50,8 +50,8 @@ fi # Install skill echo -echo "Installing Claude Code skill..." -python3 -m claude_unity_bridge.cli install-skill +echo "Installing Codex skill..." +python3 -m codex_unity_bridge.cli install-skill echo echo "Installation complete!" @@ -59,9 +59,9 @@ echo echo "Next steps:" echo " 1. Add the Unity package to your project:" echo " Window > Package Manager > + > Add package from git URL..." -echo " https://github.com/ManageXR/claude-unity-bridge.git?path=package" +echo " https://github.com/ProdFact/codex-unity-bridge.git?path=package" echo -echo " 2. Open Claude Code in your Unity project directory" +echo " 2. Open Codex in your Unity project directory" echo -echo " 3. Ask Claude naturally: \"Run the Unity tests\"" +echo " 3. Ask Codex naturally: \"Run the Unity tests\"" echo diff --git a/package/CHANGELOG.md b/package/CHANGELOG.md index b114ca2..1b29071 100644 --- a/package/CHANGELOG.md +++ b/package/CHANGELOG.md @@ -1,163 +1,26 @@ # Changelog -All notable changes to the Claude Unity Bridge package will be documented in this file. +All notable changes to the Codex Unity Bridge Unity package are documented here. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +This fork starts its package-specific changelog at `0.3.0`. -## [0.2.1] - 2026-02-17 +For pre-fork history and original development lineage, see the upstream project: +[ManageXR/claude-unity-bridge](https://github.com/ManageXR/claude-unity-bridge). -### Added - -- `play` command - Toggle Play Mode on/off -- `pause` command - Toggle pause while in Play Mode -- `step` command - Advance one frame while paused -- `IEditorPlayMode` interface abstracting `EditorApplication` for testability -- Full Moq-based test coverage for play/pause/step commands - -### Fixed - -- Play command response now reports intended play mode state instead of stale pre-transition value - -## [0.2.0] - 2026-02-11 - -### Fixed - -- Windows symlink permission error (`[WinError 1314]`) during skill installation (#13) - - Implement symlink-with-copy-fallback strategy - - Gracefully fall back to `shutil.copytree()` when symlink creation fails - - Installation now succeeds on Windows without Administrator privileges or Developer Mode -- Handle both symlinks and copied directories in `uninstall-skill` command -- Unicode encoding error on Windows console (replaced checkmarks with `[OK]`) -- CLI returning prematurely when Unity writes progress updates (#32) - - Now polls through "running" status with exponential backoff instead of returning early - - Fixes issue where test results showed 0/0/0 while tests were still executing - - Added progress info display in verbose mode - -### Added - -- `install.ps1` PowerShell installer for native Windows support -- Platform-aware error messaging (uses `rmdir /s` on Windows, `rm -rf` on Unix) -- User notification about copy fallback with Developer Mode instructions -- Comprehensive test coverage for copy fallback scenarios: - - `test_install_skill_copy_fallback_on_symlink_failure` - - `test_install_skill_copy_fallback_failure` - - `test_uninstall_skill_removes_copied_directory` - - `test_uninstall_skill_warns_on_non_skill_directory` -- Updated existing tests to handle both symlinks and copied directories on Windows -- Windows-specific troubleshooting section in installation documentation - -### Changed - -- `install_skill()` now removes existing installations (files, directories, or symlinks) before installing -- `uninstall_skill()` validates directory contains `SKILL.md` before removal - -## [0.1.5] - 2026-02-10 - -### Fixed - -- Allow read-only commands (`get-status`, `get-console-logs`) while Unity is compiling or updating — mutating commands return an explicit error instead of timing out -- Always clean up stale response, temp, and command files at the start of every command (no longer gated behind `--cleanup` flag) -- Clean up response files on timeout and error via try/finally, not just on success -- Include `.tmp` files in TTL sweep alongside `response-*.json` -- Remove orphaned `command.json` files older than timeout before writing new commands -- Clean up stale files on Unity Editor startup - -### Added - -- Contributor Covenant Code of Conduct - -## [0.1.4] - 2026-02-09 - -### Security - -- Restrict `.unity-bridge/` directory and file permissions to owner-only on POSIX systems -- Add UUID validation for command IDs to prevent path traversal attacks -- Verify response ID matches expected command ID to prevent response spoofing -- Pin GitHub Actions to commit SHAs to prevent supply chain attacks -- Pin pre-commit hooks to commit SHAs - -### Added - -- Dependabot configuration for automated dependency updates (GitHub Actions + pip) -- `.gitignore` entries for `.unity-bridge/`, `.env`, and secret file patterns -- CODEOWNERS for `@ManageXR/unity-external` team - -### Changed - -- Bumped dev dependencies: black 25.11.0, pytest 8.4.2, pytest-cov 7.0.0, flake8 7.3.0, pre-commit 4.3.0 -- Bumped CI dependencies: actions/checkout 6.0.2, actions/setup-python 6.2.0, codecov/codecov-action 5.5.2 - -## [0.1.3] - 2026-02-06 - -### Fixed - -- Handle regular file (not just directory) at install target path -- Sync all version files (pyproject.toml, __init__.py, package.json) - -### Added - -- Tests for `update_package` function (success, pip failure, subprocess exception, CLI integration) -- Test for regular file detection in `install_skill` - -## [0.1.2] - 2026-02-05 +## [0.3.0] - 2026-03-06 ### Added -- PyPI publish workflow (automated on tag push) -- Bootstrap installer (`install.sh`) for one-command setup -- `install-skill` and `update` CLI subcommands -- PATH detection and auto-configuration in installer +- Codex-specific Unity package identity: `com.prodfact.codex-bridge` +- Separate runtime directory: `.codex-unity-bridge/` +- Same-project coexistence with the original Claude bridge +- Codex-branded docs, install flow, and packaged skill assets ### Changed -- Promoted one-liner install as primary installation approach -- Excluded CHANGELOG.md.meta from package distribution - -## [0.1.1] - 2026-02-04 - -### Fixed - -- Corrected license reference in skill documentation -- Fixed Unity project folder reference in contributing guide - -## [0.1.0] - 2026-01-22 - -### Added - -- Initial pre-release of Claude Unity Bridge as a standalone Unity package -- File-based bridge enabling Claude Code to trigger Unity Editor operations -- `run-tests` command - Execute EditMode or PlayMode tests with filtering -- `compile` command - Trigger script compilation -- `refresh` command - Force asset database refresh -- `get-status` command - Check editor compilation/update state -- `get-console-logs` command - Retrieve Unity console output with filtering -- Multi-project support via per-project `.unity-bridge/` directories -- Tools menu with status display and cleanup utilities -- Zero external dependencies - pure C# implementation -- Support for Unity 2021.3 and later - -### Features - -- Command/response protocol using JSON file exchange -- Progress reporting for long-running operations -- Automatic cleanup of old response files -- Console log retrieval with type filtering (Log, Warning, Error) -- Configurable log retrieval limits -- Reflection-based access to Unity's internal console API - -### Architecture - -- Command pattern for extensibility -- Assembly definition for clean integration -- Editor-only package (no runtime impact) -- Automatic initialization via `[InitializeOnLoad]` +- Renamed C# namespaces and assembly definitions to `ProdFact.CodexBridge` +- Renamed Unity logs and menu items to `CodexBridge` +- Renamed the Python package to `codex-unity-bridge` +- Renamed the public CLI executable to `codex-unity-bridge` -[0.2.1]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.2.1 -[0.2.0]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.2.0 -[0.1.5]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.5 -[0.1.4]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.4 -[0.1.3]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.3 -[0.1.2]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.2 -[0.1.1]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.1 -[0.1.0]: https://github.com/ManageXR/claude-unity-bridge/releases/tag/v0.1.0 +[0.3.0]: https://github.com/ProdFact/codex-unity-bridge/releases/tag/v0.3.0 diff --git a/package/Editor/ClaudeBridge.cs b/package/Editor/CodexBridge.cs similarity index 84% rename from package/Editor/ClaudeBridge.cs rename to package/Editor/CodexBridge.cs index 86a3976..1184a25 100644 --- a/package/Editor/ClaudeBridge.cs +++ b/package/Editor/CodexBridge.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; -namespace MXR.ClaudeBridge { +namespace ProdFact.CodexBridge { [InitializeOnLoad] - public static class ClaudeBridge { + public static class CodexBridge { private static readonly string CommandDir; private static readonly string CommandFilePath; private static readonly Dictionary Commands; @@ -30,8 +30,8 @@ public static class ClaudeBridge { "get-console-logs" }; - static ClaudeBridge() { - CommandDir = Path.Combine(Application.dataPath, "..", ".unity-bridge"); + static CodexBridge() { + CommandDir = Path.Combine(Application.dataPath, "..", ".codex-unity-bridge"); CommandFilePath = Path.Combine(CommandDir, "command.json"); var playMode = new EditorPlayMode(); @@ -52,7 +52,7 @@ static ClaudeBridge() { EditorApplication.update += PollForCommands; #if DEBUG - Debug.Log($"[ClaudeBridge] Initialized - Watching: {CommandDir}"); + Debug.Log($"[CodexBridge] Initialized - Watching: {CommandDir}"); #endif } @@ -81,7 +81,7 @@ private static void PollForCommands() { // Auto-recover from stuck processing state if (_isProcessingCommand) { if ((DateTime.Now - _commandStartTime).TotalSeconds > COMMAND_TIMEOUT_SECONDS) { - Debug.LogWarning($"[ClaudeBridge] Command {_currentCommandId} timed out after {COMMAND_TIMEOUT_SECONDS}s, resetting state"); + Debug.LogWarning($"[CodexBridge] Command {_currentCommandId} timed out after {COMMAND_TIMEOUT_SECONDS}s, resetting state"); ResetProcessingState(); } else { // Bridge is busy - respond with error so client doesn't hang @@ -107,7 +107,7 @@ private static void TryRespondBusy() { var request = JsonUtility.FromJson(json); if (request != null && !string.IsNullOrEmpty(request.id)) { - Debug.LogWarning($"[ClaudeBridge] Rejecting command {request.id} - bridge is busy processing {_currentCommandId}"); + Debug.LogWarning($"[CodexBridge] Rejecting command {request.id} - bridge is busy processing {_currentCommandId}"); WriteResponse(CommandResponse.Error(request.id, request.action ?? "unknown", $"Bridge is busy processing another command ({_currentCommandId}). Try again later.")); } @@ -115,7 +115,7 @@ private static void TryRespondBusy() { DeleteCommandFile(); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error handling busy response: {e.Message}"); + Debug.LogError($"[CodexBridge] Error handling busy response: {e.Message}"); DeleteCommandFile(); } } @@ -145,13 +145,13 @@ private static bool TryProcessSafeCommand() { // Mutating command during compilation — reject with informative error DeleteCommandFile(); var state = EditorApplication.isCompiling ? "compiling" : "updating"; - Debug.LogWarning($"[ClaudeBridge] Rejecting command {request.action} ({request.id}) - Unity is {state}"); + Debug.LogWarning($"[CodexBridge] Rejecting command {request.action} ({request.id}) - Unity is {state}"); WriteResponse(CommandResponse.Error(request.id, request.action, $"Unity Editor is currently {state}. Only read-only commands (get-status, get-console-logs) are available. Try again later.")); return true; } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error peeking at command during compile: {e.Message}"); + Debug.LogError($"[CodexBridge] Error peeking at command during compile: {e.Message}"); DeleteCommandFile(); return true; } @@ -170,7 +170,7 @@ private static void ProcessCommand() { request = JsonUtility.FromJson(json); if (request == null || string.IsNullOrEmpty(request.id)) { - Debug.LogError("[ClaudeBridge] Invalid command file - missing id"); + Debug.LogError("[CodexBridge] Invalid command file - missing id"); DeleteCommandFile(); return; } @@ -183,11 +183,11 @@ private static void ProcessCommand() { _commandStartTime = DateTime.Now; #if DEBUG - Debug.Log($"[ClaudeBridge] Processing command: {request.action} (id: {request.id})"); + Debug.Log($"[CodexBridge] Processing command: {request.action} (id: {request.id})"); #endif if (!Commands.TryGetValue(request.action, out var command)) { - Debug.LogError($"[ClaudeBridge] Unknown action: {request.action}"); + Debug.LogError($"[CodexBridge] Unknown action: {request.action}"); WriteResponse(CommandResponse.Error(request.id, request.action, $"Unknown action: {request.action}")); _isProcessingCommand = false; return; @@ -196,7 +196,7 @@ private static void ProcessCommand() { command.Execute(request, OnProgress, OnComplete); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error processing command: {e.Message}"); + Debug.LogError($"[CodexBridge] Error processing command: {e.Message}"); DeleteCommandFile(); if (request != null) { @@ -225,7 +225,7 @@ private static void WriteResponse(CommandResponse response) { try { // Security: Validate response ID to prevent path traversal attacks if (string.IsNullOrEmpty(response.id) || !ValidIdPattern.IsMatch(response.id)) { - Debug.LogError($"[ClaudeBridge] Invalid response ID format: {response.id}"); + Debug.LogError($"[CodexBridge] Invalid response ID format: {response.id}"); return; } @@ -253,7 +253,7 @@ private static void WriteResponse(CommandResponse response) { File.Move(tempPath, responsePath); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error writing response: {e.Message}"); + Debug.LogError($"[CodexBridge] Error writing response: {e.Message}"); } } @@ -264,12 +264,12 @@ private static void DeleteCommandFile() { } } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error deleting command file: {e.Message}"); + Debug.LogError($"[CodexBridge] Error deleting command file: {e.Message}"); } } // Cleanup utility - can be called from menu - [MenuItem("Tools/Claude Bridge/Cleanup Old Responses")] + [MenuItem("Tools/Codex Bridge/Cleanup Old Responses")] private static void CleanupOldResponses() { if (!Directory.Exists(CommandDir)) return; @@ -281,7 +281,7 @@ private static void CleanupOldResponses() { if (info.LastWriteTime < cutoff) { try { File.Delete(file); - Debug.Log($"[ClaudeBridge] Cleaned up: {Path.GetFileName(file)}"); + Debug.Log($"[CodexBridge] Cleaned up: {Path.GetFileName(file)}"); } catch { // Ignore errors during cleanup @@ -290,19 +290,19 @@ private static void CleanupOldResponses() { } } - [MenuItem("Tools/Claude Bridge/Reset Processing State")] + [MenuItem("Tools/Codex Bridge/Reset Processing State")] private static void ResetProcessingStateMenu() { if (_isProcessingCommand) { - Debug.Log($"[ClaudeBridge] Manually resetting processing state (was processing: {_currentCommandId})"); + Debug.Log($"[CodexBridge] Manually resetting processing state (was processing: {_currentCommandId})"); ResetProcessingState(); } else { - Debug.Log("[ClaudeBridge] No command is currently being processed"); + Debug.Log("[CodexBridge] No command is currently being processed"); } } - [MenuItem("Tools/Claude Bridge/Show Status")] + [MenuItem("Tools/Codex Bridge/Show Status")] private static void ShowStatus() { - Debug.Log($"[ClaudeBridge] Status:"); + Debug.Log($"[CodexBridge] Status:"); Debug.Log($" Command directory: {CommandDir}"); Debug.Log($" Is processing: {_isProcessingCommand}"); Debug.Log($" Current command ID: {_currentCommandId ?? "none"}"); diff --git a/package/Editor/ClaudeBridge.cs.meta b/package/Editor/CodexBridge.cs.meta similarity index 100% rename from package/Editor/ClaudeBridge.cs.meta rename to package/Editor/CodexBridge.cs.meta diff --git a/package/Editor/Commands/CompileCommand.cs b/package/Editor/Commands/CompileCommand.cs index a5aadb6..708d3ca 100644 --- a/package/Editor/Commands/CompileCommand.cs +++ b/package/Editor/Commands/CompileCommand.cs @@ -1,11 +1,11 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class CompileCommand : ICommand { private string _commandId; private Stopwatch _stopwatch; @@ -21,7 +21,7 @@ public void Execute(CommandRequest request, Action onProgress, _hasErrors = false; #if DEBUG - UnityEngine.Debug.Log("[ClaudeBridge] Starting script compilation"); + UnityEngine.Debug.Log("[CodexBridge] Starting script compilation"); #endif var response = CommandResponse.Running(_commandId, request.action); @@ -37,7 +37,7 @@ private void OnAssemblyCompilationFinished(string assemblyPath, CompilerMessage[ foreach (var message in messages) { if (message.type == CompilerMessageType.Error) { _hasErrors = true; - UnityEngine.Debug.LogError($"[ClaudeBridge] Compilation error: {message.message}"); + UnityEngine.Debug.LogError($"[CodexBridge] Compilation error: {message.message}"); } } } @@ -48,7 +48,7 @@ private void OnCompilationFinished(object context) { _stopwatch.Stop(); #if DEBUG - UnityEngine.Debug.Log($"[ClaudeBridge] Compilation finished - HasErrors: {_hasErrors}"); + UnityEngine.Debug.Log($"[CodexBridge] Compilation finished - HasErrors: {_hasErrors}"); #endif var response = _hasErrors diff --git a/package/Editor/Commands/EditorPlayMode.cs b/package/Editor/Commands/EditorPlayMode.cs index 815d889..3e5ba22 100644 --- a/package/Editor/Commands/EditorPlayMode.cs +++ b/package/Editor/Commands/EditorPlayMode.cs @@ -1,6 +1,6 @@ using UnityEditor; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class EditorPlayMode : IEditorPlayMode { public bool IsPlaying { get => EditorApplication.isPlaying; diff --git a/package/Editor/Commands/GetConsoleLogsCommand.cs b/package/Editor/Commands/GetConsoleLogsCommand.cs index a69264e..f2cb865 100644 --- a/package/Editor/Commands/GetConsoleLogsCommand.cs +++ b/package/Editor/Commands/GetConsoleLogsCommand.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class GetConsoleLogsCommand : ICommand { // Default limit when no limit parameter is provided private const int DEFAULT_LOG_LIMIT = 50; @@ -26,7 +26,7 @@ public class GetConsoleLogsCommand : ICommand { public void Execute(CommandRequest request, Action onProgress, Action onComplete) { var stopwatch = Stopwatch.StartNew(); #if DEBUG - Debug.Log("[ClaudeBridge] Getting console logs"); + Debug.Log("[CodexBridge] Getting console logs"); #endif try { @@ -52,7 +52,7 @@ public void Execute(CommandRequest request, Action onProgress, } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Error in GetConsoleLogsCommand: {e.Message}"); + Debug.LogError($"[CodexBridge] Error in GetConsoleLogsCommand: {e.Message}"); var response = CommandResponse.Failure(request.id, request.action, stopwatch.ElapsedMilliseconds, e.Message); onComplete?.Invoke(response); } @@ -65,7 +65,7 @@ private List GetConsoleLogs(int limit, string filter) { // Use reflection to access Unity's internal LogEntries class var logEntriesType = Type.GetType("UnityEditor.LogEntries, UnityEditor"); if (logEntriesType == null) { - Debug.LogWarning("[ClaudeBridge] Could not access LogEntries type"); + Debug.LogWarning("[CodexBridge] Could not access LogEntries type"); return result; } @@ -75,7 +75,7 @@ private List GetConsoleLogs(int limit, string filter) { var endGettingEntriesMethod = logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Static | BindingFlags.Public); if (getCountMethod == null || getEntryInternalMethod == null) { - Debug.LogWarning("[ClaudeBridge] Could not access LogEntries methods"); + Debug.LogWarning("[CodexBridge] Could not access LogEntries methods"); return result; } @@ -152,7 +152,7 @@ private List GetConsoleLogs(int limit, string filter) { endGettingEntriesMethod?.Invoke(null, null); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error getting console logs: {e.Message}"); + Debug.LogError($"[CodexBridge] Error getting console logs: {e.Message}"); } return result; diff --git a/package/Editor/Commands/GetStatusCommand.cs b/package/Editor/Commands/GetStatusCommand.cs index 44b31cc..765a37a 100644 --- a/package/Editor/Commands/GetStatusCommand.cs +++ b/package/Editor/Commands/GetStatusCommand.cs @@ -1,16 +1,16 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class GetStatusCommand : ICommand { public void Execute(CommandRequest request, Action onProgress, Action onComplete) { var stopwatch = Stopwatch.StartNew(); #if DEBUG - Debug.Log("[ClaudeBridge] Getting editor status"); + Debug.Log("[CodexBridge] Getting editor status"); #endif // Use the proper EditorStatus model instead of overloading the error field diff --git a/package/Editor/Commands/ICommand.cs b/package/Editor/Commands/ICommand.cs index 8b429a3..982fdfb 100644 --- a/package/Editor/Commands/ICommand.cs +++ b/package/Editor/Commands/ICommand.cs @@ -1,7 +1,7 @@ using System; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public interface ICommand { void Execute(CommandRequest request, Action onProgress, Action onComplete); } diff --git a/package/Editor/Commands/IEditorPlayMode.cs b/package/Editor/Commands/IEditorPlayMode.cs index 481a3ae..8df6e7a 100644 --- a/package/Editor/Commands/IEditorPlayMode.cs +++ b/package/Editor/Commands/IEditorPlayMode.cs @@ -1,4 +1,4 @@ -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public interface IEditorPlayMode { bool IsPlaying { get; set; } bool IsPaused { get; set; } diff --git a/package/Editor/Commands/PauseCommand.cs b/package/Editor/Commands/PauseCommand.cs index 1e537fe..775d008 100644 --- a/package/Editor/Commands/PauseCommand.cs +++ b/package/Editor/Commands/PauseCommand.cs @@ -1,10 +1,10 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class PauseCommand : ICommand { private readonly IEditorPlayMode _editor; @@ -27,7 +27,7 @@ public void Execute(CommandRequest request, Action onProgress, stopwatch.Stop(); #if DEBUG - Debug.Log($"[ClaudeBridge] Pause toggled: isPaused={_editor.IsPaused}"); + Debug.Log($"[CodexBridge] Pause toggled: isPaused={_editor.IsPaused}"); #endif var response = CommandResponse.Success(request.id, request.action, stopwatch.ElapsedMilliseconds); @@ -41,7 +41,7 @@ public void Execute(CommandRequest request, Action onProgress, } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Pause toggle failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Pause toggle failed: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } } diff --git a/package/Editor/Commands/PlayCommand.cs b/package/Editor/Commands/PlayCommand.cs index a3cbf1f..ac20b20 100644 --- a/package/Editor/Commands/PlayCommand.cs +++ b/package/Editor/Commands/PlayCommand.cs @@ -1,10 +1,10 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class PlayCommand : ICommand { private readonly IEditorPlayMode _editor; @@ -21,7 +21,7 @@ public void Execute(CommandRequest request, Action onProgress, stopwatch.Stop(); #if DEBUG - Debug.Log($"[ClaudeBridge] Play mode toggled: isPlaying={willPlay}"); + Debug.Log($"[CodexBridge] Play mode toggled: isPlaying={willPlay}"); #endif var response = CommandResponse.Success(request.id, request.action, stopwatch.ElapsedMilliseconds); @@ -35,7 +35,7 @@ public void Execute(CommandRequest request, Action onProgress, } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Play mode toggle failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Play mode toggle failed: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } } diff --git a/package/Editor/Commands/RefreshCommand.cs b/package/Editor/Commands/RefreshCommand.cs index 5209c86..bc36c37 100644 --- a/package/Editor/Commands/RefreshCommand.cs +++ b/package/Editor/Commands/RefreshCommand.cs @@ -1,17 +1,17 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class RefreshCommand : ICommand { public void Execute(CommandRequest request, Action onProgress, Action onComplete) { var stopwatch = Stopwatch.StartNew(); #if DEBUG - Debug.Log("[ClaudeBridge] Refreshing asset database"); + Debug.Log("[CodexBridge] Refreshing asset database"); #endif var response = CommandResponse.Running(request.id, request.action); @@ -22,13 +22,13 @@ public void Execute(CommandRequest request, Action onProgress, stopwatch.Stop(); #if DEBUG - Debug.Log("[ClaudeBridge] Asset database refresh completed"); + Debug.Log("[CodexBridge] Asset database refresh completed"); #endif onComplete?.Invoke(CommandResponse.Success(request.id, request.action, stopwatch.ElapsedMilliseconds)); } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Asset database refresh failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Asset database refresh failed: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } } diff --git a/package/Editor/Commands/RunTestsCommand.cs b/package/Editor/Commands/RunTestsCommand.cs index e50918c..da94c4d 100644 --- a/package/Editor/Commands/RunTestsCommand.cs +++ b/package/Editor/Commands/RunTestsCommand.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor.TestTools.TestRunner.Api; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class RunTestsCommand : ICommand { public void Execute(CommandRequest request, Action onProgress, Action onComplete) { var stopwatch = Stopwatch.StartNew(); @@ -26,7 +26,7 @@ public void Execute(CommandRequest request, Action onProgress, } #if DEBUG - Debug.Log($"[ClaudeBridge] Running tests - Mode: {testMode}, Filter: {request.@params?.filter ?? "none"}"); + Debug.Log($"[CodexBridge] Running tests - Mode: {testMode}, Filter: {request.@params?.filter ?? "none"}"); #endif var response = CommandResponse.Running(request.id, request.action); @@ -83,7 +83,7 @@ private void CleanupApi() { public void RunStarted(ITestAdaptor testsToRun) { _total = CountTests(testsToRun); #if DEBUG - Debug.Log($"[ClaudeBridge] Test run started - {_total} tests"); + Debug.Log($"[CodexBridge] Test run started - {_total} tests"); #endif var response = CommandResponse.Running(_commandId, "run-tests"); @@ -97,7 +97,7 @@ public void RunStarted(ITestAdaptor testsToRun) { public void RunFinished(ITestResultAdaptor result) { _stopwatch.Stop(); #if DEBUG - Debug.Log($"[ClaudeBridge] Test run finished - Passed: {_passed}, Failed: {_failed}, Skipped: {_skipped}"); + Debug.Log($"[CodexBridge] Test run finished - Passed: {_passed}, Failed: {_failed}, Skipped: {_skipped}"); #endif var response = _failed > 0 diff --git a/package/Editor/Commands/StepCommand.cs b/package/Editor/Commands/StepCommand.cs index 5a5b55a..d62ace9 100644 --- a/package/Editor/Commands/StepCommand.cs +++ b/package/Editor/Commands/StepCommand.cs @@ -1,10 +1,10 @@ using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class StepCommand : ICommand { private readonly IEditorPlayMode _editor; @@ -27,7 +27,7 @@ public void Execute(CommandRequest request, Action onProgress, stopwatch.Stop(); #if DEBUG - Debug.Log("[ClaudeBridge] Editor stepped one frame"); + Debug.Log("[CodexBridge] Editor stepped one frame"); #endif var response = CommandResponse.Success(request.id, request.action, stopwatch.ElapsedMilliseconds); @@ -41,7 +41,7 @@ public void Execute(CommandRequest request, Action onProgress, } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Step failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Step failed: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } } diff --git a/package/Editor/Models/CommandRequest.cs b/package/Editor/Models/CommandRequest.cs index 346c6ce..44e4adb 100644 --- a/package/Editor/Models/CommandRequest.cs +++ b/package/Editor/Models/CommandRequest.cs @@ -1,6 +1,6 @@ using System; -namespace MXR.ClaudeBridge.Models { +namespace ProdFact.CodexBridge.Models { [Serializable] public class CommandRequest { public string id; diff --git a/package/Editor/Models/CommandResponse.cs b/package/Editor/Models/CommandResponse.cs index e6267d0..c4f735f 100644 --- a/package/Editor/Models/CommandResponse.cs +++ b/package/Editor/Models/CommandResponse.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace MXR.ClaudeBridge.Models { +namespace ProdFact.CodexBridge.Models { [Serializable] public class CommandResponse { public string id; diff --git a/package/Editor/ClaudeBridge.asmdef b/package/Editor/ProdFact.CodexBridge.asmdef similarity index 82% rename from package/Editor/ClaudeBridge.asmdef rename to package/Editor/ProdFact.CodexBridge.asmdef index c566755..c76fc37 100644 --- a/package/Editor/ClaudeBridge.asmdef +++ b/package/Editor/ProdFact.CodexBridge.asmdef @@ -1,6 +1,6 @@ { - "name": "MXR.ClaudeBridge", - "rootNamespace": "MXR.ClaudeBridge", + "name": "ProdFact.CodexBridge", + "rootNamespace": "ProdFact.CodexBridge", "references": [ "UnityEngine.TestRunner", "UnityEditor.TestRunner" diff --git a/package/Editor/ClaudeBridge.asmdef.meta b/package/Editor/ProdFact.CodexBridge.asmdef.meta similarity index 100% rename from package/Editor/ClaudeBridge.asmdef.meta rename to package/Editor/ProdFact.CodexBridge.asmdef.meta diff --git a/package/Tests/Editor/ClaudeBridgeTests.cs b/package/Tests/Editor/CodexBridgeTests.cs similarity index 97% rename from package/Tests/Editor/ClaudeBridgeTests.cs rename to package/Tests/Editor/CodexBridgeTests.cs index 2731d5f..293cf59 100644 --- a/package/Tests/Editor/ClaudeBridgeTests.cs +++ b/package/Tests/Editor/CodexBridgeTests.cs @@ -1,17 +1,17 @@ -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using System.Text.RegularExpressions; using UnityEngine; -namespace MXR.ClaudeBridge.Tests { +namespace ProdFact.CodexBridge.Tests { /// - /// Tests for ClaudeBridge dispatcher and validation logic. + /// Tests for CodexBridge dispatcher and validation logic. /// Focus: Tests response ID validation, response factory methods, and error handling patterns. /// NOT testing: File I/O operations (require integration tests), Unity Editor static state. /// [TestFixture] - public class ClaudeBridgeTests { - // Mirror the regex pattern from ClaudeBridge for testing + public class CodexBridgeTests { + // Mirror the regex pattern from CodexBridge for testing private static readonly Regex ValidIdPattern = new Regex(@"^[a-fA-F0-9\-]+$", RegexOptions.Compiled); #region Response ID Validation Tests diff --git a/package/Tests/Editor/ClaudeBridgeTests.cs.meta b/package/Tests/Editor/CodexBridgeTests.cs.meta similarity index 100% rename from package/Tests/Editor/ClaudeBridgeTests.cs.meta rename to package/Tests/Editor/CodexBridgeTests.cs.meta diff --git a/package/Tests/Editor/Commands/CompileCommandTests.cs b/package/Tests/Editor/Commands/CompileCommandTests.cs index 9047ae0..3bbb809 100644 --- a/package/Tests/Editor/Commands/CompileCommandTests.cs +++ b/package/Tests/Editor/Commands/CompileCommandTests.cs @@ -1,11 +1,11 @@ -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using UnityEditor.Compilation; using UnityEngine.TestTools; using System.Reflection; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for CompileCommand. /// Focus: Tests REAL async behavior (event registration, state tracking, callback timing) @@ -67,7 +67,7 @@ public void OnCompilationFinished_WithErrors_CallsOnCompleteWithFailure() { _command.Execute(Request, Responses.OnProgress, Responses.OnComplete); // Expect error log from CompileCommand - LogAssert.Expect(UnityEngine.LogType.Error, "[ClaudeBridge] Compilation error: Test compilation error"); + LogAssert.Expect(UnityEngine.LogType.Error, "[CodexBridge] Compilation error: Test compilation error"); // Simulate compilation errors var errorMessages = new CompilerMessage[] { @@ -128,8 +128,8 @@ public void OnAssemblyCompilationFinished_WithMultipleErrors_StillFails() { _command.Execute(Request, Responses.OnProgress, Responses.OnComplete); // Expect error logs from CompileCommand (one per error) - LogAssert.Expect(UnityEngine.LogType.Error, "[ClaudeBridge] Compilation error: Error 1"); - LogAssert.Expect(UnityEngine.LogType.Error, "[ClaudeBridge] Compilation error: Error 2"); + LogAssert.Expect(UnityEngine.LogType.Error, "[CodexBridge] Compilation error: Error 1"); + LogAssert.Expect(UnityEngine.LogType.Error, "[CodexBridge] Compilation error: Error 2"); // Simulate multiple assemblies with errors var errorMessages1 = new CompilerMessage[] { diff --git a/package/Tests/Editor/Commands/GetConsoleLogsCommandTests.cs b/package/Tests/Editor/Commands/GetConsoleLogsCommandTests.cs index 03ec214..2d0ce0d 100644 --- a/package/Tests/Editor/Commands/GetConsoleLogsCommandTests.cs +++ b/package/Tests/Editor/Commands/GetConsoleLogsCommandTests.cs @@ -1,9 +1,9 @@ -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using System.Reflection; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for GetConsoleLogsCommand. /// Focus: Tests REAL behavior (parameter parsing, response structure, synchronous execution) diff --git a/package/Tests/Editor/Commands/GetStatusCommandTests.cs b/package/Tests/Editor/Commands/GetStatusCommandTests.cs index 8495b0f..2d500b2 100644 --- a/package/Tests/Editor/Commands/GetStatusCommandTests.cs +++ b/package/Tests/Editor/Commands/GetStatusCommandTests.cs @@ -1,10 +1,10 @@ -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using UnityEditor; using UnityEngine; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for GetStatusCommand. /// Focus: Tests REAL behavior (response construction, editor state capture, JSON serialization) diff --git a/package/Tests/Editor/Commands/PauseCommandTests.cs b/package/Tests/Editor/Commands/PauseCommandTests.cs index c39c8be..b79c118 100644 --- a/package/Tests/Editor/Commands/PauseCommandTests.cs +++ b/package/Tests/Editor/Commands/PauseCommandTests.cs @@ -1,9 +1,9 @@ using Moq; -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for PauseCommand using Moq to mock IEditorPlayMode. /// Focus: Precondition check (must be playing), toggle behavior, response construction. diff --git a/package/Tests/Editor/Commands/PlayCommandTests.cs b/package/Tests/Editor/Commands/PlayCommandTests.cs index ec6b1f1..e9502ee 100644 --- a/package/Tests/Editor/Commands/PlayCommandTests.cs +++ b/package/Tests/Editor/Commands/PlayCommandTests.cs @@ -1,9 +1,9 @@ using Moq; -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for PlayCommand using Moq to mock IEditorPlayMode. /// Focus: Toggle behavior, response construction, editorStatus reflects mock state. diff --git a/package/Tests/Editor/Commands/RefreshCommandTests.cs b/package/Tests/Editor/Commands/RefreshCommandTests.cs index e37e6ed..c315691 100644 --- a/package/Tests/Editor/Commands/RefreshCommandTests.cs +++ b/package/Tests/Editor/Commands/RefreshCommandTests.cs @@ -1,9 +1,9 @@ -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using UnityEditor; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for RefreshCommand. /// Focus: Tests REAL behavior (progress reporting, error handling, duration tracking) diff --git a/package/Tests/Editor/Commands/RunTestsCommandTests.cs b/package/Tests/Editor/Commands/RunTestsCommandTests.cs index 3efd322..9ebb7d5 100644 --- a/package/Tests/Editor/Commands/RunTestsCommandTests.cs +++ b/package/Tests/Editor/Commands/RunTestsCommandTests.cs @@ -1,5 +1,5 @@ -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using NUnit.Framework.Interfaces; using UnityEditor.TestTools.TestRunner.Api; @@ -8,7 +8,7 @@ using TestStatus = UnityEditor.TestTools.TestRunner.Api.TestStatus; using RunState = UnityEditor.TestTools.TestRunner.Api.RunState; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for RunTestsCommand. /// Focus: Tests REAL behavior (test mode parsing, callback tracking, progress reporting) diff --git a/package/Tests/Editor/Commands/StepCommandTests.cs b/package/Tests/Editor/Commands/StepCommandTests.cs index e20e438..612dbf0 100644 --- a/package/Tests/Editor/Commands/StepCommandTests.cs +++ b/package/Tests/Editor/Commands/StepCommandTests.cs @@ -1,9 +1,9 @@ using Moq; -using MXR.ClaudeBridge.Commands; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Commands; +using ProdFact.CodexBridge.Models; using NUnit.Framework; -namespace MXR.ClaudeBridge.Tests.Commands { +namespace ProdFact.CodexBridge.Tests.Commands { /// /// Tests for StepCommand using Moq to mock IEditorPlayMode. /// Focus: Precondition check (must be playing), Step() call verification, response construction. diff --git a/package/Tests/Editor/Models/CommandRequestTests.cs b/package/Tests/Editor/Models/CommandRequestTests.cs index cb39a28..d014605 100644 --- a/package/Tests/Editor/Models/CommandRequestTests.cs +++ b/package/Tests/Editor/Models/CommandRequestTests.cs @@ -1,8 +1,8 @@ -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using UnityEngine; -namespace MXR.ClaudeBridge.Tests.Models { +namespace ProdFact.CodexBridge.Tests.Models { /// /// Tests for CommandRequest serialization/deserialization. /// Focus: Verify JSON serialization works correctly for file-based protocol diff --git a/package/Tests/Editor/Models/CommandResponseTests.cs b/package/Tests/Editor/Models/CommandResponseTests.cs index a7ea5c1..34770ea 100644 --- a/package/Tests/Editor/Models/CommandResponseTests.cs +++ b/package/Tests/Editor/Models/CommandResponseTests.cs @@ -1,8 +1,8 @@ -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using UnityEngine; -namespace MXR.ClaudeBridge.Tests.Models { +namespace ProdFact.CodexBridge.Tests.Models { /// /// Tests for CommandResponse factory methods and serialization. /// Focus: Verify factory methods create correct response structures diff --git a/package/Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef b/package/Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef similarity index 78% rename from package/Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef rename to package/Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef index 35a3297..435d627 100644 --- a/package/Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef +++ b/package/Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef @@ -1,8 +1,8 @@ { - "name": "MXR.ClaudeBridge.Tests.Editor", - "rootNamespace": "MXR.ClaudeBridge.Tests", + "name": "ProdFact.CodexBridge.Tests.Editor", + "rootNamespace": "ProdFact.CodexBridge.Tests", "references": [ - "MXR.ClaudeBridge", + "ProdFact.CodexBridge", "UnityEngine.TestRunner", "UnityEditor.TestRunner" ], diff --git a/package/Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef.meta b/package/Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef.meta similarity index 100% rename from package/Tests/Editor/MXR.ClaudeBridge.Tests.Editor.asmdef.meta rename to package/Tests/Editor/ProdFact.CodexBridge.Tests.Editor.asmdef.meta diff --git a/package/Tests/Editor/TestHelpers/CommandTestFixture.cs b/package/Tests/Editor/TestHelpers/CommandTestFixture.cs index e4fd5cb..eb6de87 100644 --- a/package/Tests/Editor/TestHelpers/CommandTestFixture.cs +++ b/package/Tests/Editor/TestHelpers/CommandTestFixture.cs @@ -1,8 +1,8 @@ -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using NUnit.Framework; using System; -namespace MXR.ClaudeBridge.Tests { +namespace ProdFact.CodexBridge.Tests { /// /// Base test fixture for all command tests. /// Provides common test infrastructure including response capture and request builders. diff --git a/package/Tests/Editor/TestHelpers/ResponseCapture.cs b/package/Tests/Editor/TestHelpers/ResponseCapture.cs index f70ec97..8b2ecf6 100644 --- a/package/Tests/Editor/TestHelpers/ResponseCapture.cs +++ b/package/Tests/Editor/TestHelpers/ResponseCapture.cs @@ -1,8 +1,8 @@ -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using System; using System.Collections.Generic; -namespace MXR.ClaudeBridge.Tests { +namespace ProdFact.CodexBridge.Tests { /// /// Utility class to capture command callbacks (onProgress and onComplete). /// Used in tests to verify command behavior without relying on actual file I/O. diff --git a/package/package.json b/package/package.json index da288dd..b8d5980 100644 --- a/package/package.json +++ b/package/package.json @@ -1,20 +1,20 @@ { - "name": "com.mxr.claude-bridge", - "version": "0.2.2", - "displayName": "ManageXR Claude Unity Bridge", - "description": "File-based bridge enabling Claude Code to trigger Unity Editor operations (tests, compile, refresh) in a running editor instance.", + "name": "com.prodfact.codex-bridge", + "version": "0.3.0", + "displayName": "ProdFact Codex Unity Bridge", + "description": "File-based bridge enabling Codex to trigger Unity Editor operations in a running Unity Editor instance.", "unity": "2021.3", - "documentationUrl": "https://github.com/ManageXR/claude-unity-bridge", + "documentationUrl": "https://github.com/ProdFact/codex-unity-bridge", "dependencies": {}, "keywords": [ - "claude", + "codex", "ai", + "unity", "testing", "automation" ], "author": { - "name": "Mighty Immersion, Inc", - "email": "support@managexr.com", - "url": "https://www.managexr.com" + "name": "ProdFact", + "url": "https://github.com/ProdFact/codex-unity-bridge" } } diff --git a/skill/SKILL.md b/skill/SKILL.md index 9ea4292..a79351c 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -1,478 +1,94 @@ --- name: unity -description: Execute Unity Editor commands (run tests, compile, get logs, refresh assets, play/pause/step) via file-based bridge. Auto-activates for Unity-related tasks. Requires com.mxr.claude-bridge package installed in Unity project. +description: Execute Unity Editor commands through Codex Unity Bridge. Requires the com.prodfact.codex-bridge Unity package in the current project and a running Unity Editor. --- # Unity Bridge Skill -Control Unity Editor operations from Claude Code using a reliable file-based communication protocol. +Use this skill when Codex needs to interact with a live Unity Editor from the project workspace. ## Overview -The Unity Bridge enables Claude Code to trigger operations in a running Unity Editor instance without network configuration or port conflicts. It uses a simple file-based protocol where commands are written to `.unity-bridge/command.json` and responses are read from `.unity-bridge/response-{id}.json`. +Codex Unity Bridge uses a deterministic Python CLI instead of ad-hoc in-context file handling. The CLI owns: -**Key Features:** -- Execute EditMode and PlayMode tests -- Trigger script compilation -- Refresh asset database -- Check editor status (compilation, play mode, etc.) -- Retrieve Unity console logs -- Control Play Mode (play, pause, step) +- UUID generation +- atomic command writes +- polling and timeout handling +- response parsing +- cleanup of stale files +- skill installation -**Multi-Project Support:** Each Unity project has its own `.unity-bridge/` directory, allowing multiple projects to be worked on simultaneously. +That keeps Unity interactions stable across sessions and avoids re-implementing the file protocol each time. ## Requirements -1. **Unity Package:** Install `com.mxr.claude-bridge` in your Unity project - - Via Package Manager: `https://github.com/ManageXR/claude-unity-bridge.git?path=package` - - See main package README for installation instructions +1. The Unity project has `com.prodfact.codex-bridge` installed +2. Unity Editor is open with that project loaded +3. The project root contains `.codex-unity-bridge/` after the package initializes +4. The CLI is installed: `codex-unity-bridge` -2. **Unity Editor:** Must be open with your project loaded +## Runtime Files -3. **Python 3:** The skill uses a Python script for reliable command execution +- Command file: `.codex-unity-bridge/command.json` +- Response files: `.codex-unity-bridge/response-{id}.json` -## How It Works +This is intentionally separate from the original Claude bridge runtime directory so both bridges can coexist in the same Unity project. -The skill uses a CLI tool (`unity-bridge`) that handles: -- UUID generation for command tracking -- Atomic file writes to prevent corruption -- Exponential backoff polling for responses -- File locking handling -- Automatic cleanup of old response files -- Formatted, human-readable output +## Basic Usage -This approach ensures **deterministic, rock-solid execution** - the script is tested once and behaves identically every time, handling all edge cases (timeouts, file locking, malformed responses, etc.) without requiring Claude to manage these details in-context. - -## Usage - -### Basic Pattern - -When you need to interact with Unity, use the CLI directly: - -```bash -unity-bridge [command] [options] -``` - -All commands automatically: -- Generate a unique UUID for tracking -- Write the command atomically -- Poll for response with timeout -- Format output for readability -- Cleanup response files - -### Command Examples - -#### Run Tests - -Execute Unity tests in EditMode or PlayMode: - -```bash -# Run all EditMode tests -unity-bridge run-tests --mode EditMode - -# Run tests with filter -unity-bridge run-tests --mode EditMode --filter "MXR.Tests" - -# Run all tests (both modes) -unity-bridge run-tests -``` - -**Output:** -``` -✓ Tests Passed: 410 -✗ Tests Failed: 2 -○ Tests Skipped: 0 -Duration: 1.25s - -Failed Tests: - - MXR.Tests.AuthTests.LoginWithInvalidCredentials - Expected: success, Actual: failure - - MXR.Tests.NetworkTests.TimeoutHandling - NullReferenceException: Object reference not set -``` - -**Parameters:** -- `--mode` - `EditMode` or `PlayMode` (optional, defaults to both) -- `--filter` - Test name filter pattern (optional) -- `--timeout` - Override default 30s timeout - -#### Compile Scripts - -Trigger Unity script compilation: +Run the CLI directly: ```bash -unity-bridge compile -``` - -**Output (Success):** -``` -✓ Compilation Successful -Duration: 2.3s +codex-unity-bridge [command] [options] ``` -**Output (Failure):** -``` -✗ Compilation Failed - -Assets/Scripts/Player.cs:25: error CS0103: The name 'invalidVar' does not exist -Assets/Scripts/Enemy.cs:67: error CS0246: Type 'MissingClass' could not be found -``` - -#### Get Console Logs - -Retrieve Unity console output: +Common commands: ```bash -# Get last 20 logs -unity-bridge get-console-logs --limit 20 - -# Get only errors -unity-bridge get-console-logs --limit 10 --filter Error - -# Get warnings -unity-bridge get-console-logs --filter Warning +codex-unity-bridge run-tests --mode EditMode +codex-unity-bridge compile +codex-unity-bridge refresh +codex-unity-bridge get-status +codex-unity-bridge get-console-logs --limit 20 --filter Error +codex-unity-bridge play +codex-unity-bridge pause +codex-unity-bridge step +codex-unity-bridge health-check ``` -**Output:** -``` -Console Logs (last 10, filtered by Error): - -[Error] NullReferenceException: Object reference not set - at Player.Update() in Assets/Scripts/Player.cs:34 - -[Error] Failed to load asset: missing_texture.png +## Natural-Language Examples -[Error] (x3) Shader compilation failed - See Console for details -``` +Typical prompts that map well to this skill: -**Parameters:** -- `--limit` - Maximum number of logs (default: 50) -- `--filter` - Filter by type: `Log`, `Warning`, or `Error` +- `Run the Unity tests in EditMode` +- `Check whether Unity is compiling` +- `Show the last 10 Unity errors` +- `Refresh the Unity asset database` +- `Pause play mode and step one frame` -#### Get Editor Status +## Guidance -Check Unity Editor state: - -```bash -unity-bridge get-status -``` - -**Output:** -``` -Unity Editor Status: - - Compilation: ✓ Ready - - Play Mode: ✏ Editing - - Updating: No -``` - -**Possible States:** -- Compilation: `✓ Ready` or `⏳ Compiling...` -- Play Mode: `✏ Editing`, `▶ Playing`, or `⏸ Paused` -- Updating: `Yes` or `No` - -#### Refresh Asset Database - -Force Unity to refresh assets: - -```bash -unity-bridge refresh -``` - -**Output:** -``` -✓ Asset Database Refreshed -Duration: 0.5s -``` - -#### Play Mode Control - -Toggle Play Mode, pause, and step through frames: - -```bash -# Enter/exit Play Mode (toggle) -unity-bridge play - -# Pause/unpause (while in Play Mode) -unity-bridge pause - -# Step one frame (while in Play Mode) -unity-bridge step -``` - -**Output (play):** -``` -✓ play completed -Play Mode: ▶ Playing -Duration: 0.01s -``` - -**Output (pause):** -``` -✓ pause completed -Play Mode: ⏸ Paused -Duration: 0.01s -``` - -**Output (step):** -``` -✓ step completed -Play Mode: ⏸ Paused -Duration: 0.02s -``` - -**Notes:** -- `play` toggles Play Mode on/off (like the Play button in Unity) -- `pause` and `step` require Play Mode to be active; returns error if not playing -- All three return the resulting `editorStatus` so the caller knows the current state - -### Advanced Options - -#### Timeout Configuration - -Override the default 30-second timeout: - -```bash -unity-bridge run-tests --timeout 60 -``` - -Use longer timeouts for: -- Large test suites -- PlayMode tests (which start/stop Play Mode) -- Full project compilation - -#### Cleanup Old Responses - -Automatically remove old response files before executing: - -```bash -unity-bridge compile --cleanup -``` - -This removes response files older than 1 hour. Useful for maintaining a clean workspace. - -#### Verbose Output - -See detailed execution progress: - -```bash -unity-bridge run-tests --verbose -``` - -Prints: -- Command ID -- Polling attempts -- Response file detection -- Cleanup operations - -### Error Handling - -The script provides clear error messages for common issues: - -**Unity Not Running:** -``` -Error: Unity Editor not detected. Ensure Unity is open with the project loaded. -``` - -**Command Timeout:** -``` -Error: Command timed out after 30s. Check Unity Console for errors. -``` - -**Invalid Parameters:** -``` -Error: Failed to write command file: Invalid mode 'InvalidMode' -``` - -**Exit Codes:** -- `0` - Success -- `1` - Error (Unity not running, invalid params, etc.) -- `2` - Timeout - -## Integration with Claude Code - -When you're working in a Unity project directory, you can ask Claude Code to perform Unity operations naturally: - -- "Run the Unity tests in EditMode" -- "Check if there are any compilation errors" -- "Show me the last 10 error logs from Unity" -- "Refresh the Unity asset database" -- "Enter Play Mode" -- "Pause the editor" -- "Step one frame" - -Claude Code will automatically use this skill to execute the commands via the Python script. - -## File Protocol Details - -### Command Format - -Written to `.unity-bridge/command.json`: - -```json -{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "action": "run-tests", - "params": { - "testMode": "EditMode", - "filter": "MyTests" - } -} -``` - -### Response Format - -Read from `.unity-bridge/response-{id}.json`: - -```json -{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "status": "success", - "action": "run-tests", - "duration_ms": 1250, - "result": { - "passed": 410, - "failed": 0, - "skipped": 0, - "failures": [] - } -} -``` - -**Status Values:** -- `running` - Command in progress (may see intermediate responses) -- `success` - Command completed successfully -- `failure` - Command completed with failures (e.g., failed tests) -- `error` - Command execution error - -## Project Structure - -``` -skill/ -├── SKILL.md # This file -├── pyproject.toml # Package configuration -├── src/ -│ └── claude_unity_bridge/ -│ ├── __init__.py # Package version -│ └── cli.py # CLI implementation -├── tests/ -│ └── test_cli.py # Unit tests -└── references/ - ├── COMMANDS.md # Detailed command specifications - └── EXTENDING.md # Guide for adding custom commands -``` - -## Detailed Documentation - -For more information, see: - -- **[COMMANDS.md](references/COMMANDS.md)** - Complete command reference with all parameters, response formats, and edge cases -- **[EXTENDING.md](references/EXTENDING.md)** - Tutorial for adding custom commands to the Unity Bridge for project-specific workflows +- Prefer the CLI over manual edits to bridge files +- Use `--timeout` for long-running test or compile operations +- Use `get-status` or `health-check` before assuming Unity is unavailable +- Use `get-console-logs --filter Error` before asking for a broad log dump ## Troubleshooting -### Unity Not Responding - -**Symptoms:** Commands timeout or "Unity not detected" error - -**Solutions:** -1. Ensure Unity Editor is open with the project loaded -2. Check that the package is installed (`Window > Package Manager`) -3. Verify `.unity-bridge/` directory exists in project root -4. Check Unity Console for errors from ClaudeBridge package - -### Response File Issues - -**Symptoms:** "Failed to parse response JSON" error - -**Solutions:** -1. Check Unity Console for ClaudeBridge errors -2. Manually inspect `.unity-bridge/response-*.json` files -3. Try cleaning up old responses with `--cleanup` flag -4. Restart Unity Editor if file system is in bad state - -### Performance Issues - -**Symptoms:** Slow response times, frequent timeouts - -**Solutions:** -1. Increase timeout with `--timeout 60` or higher -2. Close unnecessary Unity Editor windows -3. Reduce test scope with `--filter` parameter -4. Check system resources (CPU, memory) +If Codex cannot communicate with Unity: -### File Locking Errors +1. Verify the Unity Editor is open with the intended project +2. Check that `.codex-unity-bridge/` exists at the project root +3. Run `codex-unity-bridge health-check` +4. Inspect the Unity Console for `[CodexBridge]` messages -**Symptoms:** Intermittent errors reading/writing files - -**Solutions:** -1. The CLI handles file locking automatically with retries -2. If persistent, check for antivirus interference -3. Verify file permissions on `.unity-bridge/` directory - -## Installation - -### Quick Install - -```bash -pip install claude-unity-bridge -unity-bridge install-skill -``` - -This installs the CLI and the Claude Code skill. - -### Verify Setup +If the CLI is not on `PATH`, run: ```bash -unity-bridge health-check +python -m codex_unity_bridge.cli install-skill ``` -### Updating - -```bash -unity-bridge update -``` - -This upgrades the pip package and reinstalls the skill. - -### Uninstalling - -```bash -unity-bridge uninstall-skill -pip uninstall claude-unity-bridge -``` - -### Development Installation - -```bash -cd claude-unity-bridge/skill -pip install -e ".[dev]" -unity-bridge install-skill -``` - -## Why a CLI Tool? - -The skill uses a CLI tool instead of implementing the protocol directly in Claude Code prompts for several critical reasons: - -**Consistency:** UUID generation, polling logic, and error handling work identically every time. Without the CLI, Claude might implement these differently across sessions, leading to subtle bugs. - -**Reliability:** All edge cases are handled once in tested code: -- File locking when Unity writes responses -- Exponential backoff for polling -- Atomic command writes to prevent corruption -- Graceful handling of malformed JSON -- Proper cleanup of stale files - -**Error Messages:** Clear, actionable error messages for all failure modes. Claude doesn't have to figure out what went wrong each time. - -**Token Efficiency:** The CLI handles complexity, so Claude doesn't need to manage low-level details in-context. The SKILL.md stays concise while providing full functionality. - -**Deterministic Exit Codes:** Shell integration works reliably with standard exit codes (0=success, 1=error, 2=timeout). - -**Rock Solid:** Test the CLI once, it works forever. No variability between Claude sessions. - -## Support +## References -For issues or questions: -- Package Issues: https://github.com/ManageXR/claude-unity-bridge/issues -- Skill Issues: Report in the same repository with `[Skill]` prefix +- [Command Reference](references/COMMANDS.md) +- [Extending the Bridge](references/EXTENDING.md) diff --git a/skill/TESTING.md b/skill/TESTING.md index 8431879..dfe2224 100644 --- a/skill/TESTING.md +++ b/skill/TESTING.md @@ -88,7 +88,7 @@ For manual testing with a real Unity instance: ### 1. Start Unity -Open Unity Editor with a project that has the Claude Unity Bridge package installed. +Open Unity Editor with a project that has the Codex Unity Bridge package installed. ### 2. Run Commands @@ -96,34 +96,34 @@ Open Unity Editor with a project that has the Claude Unity Bridge package instal cd skill # Check editor status -python3 scripts/cli.py get-status +python3 src/codex_unity_bridge/cli.py get-status # Trigger compilation -python3 scripts/cli.py compile +python3 src/codex_unity_bridge/cli.py compile # Run tests -python3 scripts/cli.py run-tests --mode EditMode +python3 src/codex_unity_bridge/cli.py run-tests --mode EditMode # Get console logs -python3 scripts/cli.py get-console-logs --limit 10 --filter Error +python3 src/codex_unity_bridge/cli.py get-console-logs --limit 10 --filter Error # Refresh assets -python3 scripts/cli.py refresh +python3 src/codex_unity_bridge/cli.py refresh ``` ### 3. Test Error Scenarios ```bash # Close Unity to test "Unity not running" error -python3 scripts/cli.py get-status +python3 src/codex_unity_bridge/cli.py get-status # Should error: "Unity Editor not detected" # Test timeout -python3 scripts/cli.py get-status --timeout 2 +python3 src/codex_unity_bridge/cli.py get-status --timeout 2 # If Unity is slow to respond, will timeout # Test verbose mode -python3 scripts/cli.py compile --verbose +python3 src/codex_unity_bridge/cli.py compile --verbose # Shows detailed execution progress ``` diff --git a/skill/pyproject.toml b/skill/pyproject.toml index ef2434e..754196b 100644 --- a/skill/pyproject.toml +++ b/skill/pyproject.toml @@ -3,15 +3,15 @@ requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "claude-unity-bridge" -version = "0.2.2" -description = "Control Unity Editor from Claude Code" +name = "codex-unity-bridge" +version = "0.3.0" +description = "Control Unity Editor from Codex" readme = "SKILL.md" license = {text = "Apache-2.0"} requires-python = ">=3.8" [project.scripts] -unity-bridge = "claude_unity_bridge.cli:main" +codex-unity-bridge = "codex_unity_bridge.cli:main" [project.optional-dependencies] dev = ["pytest>=7.0", "pytest-cov>=4.0", "black>=23.0", "flake8>=6.0"] @@ -20,4 +20,7 @@ dev = ["pytest>=7.0", "pytest-cov>=4.0", "black>=23.0", "flake8>=6.0"] where = ["src"] [tool.setuptools.package-data] -claude_unity_bridge = ["skill/*.md", "skill/references/*.md"] +codex_unity_bridge = ["skill/*.md", "skill/references/*.md"] + +[tool.pytest.ini_options] +pythonpath = ["src"] diff --git a/skill/references/COMMANDS.md b/skill/references/COMMANDS.md index 1481c53..9566196 100644 --- a/skill/references/COMMANDS.md +++ b/skill/references/COMMANDS.md @@ -22,7 +22,7 @@ Execute Unity tests in EditMode or PlayMode. ### Usage ```bash -unity-bridge run-tests [options] +codex-unity-bridge run-tests [options] ``` ### Parameters @@ -44,7 +44,7 @@ unity-bridge run-tests [options] - Filter by test name or namespace - Examples: - `"MyTests"` - All tests containing "MyTests" - - `"MXR.Tests.Auth"` - All tests in the Auth namespace + - `"Example.Tests.Auth"` - All tests in the Auth namespace - `"LoginTest;LogoutTest"` - Multiple filters (semicolon-separated) - Case-sensitive - Matches test names using Unity's test filter syntax @@ -80,11 +80,11 @@ unity-bridge run-tests [options] "skipped": 0, "failures": [ { - "name": "MXR.Tests.AuthTests.LoginWithInvalidCredentials", + "name": "Example.Tests.AuthTests.LoginWithInvalidCredentials", "message": "Expected: success\nActual: failure\nat Assets/Tests/AuthTests.cs:45" }, { - "name": "MXR.Tests.NetworkTests.TimeoutHandling", + "name": "Example.Tests.NetworkTests.TimeoutHandling", "message": "NullReferenceException: Object reference not set\nat Assets/Tests/NetworkTests.cs:123" } ] @@ -104,7 +104,7 @@ During execution, you may see intermediate `status: "running"` responses with pr "progress": { "current": 150, "total": 410, - "currentTest": "MXR.Tests.Player.MovementTest" + "currentTest": "Example.Tests.Player.MovementTest" }, "failures": [] } @@ -121,11 +121,11 @@ The CLI formats the output for readability: Duration: 3.50s Failed Tests: - - MXR.Tests.AuthTests.LoginWithInvalidCredentials + - Example.Tests.AuthTests.LoginWithInvalidCredentials Expected: success Actual: failure at Assets/Tests/AuthTests.cs:45 - - MXR.Tests.NetworkTests.TimeoutHandling + - Example.Tests.NetworkTests.TimeoutHandling NullReferenceException: Object reference not set at Assets/Tests/NetworkTests.cs:123 ``` @@ -180,19 +180,19 @@ Failed Tests: ```bash # Run all EditMode tests (fast) -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode # Run specific test suite -unity-bridge run-tests --filter "MXR.Tests.Auth" +codex-unity-bridge run-tests --filter "Example.Tests.Auth" # Run multiple test suites -unity-bridge run-tests --filter "AuthTests;NetworkTests" +codex-unity-bridge run-tests --filter "AuthTests;NetworkTests" # Run PlayMode tests with extended timeout -unity-bridge run-tests --mode PlayMode --timeout 60 +codex-unity-bridge run-tests --mode PlayMode --timeout 60 # Run all tests -unity-bridge run-tests +codex-unity-bridge run-tests ``` --- @@ -204,7 +204,7 @@ Trigger Unity script compilation and wait for completion. ### Usage ```bash -unity-bridge compile [options] +codex-unity-bridge compile [options] ``` ### Parameters @@ -291,13 +291,13 @@ The command will wait for compilation to complete. ```bash # Basic compilation -unity-bridge compile +codex-unity-bridge compile # With extended timeout for large projects -unity-bridge compile --timeout 60 +codex-unity-bridge compile --timeout 60 # Cleanup old responses first -unity-bridge compile --cleanup +codex-unity-bridge compile --cleanup ``` --- @@ -309,7 +309,7 @@ Force Unity to refresh the asset database, reimporting changed assets. ### Usage ```bash -unity-bridge refresh [options] +codex-unity-bridge refresh [options] ``` ### Parameters @@ -391,13 +391,13 @@ Unity will refresh but may report import errors in the console. The command will ```bash # Basic refresh -unity-bridge refresh +codex-unity-bridge refresh # After git operations (pulling changes) -git pull && unity-bridge refresh +git pull && codex-unity-bridge refresh # With extended timeout for large projects -unity-bridge refresh --timeout 60 +codex-unity-bridge refresh --timeout 60 ``` --- @@ -409,7 +409,7 @@ Get current Unity Editor state, including compilation status, play mode, and upd ### Usage ```bash -unity-bridge get-status +codex-unity-bridge get-status ``` ### Parameters @@ -499,7 +499,7 @@ Check status before: ```bash # Check current status -unity-bridge get-status +codex-unity-bridge get-status # Wait for compilation to finish (pseudo-code workflow) while status.isCompiling: @@ -517,7 +517,7 @@ Retrieve Unity console logs with filtering options. ### Usage ```bash -unity-bridge get-console-logs [options] +codex-unity-bridge get-console-logs [options] ``` ### Parameters @@ -641,20 +641,20 @@ No console logs found ```bash # Get last 20 logs -unity-bridge get-console-logs --limit 20 +codex-unity-bridge get-console-logs --limit 20 # Get only errors -unity-bridge get-console-logs --filter Error +codex-unity-bridge get-console-logs --filter Error # Get only warnings -unity-bridge get-console-logs --filter Warning +codex-unity-bridge get-console-logs --filter Warning # Get last 5 logs of all types -unity-bridge get-console-logs --limit 5 +codex-unity-bridge get-console-logs --limit 5 # Check for errors after compilation -unity-bridge compile -unity-bridge get-console-logs --filter Error --limit 10 +codex-unity-bridge compile +codex-unity-bridge get-console-logs --filter Error --limit 10 ``` --- @@ -666,7 +666,7 @@ Toggle Unity Editor Play Mode. If not playing, enters Play Mode; if playing, exi ### Usage ```bash -unity-bridge play [options] +codex-unity-bridge play [options] ``` ### Parameters @@ -732,13 +732,13 @@ Duration: 0.01s ```bash # Enter Play Mode -unity-bridge play +codex-unity-bridge play # Check state after toggling -unity-bridge get-status +codex-unity-bridge get-status # Exit Play Mode (call again) -unity-bridge play +codex-unity-bridge play ``` --- @@ -750,7 +750,7 @@ Toggle the pause state while in Play Mode. If playing, pauses; if paused, unpaus ### Usage ```bash -unity-bridge pause [options] +codex-unity-bridge pause [options] ``` ### Parameters @@ -820,14 +820,14 @@ Formatted as: ```bash # Enter Play Mode, then pause -unity-bridge play -unity-bridge pause +codex-unity-bridge play +codex-unity-bridge pause # Unpause -unity-bridge pause +codex-unity-bridge pause # Check current state -unity-bridge get-status +codex-unity-bridge get-status ``` --- @@ -839,7 +839,7 @@ Step one frame forward in Play Mode. If not paused, Unity will pause first then ### Usage ```bash -unity-bridge step [options] +codex-unity-bridge step [options] ``` ### Parameters @@ -902,14 +902,14 @@ Formatted as: ```bash # Enter Play Mode, pause, then step through frames -unity-bridge play -unity-bridge pause -unity-bridge step -unity-bridge step -unity-bridge step +codex-unity-bridge play +codex-unity-bridge pause +codex-unity-bridge step +codex-unity-bridge step +codex-unity-bridge step # Check state between steps -unity-bridge get-status +codex-unity-bridge get-status ``` --- @@ -920,11 +920,11 @@ unity-bridge get-status ```bash # Check if compiling -status=$(unity-bridge get-status) +status=$(codex-unity-bridge get-status) # If ready, run tests if [[ $status == *"✓ Ready"* ]]; then - unity-bridge run-tests + codex-unity-bridge run-tests else echo "Waiting for compilation..." fi @@ -934,11 +934,11 @@ fi ```bash # Run tests -unity-bridge run-tests +codex-unity-bridge run-tests # If failed (exit code 1), get error logs if [ $? -ne 0 ]; then - unity-bridge get-console-logs --filter Error --limit 10 + codex-unity-bridge get-console-logs --filter Error --limit 10 fi ``` @@ -946,16 +946,16 @@ fi ```bash # 1. Check status -unity-bridge get-status +codex-unity-bridge get-status # 2. Compile -unity-bridge compile +codex-unity-bridge compile # 3. Run tests -unity-bridge run-tests +codex-unity-bridge run-tests # 4. Check for errors -unity-bridge get-console-logs --filter Error +codex-unity-bridge get-console-logs --filter Error ``` ### After Git Pull Workflow @@ -965,13 +965,13 @@ unity-bridge get-console-logs --filter Error git pull # Refresh assets -unity-bridge refresh +codex-unity-bridge refresh # Wait for compilation # (Unity will auto-compile after refresh) # Run tests -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode ``` --- @@ -992,7 +992,7 @@ All commands return standard exit codes for shell integration: #!/bin/bash # Run tests and check exit code -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode case $? in 0) @@ -1065,7 +1065,7 @@ If commands frequently timeout: **"Unity Editor not detected"** - Unity is not running - Unity project is not open -- `.unity-bridge/` directory doesn't exist (package not installed) +- `.codex-unity-bridge/` directory doesn't exist (package not installed) **Solution:** Open Unity with your project and ensure package is installed. @@ -1107,7 +1107,7 @@ If commands frequently timeout: ### Linux - File paths use forward slashes - Case-sensitive file systems -- May need to adjust file permissions on `.unity-bridge/` directory +- May need to adjust file permissions on `.codex-unity-bridge/` directory --- diff --git a/skill/references/EXTENDING.md b/skill/references/EXTENDING.md index 788731c..d683060 100644 --- a/skill/references/EXTENDING.md +++ b/skill/references/EXTENDING.md @@ -1,4 +1,4 @@ -# Extending Claude Unity Bridge +# Extending Codex Unity Bridge Learn how to add custom commands to the Unity Bridge for project-specific workflows. @@ -29,7 +29,7 @@ The Unity Bridge ships with 5 core commands that work for any Unity project. How - **Project-Specific Tools:** Run custom editor tools or validators - **CI/CD Integration:** Automate project-specific build/test workflows -By extending the bridge with custom commands, you can automate these workflows through Claude Code. +By extending the bridge with custom commands, you can automate these workflows through Codex. --- @@ -38,23 +38,23 @@ By extending the bridge with custom commands, you can automate these workflows t The Unity Bridge uses a **Command Pattern** for extensibility: ``` -Claude Code (Python Script) +Codex (Python Script) ↓ writes command.json ↓ polls (Unity EditorApplication.update) -ClaudeBridge.cs (Command Dispatcher) +CodexBridge.cs (Command Dispatcher) ↓ routes to ICommand Implementation (YourCustomCommand.cs) ↓ writes response-{id}.json ↓ reads -Claude Code (Python Script) +Codex (Python Script) ``` **Key Components:** 1. **ICommand Interface** - All commands implement this interface -2. **ClaudeBridge.cs** - Dispatches commands to registered handlers +2. **CodexBridge.cs** - Dispatches commands to registered handlers 3. **CommandRequest** - Input data structure 4. **CommandResponse** - Output data structure 5. **Python Script** - Handles command execution (no changes needed!) @@ -70,11 +70,11 @@ Create `Editor/Commands/YourCommand.cs`: ```csharp using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class YourCommand : ICommand { public void Execute( CommandRequest request, @@ -83,7 +83,7 @@ namespace MXR.ClaudeBridge.Commands { ) { var stopwatch = Stopwatch.StartNew(); - Debug.Log("[ClaudeBridge] Executing your custom command"); + Debug.Log("[CodexBridge] Executing your custom command"); // Report progress (optional) var progressResponse = CommandResponse.Running(request.id, request.action); @@ -109,7 +109,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Command failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Command failed: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -126,7 +126,7 @@ namespace MXR.ClaudeBridge.Commands { ### Step 2: Register Command -In `Editor/ClaudeBridge.cs`, add your command to the dictionary: +In `Editor/CodexBridge.cs`, add your command to the dictionary: ```csharp Commands = new Dictionary { @@ -145,10 +145,10 @@ The CLI automatically works with your custom command: ```bash # The script handles all the file I/O, polling, and formatting -unity-bridge your-command +codex-unity-bridge your-command ``` -That's it! Your command is now available through Claude Code. +That's it! Your command is now available through Codex. --- @@ -338,13 +338,13 @@ Create a command to build your Unity project for a specific platform: using System; using System.Diagnostics; using System.IO; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEditor.Build.Reporting; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class BuildCommand : ICommand { public void Execute( CommandRequest request, @@ -357,7 +357,7 @@ namespace MXR.ClaudeBridge.Commands { var platformString = request.@params?.targetPlatform ?? "StandaloneWindows64"; var isDevelopment = request.@params?.developmentBuild ?? false; - Debug.Log($"[ClaudeBridge] Building for {platformString} (development: {isDevelopment})"); + Debug.Log($"[CodexBridge] Building for {platformString} (development: {isDevelopment})"); // Report progress var progressResponse = CommandResponse.Running(request.id, request.action); @@ -384,7 +384,7 @@ namespace MXR.ClaudeBridge.Commands { // Check result if (report.summary.result == BuildResult.Succeeded) { - Debug.Log($"[ClaudeBridge] Build succeeded: {report.summary.totalSize} bytes"); + Debug.Log($"[CodexBridge] Build succeeded: {report.summary.totalSize} bytes"); var response = CommandResponse.Success( request.id, @@ -403,7 +403,7 @@ namespace MXR.ClaudeBridge.Commands { onComplete?.Invoke(response); } else { - Debug.LogError($"[ClaudeBridge] Build failed: {report.summary.result}"); + Debug.LogError($"[CodexBridge] Build failed: {report.summary.result}"); var response = CommandResponse.Failure( request.id, @@ -417,7 +417,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Build error: {e.Message}"); + Debug.LogError($"[CodexBridge] Build error: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -445,7 +445,7 @@ namespace MXR.ClaudeBridge.Commands { } ``` -**Register in ClaudeBridge.cs:** +**Register in CodexBridge.cs:** ```csharp Commands = new Dictionary { @@ -459,7 +459,7 @@ Commands = new Dictionary { ```bash # You'll need to extend the CLI to support --target-platform and --development-build # Or create a project-specific wrapper script -unity-bridge build +codex-unity-bridge build ``` --- @@ -471,13 +471,13 @@ Load or validate specific scenes: ```csharp using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class LoadSceneCommand : ICommand { public void Execute( CommandRequest request, @@ -496,14 +496,14 @@ namespace MXR.ClaudeBridge.Commands { return; } - Debug.Log($"[ClaudeBridge] Loading scene: {scenePath}"); + Debug.Log($"[CodexBridge] Loading scene: {scenePath}"); try { // Load scene var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); stopwatch.Stop(); - Debug.Log($"[ClaudeBridge] Scene loaded: {scene.name}"); + Debug.Log($"[CodexBridge] Scene loaded: {scene.name}"); var response = CommandResponse.Success( request.id, @@ -523,7 +523,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Failed to load scene: {e.Message}"); + Debug.LogError($"[CodexBridge] Failed to load scene: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -545,12 +545,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class ValidateAssetsCommand : ICommand { public void Execute( CommandRequest request, @@ -559,7 +559,7 @@ namespace MXR.ClaudeBridge.Commands { ) { var stopwatch = Stopwatch.StartNew(); - Debug.Log("[ClaudeBridge] Validating assets"); + Debug.Log("[CodexBridge] Validating assets"); var progressResponse = CommandResponse.Running(request.id, request.action); onProgress?.Invoke(progressResponse); @@ -603,7 +603,7 @@ namespace MXR.ClaudeBridge.Commands { stopwatch.Stop(); if (issues.Count == 0) { - Debug.Log("[ClaudeBridge] Asset validation passed"); + Debug.Log("[CodexBridge] Asset validation passed"); var response = CommandResponse.Success( request.id, @@ -614,7 +614,7 @@ namespace MXR.ClaudeBridge.Commands { onComplete?.Invoke(response); } else { - Debug.LogWarning($"[ClaudeBridge] Asset validation found {issues.Count} issues"); + Debug.LogWarning($"[CodexBridge] Asset validation found {issues.Count} issues"); var response = CommandResponse.Failure( request.id, @@ -637,7 +637,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Asset validation error: {e.Message}"); + Debug.LogError($"[CodexBridge] Asset validation error: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -686,7 +686,7 @@ public class CIBuildCommand : ICommand { unity -batchmode -projectPath . -executeMethod YourCICommand.Execute # Or use the bridge directly -unity-bridge ci-build +codex-unity-bridge ci-build ``` ### Pre-Commit Hook @@ -698,8 +698,8 @@ Validate project before committing: # .git/hooks/pre-commit # Run Unity validation -unity-bridge validate-assets -unity-bridge run-tests --mode EditMode +codex-unity-bridge validate-assets +codex-unity-bridge run-tests --mode EditMode if [ $? -ne 0 ]; then echo "Tests failed! Fix issues before committing." @@ -746,14 +746,14 @@ try { onComplete?.Invoke(CommandResponse.Success(...)); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error: {e.Message}"); + Debug.LogError($"[CodexBridge] Error: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } ``` ### 2. Report Progress for Long Operations -Keep Claude informed during long-running operations: +Keep Codex informed during long-running operations: ```csharp for (int i = 0; i < totalSteps; i++) { @@ -790,9 +790,9 @@ var response = CommandResponse.Success( Help users debug by logging command execution: ```csharp -Debug.Log($"[ClaudeBridge] Starting {request.action}"); +Debug.Log($"[CodexBridge] Starting {request.action}"); // ... operation -Debug.Log($"[ClaudeBridge] Completed {request.action}"); +Debug.Log($"[CodexBridge] Completed {request.action}"); ``` ### 5. Validate Parameters @@ -851,7 +851,7 @@ if (EditorApplication.isPlaying) { 1. **Write Command File:** ```bash -cat > .unity-bridge/command.json << EOF +cat > .codex-unity-bridge/command.json << EOF { "id": "test-123", "action": "your-command", @@ -869,7 +869,7 @@ EOF 3. **Read Response:** ```bash -cat .unity-bridge/response-test-123.json +cat .codex-unity-bridge/response-test-123.json ``` ### Via Python Script @@ -877,7 +877,7 @@ cat .unity-bridge/response-test-123.json Once registered, test via the CLI: ```bash -unity-bridge your-command --verbose +codex-unity-bridge your-command --verbose ``` The `--verbose` flag shows detailed execution progress. @@ -947,7 +947,7 @@ elif args.command == "load-scene": **Symptom:** "Unknown action: your-command" error **Solution:** -1. Check that command is registered in `ClaudeBridge.cs` +1. Check that command is registered in `CodexBridge.cs` 2. Verify command name matches exactly (case-sensitive) 3. Ensure Unity reloaded scripts after adding command @@ -987,7 +987,7 @@ elif args.command == "load-scene": Extending the Unity Bridge is straightforward: 1. **Create command class** implementing `ICommand` -2. **Register command** in `ClaudeBridge.cs` +2. **Register command** in `CodexBridge.cs` 3. **Test with CLI** - it automatically handles file I/O The architecture is designed for extensibility while keeping the core simple and reliable. Your custom commands benefit from the same rock-solid file protocol, error handling, and progress reporting as the built-in commands. diff --git a/skill/src/claude_unity_bridge/__init__.py b/skill/src/claude_unity_bridge/__init__.py deleted file mode 100644 index 20fbf2a..0000000 --- a/skill/src/claude_unity_bridge/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Claude Unity Bridge - Control Unity Editor from Claude Code.""" - -__version__ = "0.2.2" diff --git a/skill/src/claude_unity_bridge/skill/SKILL.md b/skill/src/claude_unity_bridge/skill/SKILL.md deleted file mode 100644 index 9ea4292..0000000 --- a/skill/src/claude_unity_bridge/skill/SKILL.md +++ /dev/null @@ -1,478 +0,0 @@ ---- -name: unity -description: Execute Unity Editor commands (run tests, compile, get logs, refresh assets, play/pause/step) via file-based bridge. Auto-activates for Unity-related tasks. Requires com.mxr.claude-bridge package installed in Unity project. ---- - -# Unity Bridge Skill - -Control Unity Editor operations from Claude Code using a reliable file-based communication protocol. - -## Overview - -The Unity Bridge enables Claude Code to trigger operations in a running Unity Editor instance without network configuration or port conflicts. It uses a simple file-based protocol where commands are written to `.unity-bridge/command.json` and responses are read from `.unity-bridge/response-{id}.json`. - -**Key Features:** -- Execute EditMode and PlayMode tests -- Trigger script compilation -- Refresh asset database -- Check editor status (compilation, play mode, etc.) -- Retrieve Unity console logs -- Control Play Mode (play, pause, step) - -**Multi-Project Support:** Each Unity project has its own `.unity-bridge/` directory, allowing multiple projects to be worked on simultaneously. - -## Requirements - -1. **Unity Package:** Install `com.mxr.claude-bridge` in your Unity project - - Via Package Manager: `https://github.com/ManageXR/claude-unity-bridge.git?path=package` - - See main package README for installation instructions - -2. **Unity Editor:** Must be open with your project loaded - -3. **Python 3:** The skill uses a Python script for reliable command execution - -## How It Works - -The skill uses a CLI tool (`unity-bridge`) that handles: -- UUID generation for command tracking -- Atomic file writes to prevent corruption -- Exponential backoff polling for responses -- File locking handling -- Automatic cleanup of old response files -- Formatted, human-readable output - -This approach ensures **deterministic, rock-solid execution** - the script is tested once and behaves identically every time, handling all edge cases (timeouts, file locking, malformed responses, etc.) without requiring Claude to manage these details in-context. - -## Usage - -### Basic Pattern - -When you need to interact with Unity, use the CLI directly: - -```bash -unity-bridge [command] [options] -``` - -All commands automatically: -- Generate a unique UUID for tracking -- Write the command atomically -- Poll for response with timeout -- Format output for readability -- Cleanup response files - -### Command Examples - -#### Run Tests - -Execute Unity tests in EditMode or PlayMode: - -```bash -# Run all EditMode tests -unity-bridge run-tests --mode EditMode - -# Run tests with filter -unity-bridge run-tests --mode EditMode --filter "MXR.Tests" - -# Run all tests (both modes) -unity-bridge run-tests -``` - -**Output:** -``` -✓ Tests Passed: 410 -✗ Tests Failed: 2 -○ Tests Skipped: 0 -Duration: 1.25s - -Failed Tests: - - MXR.Tests.AuthTests.LoginWithInvalidCredentials - Expected: success, Actual: failure - - MXR.Tests.NetworkTests.TimeoutHandling - NullReferenceException: Object reference not set -``` - -**Parameters:** -- `--mode` - `EditMode` or `PlayMode` (optional, defaults to both) -- `--filter` - Test name filter pattern (optional) -- `--timeout` - Override default 30s timeout - -#### Compile Scripts - -Trigger Unity script compilation: - -```bash -unity-bridge compile -``` - -**Output (Success):** -``` -✓ Compilation Successful -Duration: 2.3s -``` - -**Output (Failure):** -``` -✗ Compilation Failed - -Assets/Scripts/Player.cs:25: error CS0103: The name 'invalidVar' does not exist -Assets/Scripts/Enemy.cs:67: error CS0246: Type 'MissingClass' could not be found -``` - -#### Get Console Logs - -Retrieve Unity console output: - -```bash -# Get last 20 logs -unity-bridge get-console-logs --limit 20 - -# Get only errors -unity-bridge get-console-logs --limit 10 --filter Error - -# Get warnings -unity-bridge get-console-logs --filter Warning -``` - -**Output:** -``` -Console Logs (last 10, filtered by Error): - -[Error] NullReferenceException: Object reference not set - at Player.Update() in Assets/Scripts/Player.cs:34 - -[Error] Failed to load asset: missing_texture.png - -[Error] (x3) Shader compilation failed - See Console for details -``` - -**Parameters:** -- `--limit` - Maximum number of logs (default: 50) -- `--filter` - Filter by type: `Log`, `Warning`, or `Error` - -#### Get Editor Status - -Check Unity Editor state: - -```bash -unity-bridge get-status -``` - -**Output:** -``` -Unity Editor Status: - - Compilation: ✓ Ready - - Play Mode: ✏ Editing - - Updating: No -``` - -**Possible States:** -- Compilation: `✓ Ready` or `⏳ Compiling...` -- Play Mode: `✏ Editing`, `▶ Playing`, or `⏸ Paused` -- Updating: `Yes` or `No` - -#### Refresh Asset Database - -Force Unity to refresh assets: - -```bash -unity-bridge refresh -``` - -**Output:** -``` -✓ Asset Database Refreshed -Duration: 0.5s -``` - -#### Play Mode Control - -Toggle Play Mode, pause, and step through frames: - -```bash -# Enter/exit Play Mode (toggle) -unity-bridge play - -# Pause/unpause (while in Play Mode) -unity-bridge pause - -# Step one frame (while in Play Mode) -unity-bridge step -``` - -**Output (play):** -``` -✓ play completed -Play Mode: ▶ Playing -Duration: 0.01s -``` - -**Output (pause):** -``` -✓ pause completed -Play Mode: ⏸ Paused -Duration: 0.01s -``` - -**Output (step):** -``` -✓ step completed -Play Mode: ⏸ Paused -Duration: 0.02s -``` - -**Notes:** -- `play` toggles Play Mode on/off (like the Play button in Unity) -- `pause` and `step` require Play Mode to be active; returns error if not playing -- All three return the resulting `editorStatus` so the caller knows the current state - -### Advanced Options - -#### Timeout Configuration - -Override the default 30-second timeout: - -```bash -unity-bridge run-tests --timeout 60 -``` - -Use longer timeouts for: -- Large test suites -- PlayMode tests (which start/stop Play Mode) -- Full project compilation - -#### Cleanup Old Responses - -Automatically remove old response files before executing: - -```bash -unity-bridge compile --cleanup -``` - -This removes response files older than 1 hour. Useful for maintaining a clean workspace. - -#### Verbose Output - -See detailed execution progress: - -```bash -unity-bridge run-tests --verbose -``` - -Prints: -- Command ID -- Polling attempts -- Response file detection -- Cleanup operations - -### Error Handling - -The script provides clear error messages for common issues: - -**Unity Not Running:** -``` -Error: Unity Editor not detected. Ensure Unity is open with the project loaded. -``` - -**Command Timeout:** -``` -Error: Command timed out after 30s. Check Unity Console for errors. -``` - -**Invalid Parameters:** -``` -Error: Failed to write command file: Invalid mode 'InvalidMode' -``` - -**Exit Codes:** -- `0` - Success -- `1` - Error (Unity not running, invalid params, etc.) -- `2` - Timeout - -## Integration with Claude Code - -When you're working in a Unity project directory, you can ask Claude Code to perform Unity operations naturally: - -- "Run the Unity tests in EditMode" -- "Check if there are any compilation errors" -- "Show me the last 10 error logs from Unity" -- "Refresh the Unity asset database" -- "Enter Play Mode" -- "Pause the editor" -- "Step one frame" - -Claude Code will automatically use this skill to execute the commands via the Python script. - -## File Protocol Details - -### Command Format - -Written to `.unity-bridge/command.json`: - -```json -{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "action": "run-tests", - "params": { - "testMode": "EditMode", - "filter": "MyTests" - } -} -``` - -### Response Format - -Read from `.unity-bridge/response-{id}.json`: - -```json -{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "status": "success", - "action": "run-tests", - "duration_ms": 1250, - "result": { - "passed": 410, - "failed": 0, - "skipped": 0, - "failures": [] - } -} -``` - -**Status Values:** -- `running` - Command in progress (may see intermediate responses) -- `success` - Command completed successfully -- `failure` - Command completed with failures (e.g., failed tests) -- `error` - Command execution error - -## Project Structure - -``` -skill/ -├── SKILL.md # This file -├── pyproject.toml # Package configuration -├── src/ -│ └── claude_unity_bridge/ -│ ├── __init__.py # Package version -│ └── cli.py # CLI implementation -├── tests/ -│ └── test_cli.py # Unit tests -└── references/ - ├── COMMANDS.md # Detailed command specifications - └── EXTENDING.md # Guide for adding custom commands -``` - -## Detailed Documentation - -For more information, see: - -- **[COMMANDS.md](references/COMMANDS.md)** - Complete command reference with all parameters, response formats, and edge cases -- **[EXTENDING.md](references/EXTENDING.md)** - Tutorial for adding custom commands to the Unity Bridge for project-specific workflows - -## Troubleshooting - -### Unity Not Responding - -**Symptoms:** Commands timeout or "Unity not detected" error - -**Solutions:** -1. Ensure Unity Editor is open with the project loaded -2. Check that the package is installed (`Window > Package Manager`) -3. Verify `.unity-bridge/` directory exists in project root -4. Check Unity Console for errors from ClaudeBridge package - -### Response File Issues - -**Symptoms:** "Failed to parse response JSON" error - -**Solutions:** -1. Check Unity Console for ClaudeBridge errors -2. Manually inspect `.unity-bridge/response-*.json` files -3. Try cleaning up old responses with `--cleanup` flag -4. Restart Unity Editor if file system is in bad state - -### Performance Issues - -**Symptoms:** Slow response times, frequent timeouts - -**Solutions:** -1. Increase timeout with `--timeout 60` or higher -2. Close unnecessary Unity Editor windows -3. Reduce test scope with `--filter` parameter -4. Check system resources (CPU, memory) - -### File Locking Errors - -**Symptoms:** Intermittent errors reading/writing files - -**Solutions:** -1. The CLI handles file locking automatically with retries -2. If persistent, check for antivirus interference -3. Verify file permissions on `.unity-bridge/` directory - -## Installation - -### Quick Install - -```bash -pip install claude-unity-bridge -unity-bridge install-skill -``` - -This installs the CLI and the Claude Code skill. - -### Verify Setup - -```bash -unity-bridge health-check -``` - -### Updating - -```bash -unity-bridge update -``` - -This upgrades the pip package and reinstalls the skill. - -### Uninstalling - -```bash -unity-bridge uninstall-skill -pip uninstall claude-unity-bridge -``` - -### Development Installation - -```bash -cd claude-unity-bridge/skill -pip install -e ".[dev]" -unity-bridge install-skill -``` - -## Why a CLI Tool? - -The skill uses a CLI tool instead of implementing the protocol directly in Claude Code prompts for several critical reasons: - -**Consistency:** UUID generation, polling logic, and error handling work identically every time. Without the CLI, Claude might implement these differently across sessions, leading to subtle bugs. - -**Reliability:** All edge cases are handled once in tested code: -- File locking when Unity writes responses -- Exponential backoff for polling -- Atomic command writes to prevent corruption -- Graceful handling of malformed JSON -- Proper cleanup of stale files - -**Error Messages:** Clear, actionable error messages for all failure modes. Claude doesn't have to figure out what went wrong each time. - -**Token Efficiency:** The CLI handles complexity, so Claude doesn't need to manage low-level details in-context. The SKILL.md stays concise while providing full functionality. - -**Deterministic Exit Codes:** Shell integration works reliably with standard exit codes (0=success, 1=error, 2=timeout). - -**Rock Solid:** Test the CLI once, it works forever. No variability between Claude sessions. - -## Support - -For issues or questions: -- Package Issues: https://github.com/ManageXR/claude-unity-bridge/issues -- Skill Issues: Report in the same repository with `[Skill]` prefix diff --git a/skill/src/codex_unity_bridge/__init__.py b/skill/src/codex_unity_bridge/__init__.py new file mode 100644 index 0000000..9a56495 --- /dev/null +++ b/skill/src/codex_unity_bridge/__init__.py @@ -0,0 +1,3 @@ +"""Codex Unity Bridge - Control Unity Editor from Codex.""" + +__version__ = "0.3.0" diff --git a/skill/src/claude_unity_bridge/cli.py b/skill/src/codex_unity_bridge/cli.py similarity index 95% rename from skill/src/claude_unity_bridge/cli.py rename to skill/src/codex_unity_bridge/cli.py index 2b302c9..e2527c1 100644 --- a/skill/src/claude_unity_bridge/cli.py +++ b/skill/src/codex_unity_bridge/cli.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 """ -Claude Unity Bridge - Command Execution Script +Codex Unity Bridge - Command Execution Script Rock-solid, deterministic command execution for Unity Editor operations. Handles UUID generation, file-based polling, response parsing, and cleanup. -Also provides skill installation commands for Claude Code integration. +Also provides skill installation commands for Codex integration. """ import argparse @@ -22,7 +22,7 @@ from typing import Dict, Any, Optional # Constants -UNITY_DIR = Path.cwd() / ".unity-bridge" +UNITY_DIR = Path.cwd() / ".codex-unity-bridge" DEFAULT_TIMEOUT = 30 MIN_SLEEP = 0.1 MAX_SLEEP = 1.0 @@ -66,20 +66,21 @@ def _validate_command_id(command_id: str) -> None: def check_gitignore_and_notify(): - """Print a notice if .unity-bridge/ is not in .gitignore.""" + """Print a notice if .codex-unity-bridge/ is not in .gitignore.""" gitignore_path = Path.cwd() / ".gitignore" if gitignore_path.exists(): try: content = gitignore_path.read_text() # Check for various patterns that would ignore the directory - if ".unity-bridge" in content: + if ".codex-unity-bridge" in content: return # Already ignored except Exception: pass # If we can't read gitignore, show the notice print( - "\nNote: Add '.unity-bridge/' to your .gitignore to avoid committing runtime files.\n", + "\nNote: Add '.codex-unity-bridge/' to your .gitignore to avoid committing " + "runtime files.\n", file=sys.stderr, ) @@ -103,7 +104,7 @@ def write_command(action: str, params: Dict[str, Any]) -> str: # Security: Ensure UNITY_DIR is not a symlink (prevent symlink attacks) if UNITY_DIR.exists() and UNITY_DIR.is_symlink(): - raise UnityCommandError("Security error: .unity-bridge cannot be a symlink") + raise UnityCommandError("Security error: .codex-unity-bridge cannot be a symlink") # Ensure directory exists dir_existed = UNITY_DIR.exists() @@ -595,7 +596,7 @@ def get_skill_source_dir() -> Optional[Path]: # For Python 3.9+ if hasattr(resources, "files"): - package_dir = resources.files("claude_unity_bridge") + package_dir = resources.files("codex_unity_bridge") skill_dir = Path(str(package_dir)) / "skill" if skill_dir.exists(): return skill_dir @@ -611,19 +612,19 @@ def get_skill_source_dir() -> Optional[Path]: return None -def get_claude_skills_dir() -> Path: - """Get the Claude Code skills directory path.""" - return Path.home() / ".claude" / "skills" +def get_codex_skills_dir() -> Path: + """Get the Codex skills directory path.""" + return Path.home() / ".codex" / "skills" def get_skill_target_dir() -> Path: """Get the target directory for the unity-bridge skill.""" - return get_claude_skills_dir() / "unity-bridge" + return get_codex_skills_dir() / "unity-bridge" def install_skill(verbose: bool = False) -> int: """ - Install the Claude Code skill by creating a symlink (with copy fallback on Windows). + Install the Codex skill by creating a symlink (with copy fallback on Windows). Returns: Exit code (0 for success, 1 for error). @@ -633,7 +634,7 @@ def install_skill(verbose: bool = False) -> int: print("Error: Could not find skill files in package.", file=sys.stderr) print("This may indicate a corrupted installation.", file=sys.stderr) print( - "Try reinstalling: pip install --force-reinstall claude-unity-bridge", + "Try reinstalling: pip install --force-reinstall codex-unity-bridge", file=sys.stderr, ) return EXIT_ERROR @@ -648,7 +649,7 @@ def install_skill(verbose: bool = False) -> int: return EXIT_ERROR # Create skills directory if it doesn't exist - skills_dir = get_claude_skills_dir() + skills_dir = get_codex_skills_dir() try: skills_dir.mkdir(parents=True, exist_ok=True) except Exception as e: @@ -723,7 +724,7 @@ def install_skill(verbose: bool = False) -> int: print(f"✓ Skill installed (copy): {target_dir}") print() print("Note: Using directory copy instead of symlink.") - print("To update the skill, re-run: python -m claude_unity_bridge.cli install-skill") + print("To update the skill, re-run: python -m codex_unity_bridge.cli install-skill") if platform.system() == "Windows": print() print("To enable symlinks (optional), enable Developer Mode:") @@ -732,8 +733,8 @@ def install_skill(verbose: bool = False) -> int: print(f"✓ Skill installed (symlink): {target_dir} -> {source_dir}") print() - print("The Claude Code skill is now available.") - print("Restart Claude Code to load the skill, then ask Claude naturally:") + print("The Codex Code skill is now available.") + print("Restart Codex to load the skill, then ask Codex naturally:") print(' "Run the Unity tests"') print(' "Check for compilation errors"') print() @@ -743,7 +744,7 @@ def install_skill(verbose: bool = False) -> int: def uninstall_skill(verbose: bool = False) -> int: """ - Uninstall the Claude Code skill by removing the symlink or copied directory. + Uninstall the Codex skill by removing the symlink or copied directory. Returns: Exit code (0 for success, 1 for error). @@ -805,7 +806,7 @@ def update_package(verbose: bool = False) -> int: Returns: Exit code (0 for success, 1 for error). """ - print("Updating claude-unity-bridge...") + print("Updating codex-unity-bridge...") try: # Run pip install --upgrade @@ -816,7 +817,7 @@ def update_package(verbose: bool = False) -> int: "pip", "install", "--upgrade", - "claude-unity-bridge", + "codex-unity-bridge", ], capture_output=not verbose, text=True, @@ -844,7 +845,7 @@ def execute_health_check(timeout: int, verbose: bool) -> int: """Verify Unity Bridge is set up correctly.""" print("Checking Unity Bridge setup...") - # Check 1: Does .unity-bridge directory exist? + # Check 1: Does .codex-unity-bridge directory exist? if not UNITY_DIR.exists(): print("✗ Unity Bridge not detected") print(f" Directory not found: {UNITY_DIR}") @@ -870,7 +871,7 @@ def execute_health_check(timeout: int, verbose: bool) -> int: def main(): parser = argparse.ArgumentParser( - description="Execute Unity Editor commands via Claude Unity Bridge", + description="Execute Unity Editor commands via Codex Unity Bridge", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Unity Commands: @@ -885,8 +886,8 @@ def main(): health-check Verify Unity Bridge setup Skill Commands: - install-skill Install Claude Code skill - uninstall-skill Uninstall Claude Code skill + install-skill Install Codex skill + uninstall-skill Uninstall Codex skill update Update package and reinstall skill Examples: diff --git a/skill/src/codex_unity_bridge/skill/SKILL.md b/skill/src/codex_unity_bridge/skill/SKILL.md new file mode 100644 index 0000000..a79351c --- /dev/null +++ b/skill/src/codex_unity_bridge/skill/SKILL.md @@ -0,0 +1,94 @@ +--- +name: unity +description: Execute Unity Editor commands through Codex Unity Bridge. Requires the com.prodfact.codex-bridge Unity package in the current project and a running Unity Editor. +--- + +# Unity Bridge Skill + +Use this skill when Codex needs to interact with a live Unity Editor from the project workspace. + +## Overview + +Codex Unity Bridge uses a deterministic Python CLI instead of ad-hoc in-context file handling. The CLI owns: + +- UUID generation +- atomic command writes +- polling and timeout handling +- response parsing +- cleanup of stale files +- skill installation + +That keeps Unity interactions stable across sessions and avoids re-implementing the file protocol each time. + +## Requirements + +1. The Unity project has `com.prodfact.codex-bridge` installed +2. Unity Editor is open with that project loaded +3. The project root contains `.codex-unity-bridge/` after the package initializes +4. The CLI is installed: `codex-unity-bridge` + +## Runtime Files + +- Command file: `.codex-unity-bridge/command.json` +- Response files: `.codex-unity-bridge/response-{id}.json` + +This is intentionally separate from the original Claude bridge runtime directory so both bridges can coexist in the same Unity project. + +## Basic Usage + +Run the CLI directly: + +```bash +codex-unity-bridge [command] [options] +``` + +Common commands: + +```bash +codex-unity-bridge run-tests --mode EditMode +codex-unity-bridge compile +codex-unity-bridge refresh +codex-unity-bridge get-status +codex-unity-bridge get-console-logs --limit 20 --filter Error +codex-unity-bridge play +codex-unity-bridge pause +codex-unity-bridge step +codex-unity-bridge health-check +``` + +## Natural-Language Examples + +Typical prompts that map well to this skill: + +- `Run the Unity tests in EditMode` +- `Check whether Unity is compiling` +- `Show the last 10 Unity errors` +- `Refresh the Unity asset database` +- `Pause play mode and step one frame` + +## Guidance + +- Prefer the CLI over manual edits to bridge files +- Use `--timeout` for long-running test or compile operations +- Use `get-status` or `health-check` before assuming Unity is unavailable +- Use `get-console-logs --filter Error` before asking for a broad log dump + +## Troubleshooting + +If Codex cannot communicate with Unity: + +1. Verify the Unity Editor is open with the intended project +2. Check that `.codex-unity-bridge/` exists at the project root +3. Run `codex-unity-bridge health-check` +4. Inspect the Unity Console for `[CodexBridge]` messages + +If the CLI is not on `PATH`, run: + +```bash +python -m codex_unity_bridge.cli install-skill +``` + +## References + +- [Command Reference](references/COMMANDS.md) +- [Extending the Bridge](references/EXTENDING.md) diff --git a/skill/src/claude_unity_bridge/skill/references/COMMANDS.md b/skill/src/codex_unity_bridge/skill/references/COMMANDS.md similarity index 72% rename from skill/src/claude_unity_bridge/skill/references/COMMANDS.md rename to skill/src/codex_unity_bridge/skill/references/COMMANDS.md index a574cee..9566196 100644 --- a/skill/src/claude_unity_bridge/skill/references/COMMANDS.md +++ b/skill/src/codex_unity_bridge/skill/references/COMMANDS.md @@ -9,6 +9,9 @@ Complete specification for all Unity Bridge commands, including parameters, resp - [refresh](#refresh) - Refresh asset database - [get-status](#get-status) - Get editor status - [get-console-logs](#get-console-logs) - Retrieve console logs +- [play](#play) - Toggle Play Mode +- [pause](#pause) - Toggle pause in Play Mode +- [step](#step) - Step one frame in Play Mode --- @@ -19,7 +22,7 @@ Execute Unity tests in EditMode or PlayMode. ### Usage ```bash -unity-bridge run-tests [options] +codex-unity-bridge run-tests [options] ``` ### Parameters @@ -41,7 +44,7 @@ unity-bridge run-tests [options] - Filter by test name or namespace - Examples: - `"MyTests"` - All tests containing "MyTests" - - `"MXR.Tests.Auth"` - All tests in the Auth namespace + - `"Example.Tests.Auth"` - All tests in the Auth namespace - `"LoginTest;LogoutTest"` - Multiple filters (semicolon-separated) - Case-sensitive - Matches test names using Unity's test filter syntax @@ -77,11 +80,11 @@ unity-bridge run-tests [options] "skipped": 0, "failures": [ { - "name": "MXR.Tests.AuthTests.LoginWithInvalidCredentials", + "name": "Example.Tests.AuthTests.LoginWithInvalidCredentials", "message": "Expected: success\nActual: failure\nat Assets/Tests/AuthTests.cs:45" }, { - "name": "MXR.Tests.NetworkTests.TimeoutHandling", + "name": "Example.Tests.NetworkTests.TimeoutHandling", "message": "NullReferenceException: Object reference not set\nat Assets/Tests/NetworkTests.cs:123" } ] @@ -101,7 +104,7 @@ During execution, you may see intermediate `status: "running"` responses with pr "progress": { "current": 150, "total": 410, - "currentTest": "MXR.Tests.Player.MovementTest" + "currentTest": "Example.Tests.Player.MovementTest" }, "failures": [] } @@ -118,11 +121,11 @@ The CLI formats the output for readability: Duration: 3.50s Failed Tests: - - MXR.Tests.AuthTests.LoginWithInvalidCredentials + - Example.Tests.AuthTests.LoginWithInvalidCredentials Expected: success Actual: failure at Assets/Tests/AuthTests.cs:45 - - MXR.Tests.NetworkTests.TimeoutHandling + - Example.Tests.NetworkTests.TimeoutHandling NullReferenceException: Object reference not set at Assets/Tests/NetworkTests.cs:123 ``` @@ -177,19 +180,19 @@ Failed Tests: ```bash # Run all EditMode tests (fast) -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode # Run specific test suite -unity-bridge run-tests --filter "MXR.Tests.Auth" +codex-unity-bridge run-tests --filter "Example.Tests.Auth" # Run multiple test suites -unity-bridge run-tests --filter "AuthTests;NetworkTests" +codex-unity-bridge run-tests --filter "AuthTests;NetworkTests" # Run PlayMode tests with extended timeout -unity-bridge run-tests --mode PlayMode --timeout 60 +codex-unity-bridge run-tests --mode PlayMode --timeout 60 # Run all tests -unity-bridge run-tests +codex-unity-bridge run-tests ``` --- @@ -201,7 +204,7 @@ Trigger Unity script compilation and wait for completion. ### Usage ```bash -unity-bridge compile [options] +codex-unity-bridge compile [options] ``` ### Parameters @@ -288,13 +291,13 @@ The command will wait for compilation to complete. ```bash # Basic compilation -unity-bridge compile +codex-unity-bridge compile # With extended timeout for large projects -unity-bridge compile --timeout 60 +codex-unity-bridge compile --timeout 60 # Cleanup old responses first -unity-bridge compile --cleanup +codex-unity-bridge compile --cleanup ``` --- @@ -306,7 +309,7 @@ Force Unity to refresh the asset database, reimporting changed assets. ### Usage ```bash -unity-bridge refresh [options] +codex-unity-bridge refresh [options] ``` ### Parameters @@ -388,13 +391,13 @@ Unity will refresh but may report import errors in the console. The command will ```bash # Basic refresh -unity-bridge refresh +codex-unity-bridge refresh # After git operations (pulling changes) -git pull && unity-bridge refresh +git pull && codex-unity-bridge refresh # With extended timeout for large projects -unity-bridge refresh --timeout 60 +codex-unity-bridge refresh --timeout 60 ``` --- @@ -406,7 +409,7 @@ Get current Unity Editor state, including compilation status, play mode, and upd ### Usage ```bash -unity-bridge get-status +codex-unity-bridge get-status ``` ### Parameters @@ -496,7 +499,7 @@ Check status before: ```bash # Check current status -unity-bridge get-status +codex-unity-bridge get-status # Wait for compilation to finish (pseudo-code workflow) while status.isCompiling: @@ -514,7 +517,7 @@ Retrieve Unity console logs with filtering options. ### Usage ```bash -unity-bridge get-console-logs [options] +codex-unity-bridge get-console-logs [options] ``` ### Parameters @@ -638,20 +641,275 @@ No console logs found ```bash # Get last 20 logs -unity-bridge get-console-logs --limit 20 +codex-unity-bridge get-console-logs --limit 20 # Get only errors -unity-bridge get-console-logs --filter Error +codex-unity-bridge get-console-logs --filter Error # Get only warnings -unity-bridge get-console-logs --filter Warning +codex-unity-bridge get-console-logs --filter Warning # Get last 5 logs of all types -unity-bridge get-console-logs --limit 5 +codex-unity-bridge get-console-logs --limit 5 # Check for errors after compilation -unity-bridge compile -unity-bridge get-console-logs --filter Error --limit 10 +codex-unity-bridge compile +codex-unity-bridge get-console-logs --filter Error --limit 10 +``` + +--- + +## play + +Toggle Unity Editor Play Mode. If not playing, enters Play Mode; if playing, exits Play Mode. + +### Usage + +```bash +codex-unity-bridge play [options] +``` + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `--timeout` | int | No | 30 | Command timeout in seconds | + +### Response Format + +**Success:** +```json +{ + "id": "uuid", + "status": "success", + "action": "play", + "duration_ms": 10, + "editorStatus": { + "isCompiling": false, + "isUpdating": false, + "isPlaying": true, + "isPaused": false + } +} +``` + +### Formatted Output + +**Entering Play Mode:** +``` +✓ play completed +Play Mode: ▶ Playing +Duration: 0.01s +``` + +**Exiting Play Mode:** +``` +✓ play completed +Play Mode: ⏹ Stopped +Duration: 0.01s +``` + +### Error Scenarios + +**Blocked during compilation:** +```json +{ + "id": "uuid", + "status": "error", + "action": "play", + "error": "Unity Editor is currently compiling. Only read-only commands (get-status, get-console-logs) are available. Try again later." +} +``` + +### Notes + +- **Toggle behavior:** Acts like the Play button in Unity — toggles between playing and editing +- **Blocked during compilation:** Cannot enter/exit Play Mode while scripts are compiling +- **Response includes editorStatus:** Always check the returned `editorStatus` to confirm the resulting state +- **Domain reload:** Entering/exiting Play Mode may trigger a domain reload, which takes time + +### Examples + +```bash +# Enter Play Mode +codex-unity-bridge play + +# Check state after toggling +codex-unity-bridge get-status + +# Exit Play Mode (call again) +codex-unity-bridge play +``` + +--- + +## pause + +Toggle the pause state while in Play Mode. If playing, pauses; if paused, unpauses. + +### Usage + +```bash +codex-unity-bridge pause [options] +``` + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `--timeout` | int | No | 30 | Command timeout in seconds | + +### Response Format + +**Success:** +```json +{ + "id": "uuid", + "status": "success", + "action": "pause", + "duration_ms": 5, + "editorStatus": { + "isCompiling": false, + "isUpdating": false, + "isPlaying": true, + "isPaused": true + } +} +``` + +### Formatted Output + +**Pausing:** +``` +✓ pause completed +Play Mode: ⏸ Paused +Duration: 0.01s +``` + +**Unpausing:** +``` +✓ pause completed +Play Mode: ▶ Playing +Duration: 0.01s +``` + +### Error Scenarios + +**Not in Play Mode:** +```json +{ + "id": "uuid", + "status": "error", + "action": "pause", + "error": "Cannot pause: Unity Editor is not in Play Mode. Use 'play' to enter Play Mode first." +} +``` + +Formatted as: +``` +✗ Error: Cannot pause: Unity Editor is not in Play Mode. Use 'play' to enter Play Mode first. +``` + +### Notes + +- **Requires Play Mode:** Returns error if not currently in Play Mode +- **Toggle behavior:** Like the Pause button in Unity +- **Inspection:** While paused, you can inspect GameObjects and variables in the editor + +### Examples + +```bash +# Enter Play Mode, then pause +codex-unity-bridge play +codex-unity-bridge pause + +# Unpause +codex-unity-bridge pause + +# Check current state +codex-unity-bridge get-status +``` + +--- + +## step + +Step one frame forward in Play Mode. If not paused, Unity will pause first then step. + +### Usage + +```bash +codex-unity-bridge step [options] +``` + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `--timeout` | int | No | 30 | Command timeout in seconds | + +### Response Format + +**Success:** +```json +{ + "id": "uuid", + "status": "success", + "action": "step", + "duration_ms": 20, + "editorStatus": { + "isCompiling": false, + "isUpdating": false, + "isPlaying": true, + "isPaused": true + } +} +``` + +### Formatted Output + +``` +✓ step completed +Play Mode: ⏸ Paused +Duration: 0.02s +``` + +### Error Scenarios + +**Not in Play Mode:** +```json +{ + "id": "uuid", + "status": "error", + "action": "step", + "error": "Cannot step: Unity Editor is not in Play Mode. Use 'play' to enter Play Mode first." +} +``` + +Formatted as: +``` +✗ Error: Cannot step: Unity Editor is not in Play Mode. Use 'play' to enter Play Mode first. +``` + +### Notes + +- **Requires Play Mode:** Returns error if not currently in Play Mode +- **Auto-pause:** If Unity is playing (not paused), stepping will pause first then advance one frame +- **Frame-by-frame debugging:** Useful for inspecting state changes one frame at a time +- **Stays paused:** After stepping, the editor remains paused + +### Examples + +```bash +# Enter Play Mode, pause, then step through frames +codex-unity-bridge play +codex-unity-bridge pause +codex-unity-bridge step +codex-unity-bridge step +codex-unity-bridge step + +# Check state between steps +codex-unity-bridge get-status ``` --- @@ -662,11 +920,11 @@ unity-bridge get-console-logs --filter Error --limit 10 ```bash # Check if compiling -status=$(unity-bridge get-status) +status=$(codex-unity-bridge get-status) # If ready, run tests if [[ $status == *"✓ Ready"* ]]; then - unity-bridge run-tests + codex-unity-bridge run-tests else echo "Waiting for compilation..." fi @@ -676,11 +934,11 @@ fi ```bash # Run tests -unity-bridge run-tests +codex-unity-bridge run-tests # If failed (exit code 1), get error logs if [ $? -ne 0 ]; then - unity-bridge get-console-logs --filter Error --limit 10 + codex-unity-bridge get-console-logs --filter Error --limit 10 fi ``` @@ -688,16 +946,16 @@ fi ```bash # 1. Check status -unity-bridge get-status +codex-unity-bridge get-status # 2. Compile -unity-bridge compile +codex-unity-bridge compile # 3. Run tests -unity-bridge run-tests +codex-unity-bridge run-tests # 4. Check for errors -unity-bridge get-console-logs --filter Error +codex-unity-bridge get-console-logs --filter Error ``` ### After Git Pull Workflow @@ -707,13 +965,13 @@ unity-bridge get-console-logs --filter Error git pull # Refresh assets -unity-bridge refresh +codex-unity-bridge refresh # Wait for compilation # (Unity will auto-compile after refresh) # Run tests -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode ``` --- @@ -734,7 +992,7 @@ All commands return standard exit codes for shell integration: #!/bin/bash # Run tests and check exit code -unity-bridge run-tests --mode EditMode +codex-unity-bridge run-tests --mode EditMode case $? in 0) @@ -807,7 +1065,7 @@ If commands frequently timeout: **"Unity Editor not detected"** - Unity is not running - Unity project is not open -- `.unity-bridge/` directory doesn't exist (package not installed) +- `.codex-unity-bridge/` directory doesn't exist (package not installed) **Solution:** Open Unity with your project and ensure package is installed. @@ -849,7 +1107,7 @@ If commands frequently timeout: ### Linux - File paths use forward slashes - Case-sensitive file systems -- May need to adjust file permissions on `.unity-bridge/` directory +- May need to adjust file permissions on `.codex-unity-bridge/` directory --- diff --git a/skill/src/claude_unity_bridge/skill/references/EXTENDING.md b/skill/src/codex_unity_bridge/skill/references/EXTENDING.md similarity index 91% rename from skill/src/claude_unity_bridge/skill/references/EXTENDING.md rename to skill/src/codex_unity_bridge/skill/references/EXTENDING.md index 788731c..d683060 100644 --- a/skill/src/claude_unity_bridge/skill/references/EXTENDING.md +++ b/skill/src/codex_unity_bridge/skill/references/EXTENDING.md @@ -1,4 +1,4 @@ -# Extending Claude Unity Bridge +# Extending Codex Unity Bridge Learn how to add custom commands to the Unity Bridge for project-specific workflows. @@ -29,7 +29,7 @@ The Unity Bridge ships with 5 core commands that work for any Unity project. How - **Project-Specific Tools:** Run custom editor tools or validators - **CI/CD Integration:** Automate project-specific build/test workflows -By extending the bridge with custom commands, you can automate these workflows through Claude Code. +By extending the bridge with custom commands, you can automate these workflows through Codex. --- @@ -38,23 +38,23 @@ By extending the bridge with custom commands, you can automate these workflows t The Unity Bridge uses a **Command Pattern** for extensibility: ``` -Claude Code (Python Script) +Codex (Python Script) ↓ writes command.json ↓ polls (Unity EditorApplication.update) -ClaudeBridge.cs (Command Dispatcher) +CodexBridge.cs (Command Dispatcher) ↓ routes to ICommand Implementation (YourCustomCommand.cs) ↓ writes response-{id}.json ↓ reads -Claude Code (Python Script) +Codex (Python Script) ``` **Key Components:** 1. **ICommand Interface** - All commands implement this interface -2. **ClaudeBridge.cs** - Dispatches commands to registered handlers +2. **CodexBridge.cs** - Dispatches commands to registered handlers 3. **CommandRequest** - Input data structure 4. **CommandResponse** - Output data structure 5. **Python Script** - Handles command execution (no changes needed!) @@ -70,11 +70,11 @@ Create `Editor/Commands/YourCommand.cs`: ```csharp using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class YourCommand : ICommand { public void Execute( CommandRequest request, @@ -83,7 +83,7 @@ namespace MXR.ClaudeBridge.Commands { ) { var stopwatch = Stopwatch.StartNew(); - Debug.Log("[ClaudeBridge] Executing your custom command"); + Debug.Log("[CodexBridge] Executing your custom command"); // Report progress (optional) var progressResponse = CommandResponse.Running(request.id, request.action); @@ -109,7 +109,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Command failed: {e.Message}"); + Debug.LogError($"[CodexBridge] Command failed: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -126,7 +126,7 @@ namespace MXR.ClaudeBridge.Commands { ### Step 2: Register Command -In `Editor/ClaudeBridge.cs`, add your command to the dictionary: +In `Editor/CodexBridge.cs`, add your command to the dictionary: ```csharp Commands = new Dictionary { @@ -145,10 +145,10 @@ The CLI automatically works with your custom command: ```bash # The script handles all the file I/O, polling, and formatting -unity-bridge your-command +codex-unity-bridge your-command ``` -That's it! Your command is now available through Claude Code. +That's it! Your command is now available through Codex. --- @@ -338,13 +338,13 @@ Create a command to build your Unity project for a specific platform: using System; using System.Diagnostics; using System.IO; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEditor.Build.Reporting; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class BuildCommand : ICommand { public void Execute( CommandRequest request, @@ -357,7 +357,7 @@ namespace MXR.ClaudeBridge.Commands { var platformString = request.@params?.targetPlatform ?? "StandaloneWindows64"; var isDevelopment = request.@params?.developmentBuild ?? false; - Debug.Log($"[ClaudeBridge] Building for {platformString} (development: {isDevelopment})"); + Debug.Log($"[CodexBridge] Building for {platformString} (development: {isDevelopment})"); // Report progress var progressResponse = CommandResponse.Running(request.id, request.action); @@ -384,7 +384,7 @@ namespace MXR.ClaudeBridge.Commands { // Check result if (report.summary.result == BuildResult.Succeeded) { - Debug.Log($"[ClaudeBridge] Build succeeded: {report.summary.totalSize} bytes"); + Debug.Log($"[CodexBridge] Build succeeded: {report.summary.totalSize} bytes"); var response = CommandResponse.Success( request.id, @@ -403,7 +403,7 @@ namespace MXR.ClaudeBridge.Commands { onComplete?.Invoke(response); } else { - Debug.LogError($"[ClaudeBridge] Build failed: {report.summary.result}"); + Debug.LogError($"[CodexBridge] Build failed: {report.summary.result}"); var response = CommandResponse.Failure( request.id, @@ -417,7 +417,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Build error: {e.Message}"); + Debug.LogError($"[CodexBridge] Build error: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -445,7 +445,7 @@ namespace MXR.ClaudeBridge.Commands { } ``` -**Register in ClaudeBridge.cs:** +**Register in CodexBridge.cs:** ```csharp Commands = new Dictionary { @@ -459,7 +459,7 @@ Commands = new Dictionary { ```bash # You'll need to extend the CLI to support --target-platform and --development-build # Or create a project-specific wrapper script -unity-bridge build +codex-unity-bridge build ``` --- @@ -471,13 +471,13 @@ Load or validate specific scenes: ```csharp using System; using System.Diagnostics; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class LoadSceneCommand : ICommand { public void Execute( CommandRequest request, @@ -496,14 +496,14 @@ namespace MXR.ClaudeBridge.Commands { return; } - Debug.Log($"[ClaudeBridge] Loading scene: {scenePath}"); + Debug.Log($"[CodexBridge] Loading scene: {scenePath}"); try { // Load scene var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); stopwatch.Stop(); - Debug.Log($"[ClaudeBridge] Scene loaded: {scene.name}"); + Debug.Log($"[CodexBridge] Scene loaded: {scene.name}"); var response = CommandResponse.Success( request.id, @@ -523,7 +523,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Failed to load scene: {e.Message}"); + Debug.LogError($"[CodexBridge] Failed to load scene: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -545,12 +545,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using MXR.ClaudeBridge.Models; +using ProdFact.CodexBridge.Models; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; -namespace MXR.ClaudeBridge.Commands { +namespace ProdFact.CodexBridge.Commands { public class ValidateAssetsCommand : ICommand { public void Execute( CommandRequest request, @@ -559,7 +559,7 @@ namespace MXR.ClaudeBridge.Commands { ) { var stopwatch = Stopwatch.StartNew(); - Debug.Log("[ClaudeBridge] Validating assets"); + Debug.Log("[CodexBridge] Validating assets"); var progressResponse = CommandResponse.Running(request.id, request.action); onProgress?.Invoke(progressResponse); @@ -603,7 +603,7 @@ namespace MXR.ClaudeBridge.Commands { stopwatch.Stop(); if (issues.Count == 0) { - Debug.Log("[ClaudeBridge] Asset validation passed"); + Debug.Log("[CodexBridge] Asset validation passed"); var response = CommandResponse.Success( request.id, @@ -614,7 +614,7 @@ namespace MXR.ClaudeBridge.Commands { onComplete?.Invoke(response); } else { - Debug.LogWarning($"[ClaudeBridge] Asset validation found {issues.Count} issues"); + Debug.LogWarning($"[CodexBridge] Asset validation found {issues.Count} issues"); var response = CommandResponse.Failure( request.id, @@ -637,7 +637,7 @@ namespace MXR.ClaudeBridge.Commands { } catch (Exception e) { stopwatch.Stop(); - Debug.LogError($"[ClaudeBridge] Asset validation error: {e.Message}"); + Debug.LogError($"[CodexBridge] Asset validation error: {e.Message}"); onComplete?.Invoke( CommandResponse.Error(request.id, request.action, e.Message) @@ -686,7 +686,7 @@ public class CIBuildCommand : ICommand { unity -batchmode -projectPath . -executeMethod YourCICommand.Execute # Or use the bridge directly -unity-bridge ci-build +codex-unity-bridge ci-build ``` ### Pre-Commit Hook @@ -698,8 +698,8 @@ Validate project before committing: # .git/hooks/pre-commit # Run Unity validation -unity-bridge validate-assets -unity-bridge run-tests --mode EditMode +codex-unity-bridge validate-assets +codex-unity-bridge run-tests --mode EditMode if [ $? -ne 0 ]; then echo "Tests failed! Fix issues before committing." @@ -746,14 +746,14 @@ try { onComplete?.Invoke(CommandResponse.Success(...)); } catch (Exception e) { - Debug.LogError($"[ClaudeBridge] Error: {e.Message}"); + Debug.LogError($"[CodexBridge] Error: {e.Message}"); onComplete?.Invoke(CommandResponse.Error(request.id, request.action, e.Message)); } ``` ### 2. Report Progress for Long Operations -Keep Claude informed during long-running operations: +Keep Codex informed during long-running operations: ```csharp for (int i = 0; i < totalSteps; i++) { @@ -790,9 +790,9 @@ var response = CommandResponse.Success( Help users debug by logging command execution: ```csharp -Debug.Log($"[ClaudeBridge] Starting {request.action}"); +Debug.Log($"[CodexBridge] Starting {request.action}"); // ... operation -Debug.Log($"[ClaudeBridge] Completed {request.action}"); +Debug.Log($"[CodexBridge] Completed {request.action}"); ``` ### 5. Validate Parameters @@ -851,7 +851,7 @@ if (EditorApplication.isPlaying) { 1. **Write Command File:** ```bash -cat > .unity-bridge/command.json << EOF +cat > .codex-unity-bridge/command.json << EOF { "id": "test-123", "action": "your-command", @@ -869,7 +869,7 @@ EOF 3. **Read Response:** ```bash -cat .unity-bridge/response-test-123.json +cat .codex-unity-bridge/response-test-123.json ``` ### Via Python Script @@ -877,7 +877,7 @@ cat .unity-bridge/response-test-123.json Once registered, test via the CLI: ```bash -unity-bridge your-command --verbose +codex-unity-bridge your-command --verbose ``` The `--verbose` flag shows detailed execution progress. @@ -947,7 +947,7 @@ elif args.command == "load-scene": **Symptom:** "Unknown action: your-command" error **Solution:** -1. Check that command is registered in `ClaudeBridge.cs` +1. Check that command is registered in `CodexBridge.cs` 2. Verify command name matches exactly (case-sensitive) 3. Ensure Unity reloaded scripts after adding command @@ -987,7 +987,7 @@ elif args.command == "load-scene": Extending the Unity Bridge is straightforward: 1. **Create command class** implementing `ICommand` -2. **Register command** in `ClaudeBridge.cs` +2. **Register command** in `CodexBridge.cs` 3. **Test with CLI** - it automatically handles file I/O The architecture is designed for extensibility while keeping the core simple and reliable. Your custom commands benefit from the same rock-solid file protocol, error handling, and progress reporting as the built-in commands. diff --git a/skill/tests/test_cli.py b/skill/tests/test_cli.py index 4d14c07..7c4ef0c 100644 --- a/skill/tests/test_cli.py +++ b/skill/tests/test_cli.py @@ -12,7 +12,7 @@ import pytest -from claude_unity_bridge.cli import ( +from codex_unity_bridge.cli import ( format_response, format_test_results, format_compile_results, @@ -34,7 +34,7 @@ update_package, get_skill_source_dir, get_skill_target_dir, - get_claude_skills_dir, + get_codex_skills_dir, _validate_command_id, main, UnityCommandError, @@ -370,7 +370,7 @@ class TestWriteCommand: def test_write_command_creates_file(self, tmp_path): # Patch UNITY_DIR to use temp directory - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = write_command("test-action", {"param": "value"}) # Check UUID format @@ -389,7 +389,7 @@ def test_write_command_creates_file(self, tmp_path): def test_write_command_creates_directory(self, tmp_path): unity_dir = tmp_path / "nested" / "unity" - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): write_command("test", {}) assert unity_dir.exists() @@ -398,7 +398,7 @@ class TestWaitForResponse: """Test response waiting and polling""" def test_wait_for_response_success(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" response_data = {"id": command_id, "status": "success", "action": "test"} @@ -411,7 +411,7 @@ def test_wait_for_response_success(self, tmp_path): assert result == response_data def test_wait_for_response_timeout(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Create directory to simulate Unity running tmp_path.mkdir(exist_ok=True) @@ -423,7 +423,7 @@ def test_wait_for_response_timeout(self, tmp_path): def test_wait_for_response_unity_not_running(self, tmp_path): # Don't create directory to simulate Unity not running nonexistent_dir = tmp_path / "does-not-exist" - with patch("claude_unity_bridge.cli.UNITY_DIR", nonexistent_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", nonexistent_dir): with pytest.raises(UnityNotRunningError) as exc_info: wait_for_response("c3d4e5f6-a7b8-9012-cdef-123456789012", timeout=1) @@ -434,7 +434,7 @@ class TestCleanupOldResponses: """Test cleanup functionality""" def test_cleanup_old_responses(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Create some response files old_file = tmp_path / "response-old-123.json" recent_file = tmp_path / "response-recent-456.json" @@ -459,7 +459,7 @@ def test_cleanup_old_responses(self, tmp_path): def test_cleanup_no_directory(self, tmp_path): # Should not raise error if directory doesn't exist nonexistent_dir = tmp_path / "does-not-exist" - with patch("claude_unity_bridge.cli.UNITY_DIR", nonexistent_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", nonexistent_dir): cleanup_old_responses() # Should not raise @@ -468,7 +468,7 @@ class TestIntegration: def test_full_command_cycle(self, tmp_path): """Test writing command, waiting for response, and formatting""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Write command command_id = write_command("get-status", {}) @@ -683,7 +683,7 @@ class TestCleanupResponseFile: """Test cleanup_response_file function""" def test_cleanup_existing_file(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "d4e5f6a7-b8c9-0123-defa-234567890123" response_file = tmp_path / f"response-{command_id}.json" response_file.write_text('{"id": "test"}') @@ -692,12 +692,12 @@ def test_cleanup_existing_file(self, tmp_path): assert not response_file.exists() def test_cleanup_nonexistent_file(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Should not raise error cleanup_response_file("e5f6a7b8-c9d0-1234-efab-345678901234") def test_cleanup_with_verbose(self, tmp_path, capsys): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "f6a7b8c9-d0e1-2345-fabc-456789012345" response_file = tmp_path / f"response-{command_id}.json" response_file.write_text('{"id": "test"}') @@ -712,7 +712,7 @@ class TestCleanupOldResponsesVerbose: """Test cleanup_old_responses verbose mode""" def test_cleanup_verbose_output(self, tmp_path, capsys): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): old_file = tmp_path / "response-old-verbose.json" old_file.write_text('{"id": "old"}') @@ -736,14 +736,14 @@ def test_write_command_mkdir_failure(self, tmp_path): blocking_file.write_text("blocking") unity_dir = blocking_file / "unity" - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): with pytest.raises(UnityCommandError) as exc_info: write_command("test", {}) assert "Failed to create Unity directory" in str(exc_info.value) def test_write_command_file_write_failure(self, tmp_path): # Make the directory read-only to cause write failure - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): tmp_path.mkdir(parents=True, exist_ok=True) # Mock Path.write_text to raise an exception with patch.object(Path, "write_text", side_effect=PermissionError("Permission denied")): @@ -756,7 +756,7 @@ class TestWaitForResponseEdgeCases: """Test edge cases in wait_for_response""" def test_wait_verbose_polling(self, tmp_path, capsys): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "a7b8c9d0-e1f2-3456-abcd-567890123456" response_data = {"id": command_id, "status": "success"} @@ -778,7 +778,7 @@ def create_response(): def test_wait_json_decode_error_recovery(self, tmp_path, capsys): """Test that mid-write JSON errors are retried once""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "b8c9d0e1-f2a3-4567-bcde-678901234567" response_file = tmp_path / f"response-{command_id}.json" @@ -800,7 +800,7 @@ def mock_read(self): def test_wait_json_decode_error_persistent(self, tmp_path, capsys): """Test that persistent JSON errors raise an exception""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "c9d0e1f2-a3b4-5678-cdef-789012345678" response_file = tmp_path / f"response-{command_id}.json" @@ -820,7 +820,7 @@ class TestWaitForRunningStatus: def test_polls_until_complete(self, tmp_path): """wait_for_response should keep polling when status is 'running'""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" response_file = tmp_path / f"response-{command_id}.json" @@ -858,7 +858,7 @@ def update_response(): def test_timeout_while_running(self, tmp_path): """wait_for_response should timeout even if status stays 'running'""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "b2c3d4e5-f6a7-8901-bcde-f12345678901" response_file = tmp_path / f"response-{command_id}.json" @@ -876,7 +876,7 @@ def test_timeout_while_running(self, tmp_path): def test_verbose_progress_output(self, tmp_path, capsys): """wait_for_response should print progress when verbose and status is 'running'""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "c3d4e5f6-a7b8-9012-cdef-123456789012" response_file = tmp_path / f"response-{command_id}.json" @@ -915,7 +915,7 @@ def update_response(): def test_verbose_no_progress_info(self, tmp_path, capsys): """Verbose output should say 'Command running...' when no progress info""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "d4e5f6a7-b8c9-0123-defa-234567890123" response_file = tmp_path / f"response-{command_id}.json" @@ -952,7 +952,7 @@ def update_response(): def test_returns_failure_not_running(self, tmp_path): """wait_for_response should return immediately for non-running statuses""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "e5f6a7b8-c9d0-1234-efab-345678901234" response_file = tmp_path / f"response-{command_id}.json" @@ -975,7 +975,7 @@ class TestExecuteCommand: """Test execute_command function""" def test_execute_command_success(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Write command file manually import uuid @@ -1009,13 +1009,13 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): result = execute_command("get-status", {}, timeout=5) assert "Unity Editor Status" in result def test_execute_command_always_cleans_up(self, tmp_path): """execute_command always runs cleanup, even without cleanup flag""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Create an old response file tmp_path.mkdir(parents=True, exist_ok=True) old_file = tmp_path / "response-old-exec.json" @@ -1043,7 +1043,7 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): # Note: cleanup flag NOT passed — cleanup should still run result = execute_command("compile", {}, timeout=5) assert "Compilation Successful" in result @@ -1051,7 +1051,7 @@ def mock_write(action, params): assert not old_file.exists() def test_execute_command_verbose(self, tmp_path, capsys): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import uuid command_id = str(uuid.uuid4()) @@ -1071,7 +1071,7 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): result = execute_command("refresh", {}, timeout=5, verbose=True) assert "Asset Database Refreshed" in result @@ -1087,7 +1087,7 @@ class TestHealthCheck: def test_health_check_no_directory(self, tmp_path, capsys): """Health check fails when Unity directory doesn't exist""" nonexistent_dir = tmp_path / "does-not-exist" - with patch("claude_unity_bridge.cli.UNITY_DIR", nonexistent_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", nonexistent_dir): result = execute_health_check(timeout=5, verbose=False) assert result == EXIT_ERROR @@ -1097,14 +1097,14 @@ def test_health_check_no_directory(self, tmp_path, capsys): def test_health_check_success(self, tmp_path, capsys): """Health check succeeds when Unity responds""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): tmp_path.mkdir(parents=True, exist_ok=True) # Mock execute_command to return success def mock_execute(action, params, timeout, verbose): return "Unity Editor Status:\n - Compilation: ✓ Ready" - with patch("claude_unity_bridge.cli.execute_command", side_effect=mock_execute): + with patch("codex_unity_bridge.cli.execute_command", side_effect=mock_execute): result = execute_health_check(timeout=5, verbose=False) assert result == EXIT_SUCCESS @@ -1114,12 +1114,12 @@ def mock_execute(action, params, timeout, verbose): def test_health_check_unity_not_responding(self, tmp_path, capsys): """Health check fails when Unity doesn't respond""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): tmp_path.mkdir(parents=True, exist_ok=True) # Mock execute_command to raise UnityNotRunningError with patch( - "claude_unity_bridge.cli.execute_command", + "codex_unity_bridge.cli.execute_command", side_effect=UnityNotRunningError("Unity not running"), ): result = execute_health_check(timeout=5, verbose=False) @@ -1130,12 +1130,12 @@ def test_health_check_unity_not_responding(self, tmp_path, capsys): def test_health_check_timeout(self, tmp_path, capsys): """Health check returns timeout when Unity times out""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): tmp_path.mkdir(parents=True, exist_ok=True) # Mock execute_command to raise CommandTimeoutError with patch( - "claude_unity_bridge.cli.execute_command", + "codex_unity_bridge.cli.execute_command", side_effect=CommandTimeoutError("Timeout"), ): result = execute_health_check(timeout=5, verbose=False) @@ -1149,14 +1149,14 @@ class TestMainFunction: """Test main() CLI function""" def test_main_help(self, capsys): - with patch("sys.argv", ["unity-bridge", "--help"]): + with patch("sys.argv", ["codex-unity-bridge", "--help"]): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 0 def test_main_run_tests(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - argv = ["unity-bridge", "run-tests", "--mode", "EditMode", "--timeout", "1"] + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + argv = ["codex-unity-bridge", "run-tests", "--mode", "EditMode", "--timeout", "1"] with patch("sys.argv", argv): # Create response immediately def mock_write(action, params): @@ -1180,14 +1180,14 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_get_console_logs(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "10", @@ -1215,28 +1215,28 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_health_check(self, tmp_path, capsys): """Test health-check via main()""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): tmp_path.mkdir(parents=True, exist_ok=True) - argv = ["unity-bridge", "health-check", "--timeout", "5"] + argv = ["codex-unity-bridge", "health-check", "--timeout", "5"] with patch("sys.argv", argv): def mock_execute(action, params, timeout, verbose): return "Unity Editor Status:\n - Compilation: ✓ Ready" - with patch("claude_unity_bridge.cli.execute_command", side_effect=mock_execute): + with patch("codex_unity_bridge.cli.execute_command", side_effect=mock_execute): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_play(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - argv = ["unity-bridge", "play", "--timeout", "1"] + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + argv = ["codex-unity-bridge", "play", "--timeout", "1"] with patch("sys.argv", argv): def mock_write(action, params): @@ -1260,13 +1260,13 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_pause(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - argv = ["unity-bridge", "pause", "--timeout", "1"] + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + argv = ["codex-unity-bridge", "pause", "--timeout", "1"] with patch("sys.argv", argv): def mock_write(action, params): @@ -1290,13 +1290,13 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_step(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - argv = ["unity-bridge", "step", "--timeout", "1"] + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + argv = ["codex-unity-bridge", "step", "--timeout", "1"] with patch("sys.argv", argv): def mock_write(action, params): @@ -1320,26 +1320,26 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS def test_main_timeout_error(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "1"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("sys.argv", ["codex-unity-bridge", "compile", "--timeout", "1"]): # Don't create response - will timeout exit_code = main() assert exit_code == EXIT_TIMEOUT def test_main_unity_not_running(self, tmp_path): nonexistent_dir = tmp_path / "nonexistent" - with patch("claude_unity_bridge.cli.UNITY_DIR", nonexistent_dir): - with patch("sys.argv", ["unity-bridge", "get-status", "--timeout", "1"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", nonexistent_dir): + with patch("sys.argv", ["codex-unity-bridge", "get-status", "--timeout", "1"]): # Mock write_command to return an ID without creating the directory # This simulates the case where the command file can't be written # because Unity never created the directory structure with patch( - "claude_unity_bridge.cli.write_command", + "codex_unity_bridge.cli.write_command", return_value="f2a3b4c5-d6e7-8901-fabc-012345678901", ): exit_code = main() @@ -1351,36 +1351,38 @@ def test_main_command_error(self, tmp_path): blocking_file.write_text("blocking") unity_dir = blocking_file / "unity" - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "1"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("sys.argv", ["codex-unity-bridge", "compile", "--timeout", "1"]): exit_code = main() assert exit_code == EXIT_ERROR def test_main_keyboard_interrupt(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "1"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("sys.argv", ["codex-unity-bridge", "compile", "--timeout", "1"]): with patch( - "claude_unity_bridge.cli.execute_command", + "codex_unity_bridge.cli.execute_command", side_effect=KeyboardInterrupt, ): exit_code = main() assert exit_code == EXIT_ERROR def test_main_unexpected_error(self, tmp_path): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "1"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("sys.argv", ["codex-unity-bridge", "compile", "--timeout", "1"]): with patch( - "claude_unity_bridge.cli.execute_command", + "codex_unity_bridge.cli.execute_command", side_effect=RuntimeError("Unexpected"), ): exit_code = main() assert exit_code == EXIT_ERROR def test_main_verbose_unexpected_error(self, tmp_path, capsys): - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "1", "--verbose"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch( + "sys.argv", ["codex-unity-bridge", "compile", "--timeout", "1", "--verbose"] + ): with patch( - "claude_unity_bridge.cli.execute_command", + "codex_unity_bridge.cli.execute_command", side_effect=RuntimeError("Unexpected"), ): exit_code = main() @@ -1394,8 +1396,8 @@ class TestArgumentValidation: def test_timeout_zero_rejected(self, tmp_path, capsys): """--timeout 0 should fail validation""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "get-status", "--timeout", "0"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("sys.argv", ["codex-unity-bridge", "get-status", "--timeout", "0"]): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 2 # argparse error exit code @@ -1405,8 +1407,8 @@ def test_timeout_zero_rejected(self, tmp_path, capsys): def test_timeout_negative_rejected(self, tmp_path, capsys): """--timeout -5 should fail validation""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): - with patch("sys.argv", ["unity-bridge", "compile", "--timeout", "-5"]): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("sys.argv", ["codex-unity-bridge", "compile", "--timeout", "-5"]): with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 2 @@ -1416,9 +1418,9 @@ def test_timeout_negative_rejected(self, tmp_path, capsys): def test_limit_zero_rejected(self, tmp_path, capsys): """--limit 0 should fail validation""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "0", @@ -1436,9 +1438,9 @@ def test_limit_zero_rejected(self, tmp_path, capsys): def test_limit_negative_rejected(self, tmp_path, capsys): """--limit -1 should fail validation""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "-1", @@ -1455,9 +1457,9 @@ def test_limit_negative_rejected(self, tmp_path, capsys): def test_limit_too_large_rejected(self, tmp_path, capsys): """--limit 1001 should fail validation (exceeds MAX_LIMIT)""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "1001", @@ -1475,10 +1477,10 @@ def test_limit_too_large_rejected(self, tmp_path, capsys): def test_limit_valid_boundary(self, tmp_path): """--limit 1 and --limit 1000 should be accepted""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): # Test lower boundary argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "1", @@ -1503,13 +1505,13 @@ def mock_write(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): exit_code = main() assert exit_code == EXIT_SUCCESS # Test upper boundary argv = [ - "unity-bridge", + "codex-unity-bridge", "get-console-logs", "--limit", "1000", @@ -1534,7 +1536,7 @@ def mock_write_1000(action, params): ) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write_1000): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write_1000): exit_code = main() assert exit_code == EXIT_SUCCESS @@ -1543,13 +1545,13 @@ class TestSecurityValidation: """Test security-related validations""" def test_symlink_detection(self, tmp_path): - """Symlinked .unity-bridge directory should raise security error""" + """Symlinked .codex-unity-bridge directory should raise security error""" # Create a target directory for the symlink target_dir = tmp_path / "real_dir" target_dir.mkdir() - # Try to create a symlink for the .unity-bridge directory - symlink_path = tmp_path / ".unity-bridge" + # Try to create a symlink for the .codex-unity-bridge directory + symlink_path = tmp_path / ".codex-unity-bridge" try: symlink_path.symlink_to(target_dir) except OSError: @@ -1557,7 +1559,7 @@ def test_symlink_detection(self, tmp_path): # Skip this test as it requires symlink support pytest.skip("Symlink creation not supported (requires Developer Mode on Windows)") - with patch("claude_unity_bridge.cli.UNITY_DIR", symlink_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", symlink_path): with pytest.raises(UnityCommandError) as exc_info: write_command("test", {}) @@ -1566,9 +1568,9 @@ def test_symlink_detection(self, tmp_path): def test_normal_directory_allowed(self, tmp_path): """Normal (non-symlink) directory should work fine""" - unity_dir = tmp_path / ".unity-bridge" + unity_dir = tmp_path / ".codex-unity-bridge" # Don't create it - write_command should create it - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): # Also patch cwd for gitignore check with patch("pathlib.Path.cwd", return_value=tmp_path): command_id = write_command("test-action", {"param": "value"}) @@ -1583,26 +1585,26 @@ class TestGitignoreNotification: """Test gitignore notification feature""" def test_no_notification_when_gitignore_contains_unity_bridge(self, tmp_path, capsys): - """No notification when .unity-bridge is already in .gitignore""" + """No notification when .codex-unity-bridge is already in .gitignore""" gitignore = tmp_path / ".gitignore" - gitignore.write_text(".unity-bridge/\n") + gitignore.write_text(".codex-unity-bridge/\n") with patch("pathlib.Path.cwd", return_value=tmp_path): check_gitignore_and_notify() captured = capsys.readouterr() - assert ".unity-bridge" not in captured.err + assert ".codex-unity-bridge" not in captured.err def test_no_notification_when_gitignore_contains_pattern(self, tmp_path, capsys): - """No notification when gitignore contains .unity-bridge pattern (without slash)""" + """No notification when gitignore contains .codex-unity-bridge pattern (without slash)""" gitignore = tmp_path / ".gitignore" - gitignore.write_text("*.log\n.unity-bridge\ntemp/\n") + gitignore.write_text("*.log\n.codex-unity-bridge\ntemp/\n") with patch("pathlib.Path.cwd", return_value=tmp_path): check_gitignore_and_notify() captured = capsys.readouterr() - assert ".unity-bridge" not in captured.err + assert ".codex-unity-bridge" not in captured.err def test_notification_when_gitignore_missing(self, tmp_path, capsys): """Notification when .gitignore doesn't exist""" @@ -1615,11 +1617,11 @@ def test_notification_when_gitignore_missing(self, tmp_path, capsys): check_gitignore_and_notify() captured = capsys.readouterr() - assert ".unity-bridge/" in captured.err + assert ".codex-unity-bridge/" in captured.err assert "gitignore" in captured.err.lower() def test_notification_when_gitignore_exists_without_unity_bridge(self, tmp_path, capsys): - """Notification when .gitignore exists but doesn't contain .unity-bridge""" + """Notification when .gitignore exists but doesn't contain .codex-unity-bridge""" gitignore = tmp_path / ".gitignore" gitignore.write_text("*.log\nnode_modules/\n") @@ -1627,35 +1629,35 @@ def test_notification_when_gitignore_exists_without_unity_bridge(self, tmp_path, check_gitignore_and_notify() captured = capsys.readouterr() - assert ".unity-bridge/" in captured.err + assert ".codex-unity-bridge/" in captured.err assert "gitignore" in captured.err.lower() def test_notification_on_first_directory_creation(self, tmp_path, capsys): """Notification is shown when directory is first created""" - unity_dir = tmp_path / ".unity-bridge" + unity_dir = tmp_path / ".codex-unity-bridge" # Ensure no gitignore to trigger notification gitignore = tmp_path / ".gitignore" if gitignore.exists(): gitignore.unlink() - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): with patch("pathlib.Path.cwd", return_value=tmp_path): write_command("test", {}) captured = capsys.readouterr() - assert ".unity-bridge/" in captured.err + assert ".codex-unity-bridge/" in captured.err def test_no_notification_on_subsequent_command(self, tmp_path, capsys): """No notification when directory already exists""" - unity_dir = tmp_path / ".unity-bridge" + unity_dir = tmp_path / ".codex-unity-bridge" unity_dir.mkdir() # Pre-create directory - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): with patch("pathlib.Path.cwd", return_value=tmp_path): write_command("test", {}) captured = capsys.readouterr() - assert ".unity-bridge/" not in captured.err + assert ".codex-unity-bridge/" not in captured.err class TestSkillManagement: @@ -1668,15 +1670,15 @@ def test_get_skill_source_dir_exists(self): assert source_dir.exists() assert (source_dir / "SKILL.md").exists() - def test_get_claude_skills_dir(self): - """get_claude_skills_dir should return ~/.claude/skills""" - skills_dir = get_claude_skills_dir() - assert skills_dir == Path.home() / ".claude" / "skills" + def test_get_codex_skills_dir(self): + """get_codex_skills_dir should return ~/.codex/skills""" + skills_dir = get_codex_skills_dir() + assert skills_dir == Path.home() / ".codex" / "skills" def test_get_skill_target_dir(self): - """get_skill_target_dir should return ~/.claude/skills/unity-bridge""" + """get_skill_target_dir should return ~/.codex/skills/unity-bridge""" target_dir = get_skill_target_dir() - assert target_dir == Path.home() / ".claude" / "skills" / "unity-bridge" + assert target_dir == Path.home() / ".codex" / "skills" / "unity-bridge" def test_install_skill_creates_symlink(self, tmp_path, capsys): """install_skill should create a symlink or copy to the skill directory""" @@ -1684,15 +1686,15 @@ def test_install_skill_creates_symlink(self, tmp_path, capsys): with patch.object(Path, "home", return_value=tmp_path / "home"): # Create mock home directory structure - (tmp_path / "home" / ".claude").mkdir(parents=True) + (tmp_path / "home" / ".codex").mkdir(parents=True) - # Patch get_claude_skills_dir to use our temp dir + # Patch get_codex_skills_dir to use our temp dir with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skills_dir / "unity-bridge", ): result = install_skill(verbose=False) @@ -1705,6 +1707,7 @@ def test_install_skill_creates_symlink(self, tmp_path, capsys): captured = capsys.readouterr() assert "Skill installed" in captured.out + assert "Codex Code" in captured.out def test_install_skill_replaces_existing_symlink(self, tmp_path, capsys): """install_skill should replace an existing symlink or directory""" @@ -1729,11 +1732,11 @@ def test_install_skill_replaces_existing_symlink(self, tmp_path, capsys): created_symlink = False with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_path, ): result = install_skill(verbose=True) @@ -1761,11 +1764,11 @@ def test_install_skill_removes_existing_directory(self, tmp_path, capsys): (skill_dir / "some_file.txt").write_text("test") with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skill_dir, ): result = install_skill(verbose=True) @@ -1782,7 +1785,7 @@ def test_install_skill_removes_existing_directory(self, tmp_path, capsys): def test_install_skill_fails_when_source_missing(self, tmp_path, capsys): """install_skill should fail when skill source directory is missing""" with patch( - "claude_unity_bridge.cli.get_skill_source_dir", + "codex_unity_bridge.cli.get_skill_source_dir", return_value=None, ): result = install_skill(verbose=False) @@ -1814,7 +1817,7 @@ def test_uninstall_skill_removes_symlink(self, tmp_path, capsys): is_symlink = False with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=install_path, ): result = uninstall_skill(verbose=False) @@ -1836,7 +1839,7 @@ def test_uninstall_skill_idempotent(self, tmp_path, capsys): symlink = skills_dir / "unity-bridge" with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=symlink, ): result = uninstall_skill(verbose=False) @@ -1855,7 +1858,7 @@ def test_uninstall_skill_removes_skill_directory(self, tmp_path, capsys): (skill_dir / "SKILL.md").write_text("# Skill") with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skill_dir, ): result = uninstall_skill(verbose=False) @@ -1870,13 +1873,13 @@ def test_main_install_skill(self, tmp_path, capsys): """Test install-skill command via main()""" skills_dir = tmp_path / "skills" - with patch("sys.argv", ["unity-bridge", "install-skill"]): + with patch("sys.argv", ["codex-unity-bridge", "install-skill"]): with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skills_dir / "unity-bridge", ): exit_code = main() @@ -1902,9 +1905,9 @@ def test_main_uninstall_skill(self, tmp_path, capsys): shutil.copytree(target, install_path) - with patch("sys.argv", ["unity-bridge", "uninstall-skill"]): + with patch("sys.argv", ["codex-unity-bridge", "uninstall-skill"]): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=install_path, ): exit_code = main() @@ -1919,11 +1922,11 @@ def test_install_skill_removes_regular_file(self, tmp_path, capsys): target_file.write_text("not a symlink or directory") with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_file, ): result = install_skill(verbose=True) @@ -1943,18 +1946,19 @@ def test_update_package_success(self, tmp_path, capsys): mock_result = type("Result", (), {"returncode": 0, "stderr": ""})() - with patch("subprocess.run", return_value=mock_result): + with patch("subprocess.run", return_value=mock_result) as mock_run: with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skills_dir / "unity-bridge", ): result = update_package(verbose=False) assert result == EXIT_SUCCESS + assert mock_run.call_args.args[0][-1] == "codex-unity-bridge" captured = capsys.readouterr() assert "Updating" in captured.out @@ -1986,14 +1990,14 @@ def test_main_update(self, tmp_path, capsys): skills_dir = tmp_path / "skills" mock_result = type("Result", (), {"returncode": 0, "stderr": ""})() - with patch("sys.argv", ["unity-bridge", "update"]): + with patch("sys.argv", ["codex-unity-bridge", "update"]): with patch("subprocess.run", return_value=mock_result): with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=skills_dir / "unity-bridge", ): exit_code = main() @@ -2011,11 +2015,11 @@ def mock_symlink_to(self, target): with patch.object(Path, "symlink_to", mock_symlink_to): with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_dir, ): result = install_skill(verbose=True) @@ -2044,11 +2048,11 @@ def mock_symlink_to(self, target): with patch.object(Path, "symlink_to", mock_symlink_to): with patch("shutil.copytree", side_effect=PermissionError("Permission denied")): with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_dir, ): result = install_skill(verbose=False) @@ -2069,11 +2073,11 @@ def test_install_skill_replaces_existing_directory(self, tmp_path, capsys): (target_dir / "old_file.txt").write_text("old") with patch( - "claude_unity_bridge.cli.get_claude_skills_dir", + "codex_unity_bridge.cli.get_codex_skills_dir", return_value=skills_dir, ): with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_dir, ): result = install_skill(verbose=True) @@ -2100,7 +2104,7 @@ def test_uninstall_skill_removes_copied_directory(self, tmp_path, capsys): (target_dir / "scripts").mkdir() with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_dir, ): result = uninstall_skill(verbose=False) @@ -2122,7 +2126,7 @@ def test_uninstall_skill_warns_on_non_skill_directory(self, tmp_path, capsys): (target_dir / "random_file.txt").write_text("not a skill") with patch( - "claude_unity_bridge.cli.get_skill_target_dir", + "codex_unity_bridge.cli.get_skill_target_dir", return_value=target_dir, ): result = uninstall_skill(verbose=False) @@ -2162,19 +2166,19 @@ def test_uuid_with_extra_chars_rejected(self): def test_wait_for_response_validates_id(self, tmp_path): """wait_for_response should reject invalid command IDs""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): with pytest.raises(UnityCommandError, match="Invalid command ID format"): wait_for_response("../../etc/passwd", timeout=1) def test_cleanup_response_file_validates_id(self, tmp_path): """cleanup_response_file should reject invalid command IDs""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): with pytest.raises(UnityCommandError, match="Invalid command ID format"): cleanup_response_file("../../etc/passwd") def test_response_id_mismatch_rejected(self, tmp_path): """Response with mismatched ID should raise UnityCommandError""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" response_file = tmp_path / f"response-{command_id}.json" response_file.write_text(json.dumps({"id": "different-id", "status": "success"})) @@ -2185,14 +2189,14 @@ def test_response_id_mismatch_rejected(self, tmp_path): @pytest.mark.skipif(sys.platform == "win32", reason="POSIX permissions not supported on Windows") class TestDirectoryPermissions: - """Test that .unity-bridge/ directory and files get restrictive permissions""" + """Test that .codex-unity-bridge/ directory and files get restrictive permissions""" def test_directory_created_with_0700_permissions(self, tmp_path): import os import stat - unity_dir = tmp_path / ".unity-bridge" - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + unity_dir = tmp_path / ".codex-unity-bridge" + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): write_command("test-action", {"param": "value"}) mode = os.stat(unity_dir).st_mode dir_perms = stat.S_IMODE(mode) @@ -2202,8 +2206,8 @@ def test_command_file_has_0600_permissions(self, tmp_path): import os import stat - unity_dir = tmp_path / ".unity-bridge" - with patch("claude_unity_bridge.cli.UNITY_DIR", unity_dir): + unity_dir = tmp_path / ".codex-unity-bridge" + with patch("codex_unity_bridge.cli.UNITY_DIR", unity_dir): write_command("test-action", {"param": "value"}) command_file = unity_dir / "command.json" mode = os.stat(command_file).st_mode @@ -2216,7 +2220,7 @@ class TestCleanupStaleCommandFile: def test_removes_stale_command_file(self, tmp_path): """Stale command.json older than timeout is removed""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import os command_file = tmp_path / "command.json" @@ -2231,7 +2235,7 @@ def test_removes_stale_command_file(self, tmp_path): def test_keeps_fresh_command_file(self, tmp_path): """Recent command.json within timeout is kept""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): command_file = tmp_path / "command.json" command_file.write_text('{"id": "fresh", "action": "compile"}') @@ -2240,12 +2244,12 @@ def test_keeps_fresh_command_file(self, tmp_path): def test_no_command_file(self, tmp_path): """No error when command.json doesn't exist""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): cleanup_stale_command_file(timeout=30) # Should not raise def test_verbose_output(self, tmp_path, capsys): """Verbose mode logs stale command file cleanup""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import os command_file = tmp_path / "command.json" @@ -2264,7 +2268,7 @@ class TestCleanupOldResponsesWithTmpFiles: def test_cleanup_old_tmp_files(self, tmp_path): """Old .tmp files are cleaned up alongside response files""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import os # Create old tmp file @@ -2284,7 +2288,7 @@ def test_cleanup_old_tmp_files(self, tmp_path): def test_cleanup_both_response_and_tmp(self, tmp_path): """Both old response files and old tmp files are cleaned""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import os old_time = time.time() - 7200 @@ -2308,7 +2312,7 @@ class TestResponseCleanupOnError: def test_response_file_cleaned_on_timeout(self, tmp_path): """Response file is cleaned up when CommandTimeoutError is raised""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import uuid command_id = str(uuid.uuid4()) @@ -2319,9 +2323,9 @@ def mock_write(action, params): response_file.write_text('{"partial": true}') return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): with patch( - "claude_unity_bridge.cli.wait_for_response", + "codex_unity_bridge.cli.wait_for_response", side_effect=CommandTimeoutError("Timed out"), ): with pytest.raises(CommandTimeoutError): @@ -2333,7 +2337,7 @@ def mock_write(action, params): def test_response_file_cleaned_on_format_error(self, tmp_path): """Response file is cleaned up when format_response raises""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import uuid command_id = str(uuid.uuid4()) @@ -2349,9 +2353,9 @@ def mock_write(action, params): response_file.write_text(json.dumps(response_data)) return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): with patch( - "claude_unity_bridge.cli.format_response", + "codex_unity_bridge.cli.format_response", side_effect=RuntimeError("Format error"), ): with pytest.raises(RuntimeError, match="Format error"): @@ -2363,7 +2367,7 @@ def mock_write(action, params): def test_cleanup_handles_missing_response_file_on_timeout(self, tmp_path): """No error when response file doesn't exist during timeout cleanup""" - with patch("claude_unity_bridge.cli.UNITY_DIR", tmp_path): + with patch("codex_unity_bridge.cli.UNITY_DIR", tmp_path): import uuid command_id = str(uuid.uuid4()) @@ -2372,9 +2376,9 @@ def mock_write(action, params): # Don't create response file — simulates Unity never responding return command_id - with patch("claude_unity_bridge.cli.write_command", side_effect=mock_write): + with patch("codex_unity_bridge.cli.write_command", side_effect=mock_write): with patch( - "claude_unity_bridge.cli.wait_for_response", + "codex_unity_bridge.cli.wait_for_response", side_effect=CommandTimeoutError("Timed out"), ): with pytest.raises(CommandTimeoutError): From aee57fdd2290d4c4a7f63ad3cf9b0a7a880f7524 Mon Sep 17 00:00:00 2001 From: Enzo Esat Emre Demirel Date: Fri, 6 Mar 2026 16:41:05 +0100 Subject: [PATCH 2/2] fix: honor CODEX_HOME for skill installs --- skill/src/codex_unity_bridge/cli.py | 3 +++ skill/tests/test_cli.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/skill/src/codex_unity_bridge/cli.py b/skill/src/codex_unity_bridge/cli.py index e2527c1..0dc32b5 100644 --- a/skill/src/codex_unity_bridge/cli.py +++ b/skill/src/codex_unity_bridge/cli.py @@ -614,6 +614,9 @@ def get_skill_source_dir() -> Optional[Path]: def get_codex_skills_dir() -> Path: """Get the Codex skills directory path.""" + codex_home = os.environ.get("CODEX_HOME") + if codex_home: + return Path(codex_home).expanduser() / "skills" return Path.home() / ".codex" / "skills" diff --git a/skill/tests/test_cli.py b/skill/tests/test_cli.py index 7c4ef0c..12438a5 100644 --- a/skill/tests/test_cli.py +++ b/skill/tests/test_cli.py @@ -1675,11 +1675,29 @@ def test_get_codex_skills_dir(self): skills_dir = get_codex_skills_dir() assert skills_dir == Path.home() / ".codex" / "skills" + def test_get_codex_skills_dir_uses_codex_home_when_set(self, tmp_path): + """get_codex_skills_dir should honor CODEX_HOME when it is set""" + codex_home = tmp_path / "custom-codex-home" + + with patch.dict("os.environ", {"CODEX_HOME": str(codex_home)}, clear=False): + skills_dir = get_codex_skills_dir() + + assert skills_dir == codex_home / "skills" + def test_get_skill_target_dir(self): """get_skill_target_dir should return ~/.codex/skills/unity-bridge""" target_dir = get_skill_target_dir() assert target_dir == Path.home() / ".codex" / "skills" / "unity-bridge" + def test_get_skill_target_dir_uses_codex_home_when_set(self, tmp_path): + """get_skill_target_dir should derive from CODEX_HOME when it is set""" + codex_home = tmp_path / "custom-codex-home" + + with patch.dict("os.environ", {"CODEX_HOME": str(codex_home)}, clear=False): + target_dir = get_skill_target_dir() + + assert target_dir == codex_home / "skills" / "unity-bridge" + def test_install_skill_creates_symlink(self, tmp_path, capsys): """install_skill should create a symlink or copy to the skill directory""" skills_dir = tmp_path / "skills"