Transform Unity Editor into a programmable automation element for CI/CD pipelines and development workflows.
The Unity Pipeline package enables remote command execution in Unity Editor through HTTP APIs, allowing external tools and scripts to control Unity Editor instances programmatically. Perfect for:
- CI/CD Automation - Integrate Unity Editor into build pipelines
- Development Workflows - Automate repetitive Unity tasks
- Testing Automation - Control play mode and execute tests remotely
- Multi-Project Management - Manage multiple Unity instances from CLI
- 🌐 HTTP API Server - RESTful endpoints for Unity Editor control
- 🔍 Auto-Discovery - Automatic detection of running Unity instances
- 📦 Command Registration - Extensible command system with attributes
- 📋 Command Discovery - List available commands and parameters
- 🔒 Local Security - Localhost-only connections for MVP
- ⚡ Fast Response - TypeCache-based command discovery
- Unity 6.0 or higher (Package uses Unity 6.0+ features)
- Git (For package installation)
- Node.js (For CLI building)
macOS / Linux
curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh | UNITY_CLI_CHANNEL=beta bash
Windows (PowerShell)
$env:UNITY_CLI_CHANNEL='beta'; irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex
For more detailed check here: here.
Use the modified CLI to install the Pipeline package:
# Install in current Unity project directory
unity pipeline install
# Or specify a project path
unity pipeline install --project-path /path/to/your/unity/project
# Use SSH authentication if needed
unity pipeline install --sshOpen your Unity project in Unity Editor. The Pipeline package will:
- Automatically start the HTTP server on a port in the
7800-7849range - Create a
.unity-pipeline-portdescriptor file underLibrary/Pipeline/(git-ignored) - Register built-in commands (play mode control, status, recompile, tests, etc.)
Check that everything is working:
# List all Unity instances with Pipeline package
unity pipeline list
# Connect to Unity and list available commands
unity command
# Test a simple command
unity command editor_statusThe canonical CLI verb is
unity command.unity requestis a supported alias — all examples below useunity command.
Install the Pipeline package in a Unity project.
# Auto-detect project (current directory or running Unity instance)
unity pipeline install
# Specify project path
unity pipeline install --project-path /path/to/project
# Use SSH authentication
unity pipeline install --sshInstallation Strategy:
- Current directory (if Unity project) → Install here
- Single running Unity instance → Install there
- Multiple instances → Smart selection or user choice
List all Unity Editor instances and their Pipeline status.
unity pipeline listSample Output:
🎮 Found 2 Unity Editor instance(s):
1. MyProject1 - 🟢 Running (PID: 12345)
📁 Project: C:\Projects\MyProject1
📦 Pipeline: ✅ Installed
🔌 Server: 🟢 Connected on port 7801
🌐 API: http://localhost:7801/api/editor_status
2. MyProject2 - 🟢 Running (PID: 12346)
📁 Project: C:\Projects\MyProject2
📦 Pipeline: ❌ Not installed
💡 Run: unity pipeline install --project-path "C:\Projects\MyProject2"
📊 Summary:
🎮 Unity Instances: 2/2 running
📦 Pipeline Package: 1/2 installed
🔌 Pipeline Servers: 1/1 reachable
Connect to a Unity instance and list its available commands.
# Auto-discover Unity instance
unity command
# Connect to specific project
unity command --project-path /path/to/project
# Connect to specific instance
unity command --instance localhost:7801Execute a specific command on the connected Unity instance.
# Play mode control (Editor)
unity command editor_play # Enter play mode
unity command editor_stop # Exit play mode
unity command editor_pause # Pause play mode
# Editor status
unity command editor_status
# Evaluate C# (positional code argument)
unity command eval "return 2 + 2;"
# Write to the Unity console
unity command log "Build started" --level info
# Force a recompile, then poll for completion
unity command recompile
unity command recompile_status
# Execute with connection options
unity command editor_play --project-path /path/to/project
unity command editor_status --instance localhost:7802The CLI supports both positional arguments and flags, following Git conventions:
Positional Arguments (for core data):
unity command eval "return 2 + 2;" # Code is positional
unity command log "Hello World" # Message is positional
unity command editor_status # No parameters neededFlags/Switches (for options/modifiers):
unity command log "Error occurred" --level error
unity command set_autotick --enable true --interval_ms 16Mixed Syntax:
unity command run_tests "MyFixture.MyTest" --mode editor --timeout 120Parsing Rules:
- Required parameters can be positional (like Git commit messages)
- Optional parameters use
--flag valuesyntax - Flags without values become
true(boolean flags) - Values are auto-parsed (strings, numbers, booleans)
The package runs two independent servers, and the CLI talks to both the same way:
| Server | Ports | Descriptor file | When it runs |
|---|---|---|---|
| Editor | 7800-7849 |
.unity-pipeline-port (Library/Pipeline/) |
Auto-starts when the Unity Editor loads |
| Runtime | 7900-7949 |
.unity-pipeline-runtime-port (working dir) |
Only in a development Player build with the runtime server enabled — not in Editor play mode |
Editor commands (editor_*, recompile, run_tests, set_autotick, …) target the
Editor server. Runtime commands (eval, log, reload_file_override, set_timescale, …) target
the Runtime server of a running development Player. See the Commands reference below for
which command lives where.
Connection resolution (cascading, no magic):
- Explicit Instance (
--instance localhost:7801) → direct connection - Explicit Project (
--project-path /path) → connect to that project's instance via its descriptor file - Auto-Discovery — current directory must be a Unity project root with a descriptor file:
- Single instance → auto-connect
- Multiple instances → list options and require user choice
- No instances → clear error with guidance
To control a running development Player instead of the Editor, point the CLI at the Player
executable with --runtime:
# By executable name (matched against discovered Player instances)
unity --runtime MyGame.exe
# By full path to the built executable
unity --runtime "C:\Builds\MyGame.exe"Combine --runtime with a command to run runtime commands against the Player:
unity --runtime MyGame.exe command runtime_status
unity --runtime MyGame.exe command eval "return 2 + 2;"The Runtime server only starts if the Player build is set up for it:
- Add a
RuntimePipelineManagerto your scene. Add the Pipeline ▸ Runtime Pipeline Manager component to a GameObject in your startup scene. It is marked[DontDestroyOnLoad], and only one instance should exist. - Enable it in builds. On the component, tick Enable in builds (
enableInBuilds) — it is off by default, and the server will not start without it. Leave Auto Start on (the default) so the server starts automatically onStart(). - Build a Development Build. Runtime code compilation/
evalis restricted to development desktop builds. In the Build Profile (or Build Settings) enable Development Build — release builds will not expose the server. - Token management. The Runtime server requires an authentication token:
- Leave Custom Token empty to auto-generate a cryptographically secure 256-bit token at startup (recommended).
- Or set a Custom Token explicitly — it must be at least 32 characters (Base64 recommended).
- The effective token is written to the
.unity-pipeline-runtime-portdescriptor in the Player's working directory. For local requests the server auto-injects it, so--runtimecommands don't need the token passed manually. - Security defaults to TokenAndIpAllowlist with an IP allowlist of
127.0.0.1(localhost only); widen the allowlist only for trusted, non-production network access.
Hot reload changes gameplay code in a running game — Editor Play Mode or a development Player build — without a domain reload or restart. There are two workflows; in-place is the default.
Where it works: Editor Play Mode and Mono desktop development builds (Windows/Mac/Linux). It compiles the new code at runtime, so it is not available in IL2CPP builds (iOS, consoles, release).
Setup (both workflows): add a Pipeline ▸ Runtime Pipeline Manager to your scene (see
Runtime requirements). On Awake it auto-discovers every method tagged
[HotReload] or [HotReloadWithOverrides] — no manual registration needed. In Editor Play Mode the
reload_file* commands target the Editor server (the default connection); for a development Player
build, target it with --runtime MyGame.exe.
Edit the method directly — no separate file and no helper boilerplate.
-
Mark the method
[HotReload]:using Unity.Pipeline.HotReload; using UnityEngine; public class Spinner : MonoBehaviour { public float rotationSpeed = 90f; [HotReload] void Update() { transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime); } }
-
Edit the method body in place (for example, change
Vector3.uptoVector3.forward, or the speed). -
Apply it while the game runs, and re-run to iterate:
unity command reload_file "<absolute path>/Spinner.cs"
Add
--pdbto make the reloaded method debuggable — it emits a portable PDB mapped back to your source so breakpoints set in the original file bind to the running override:unity command reload_file "<absolute path>/Spinner.cs" --pdb
Attach your IDE (Rider/Visual Studio) to the Editor and enable Preferences → External Tools → Editor Attaching.
--pdbcompiles unoptimized, so use it for debugging sessions rather than the fastest iteration.
Limitations
- Without
--pdbyou cannot break into / step through the reloaded method in a debugger (it is compiled without symbols). Pass--pdbto enable debugging. - You can only access public fields and methods (the new code is compiled into a separate assembly).
- Currently only
voidinstance methods are supported in place; value-returning, generic, and coroutine methods are not yet hot-reloadable this way.
The original method stays put; the tweaked version lives in a separate override file.
-
Mark the method
[HotReloadWithOverrides]and route it through the helper (the original logic stays as the fallback):using Unity.Pipeline.HotReload; using UnityEngine; public class BossController : MonoBehaviour { [HotReloadWithOverrides] void Update() { HotReloadHelper.ExecuteWithHotReload(this, "Update", OriginalUpdate); } public void OriginalUpdate() { transform.Rotate(45 * Time.deltaTime, 0, 0); } }
-
Write the override in its own file as a
public staticmethod tagged[HotReloadOverrideMethod("Type.Method")]that takes the instance as its first parameter. The file must not redeclare the target type:using Unity.Pipeline.HotReload; using UnityEngine; public static class BossOverrides { [HotReloadOverrideMethod("BossController.Update")] public static void TweakedUpdate(BossController instance) { instance.transform.Rotate(0, 90 * Time.deltaTime, 0); // new behaviour } }
-
Apply it while the game runs, and re-run to iterate:
unity command reload_file_override "<absolute path>/BossOverrides.cs"
reload_file_overridevalidates the file up front and reports a clear error if it contains no[HotReloadOverrideMethod], redeclares the target type, or the override signature is wrong.
Limitations
- You cannot break into / step through the override in a debugger — it is compiled and loaded at runtime without debug symbols.
- The override can only access public fields and methods of the target, because it lives in a separate assembly.
All commands are invoked as unity command <name> [args] (or via POST /api/exec).
Commands are discovered dynamically — run unity command with no name to list exactly what
a given instance exposes. Parameters are auto-parsed, and the first required parameter may be
passed positionally.
| Command | Description | Parameters |
|---|---|---|
editor_status |
Get detailed Unity Editor status and state information | — |
editor_play |
Enter Unity Editor play mode | — |
editor_stop |
Exit Unity Editor play mode | — |
editor_pause |
Pause Unity Editor play mode | — |
editor_focus |
Bring the Unity Editor window to the foreground | — |
recompile |
Force a script recompile (works while unfocused/minimized). Poll recompile_status for completion. |
— |
recompile_status |
Get the status of the last recompile | returns idle | triggered | compiling | completed | up_to_date |
set_autotick |
Keep the editor ticking while unfocused by forcing EditorApplication.SignalTick at a throttled rate |
enable: bool = true, interval_ms: int = 16 (0 = every update, max rate) |
list_tests |
List all available tests (EditMode/PlayMode) without running them | mode: string = "all" (all/editor/playmode) |
run_tests |
Execute Unity tests with filtering options | filter: string = "", mode: string = "all" (all/editor/playmode), filter_type: string = "testName" (testName/assembly/category), include_explicit: bool = false, async_tests: bool = false, timeout: int = 300 |
test_status |
Get status of running async test execution | — |
cancel_tests |
Cancel running test execution | — |
These are tagged [CliCommand(RuntimeOnly = true)]. They are not listed by unity command
when you are connected to an Editor server — only when connected to a running Player's Runtime
server. (They remain executable in the Editor for testing, just not advertised there.)
| Command | Description | Parameters |
|---|---|---|
runtime_status |
Get comprehensive runtime application status | — |
eval |
Evaluate C# code dynamically using the Roslyn compiler | code: string (required), token: string (auto-injected locally), timeout: int = 5000 |
log |
Write a message to the Unity console | message: string (required), level: string = "info" (info/warning/error) |
set_target_framerate |
Set the target frame rate for the application | frameRate: int (required; -1 = platform default, 0 = unlimited) |
set_timescale |
Set the time scale for the application | scale: float (required; 0.0 = pause, 1.0 = normal) |
quit |
Gracefully quit the Unity application | exitCode: int = 0 |
reload_file_override |
Compile and apply hot reload changes from a separate override file (helper workflow) | filename: string (required), token: string (auto-injected locally), timeout: int = 30000, assemblyDir: string = null |
reload_file |
Compile and apply in-place [HotReload] edits from a source file |
filename: string (required), token: string (auto-injected locally), timeout: int = 30000, assemblyDir: string = null |
hotreload_status |
Show current hot reload registry status and statistics | token: string (auto-injected locally) |
cleanup_hotreload |
Remove old hot reload DLL versions and clear registry | token: string, assemblyDir: string, force_domain_reload: bool = true |
apply_function |
Validate and integrate hot reload changes back to original source (not yet implemented) | prototype_file: string, original_file: string, token: string, create_backup: bool = true |
Commands with a
tokenparameter require a security token. For local requests the server auto-injects it from the instance descriptor, so you don't need to pass it manually.
Two operations don't finish within a single request and use a trigger-then-poll pattern:
recompile— a successful compile triggers a domain reload that tears down the HTTP server, so the triggering request can't stay open. Callrecompile, then pollrecompile_statusuntil it reportscompletedorup_to_date. Tolerate connection errors while the reload is in progress.run_tests --async_tests true— returns immediately; polltest_status(orGET /api/test-status) until the run finishes, andcancel_teststo abort.
The package exposes the same HTTP endpoints on the Editor server (localhost:7800-7849)
and the Runtime server (localhost:7900-7949):
GET /api/status- Basic server health (port, start time; no Unity APIs)GET /api/editor_status- Detailed Unity Editor statusGET /api/commands- List all available commands with schemasPOST /api/exec- Execute a command with a JSON payloadGET /api/test-status- Status of an async test run
Request Format:
POST /api/exec
{
"command": "set_autotick",
"parameters": { "enable": true, "interval_ms": 16 }
}Response Format:
{
"success": true,
"command": "set_autotick",
"result": "Auto-tick enabled"
}On failure, success is false and an error field describes the problem.
Extend the Pipeline with custom commands using attributes. The methods below are illustrative templates — write your own and they are discovered automatically:
using Unity.Pipeline.Commands;
using UnityEditor;
public static class CustomCommands
{
[CliCommand("build_player", "Build the Unity project")]
[CliArg("platform", "Target build platform", typeof(string), required: true)]
[CliArg("outputPath", "Build output directory", typeof(string))]
public static string BuildPlayer(string platform, string outputPath = "Builds/")
{
// Custom build logic here
var buildTarget = ParseBuildTarget(platform);
var result = BuildPipeline.BuildPlayer(/*...*/);
return result.succeeded ?
$"Build completed: {outputPath}" :
$"Build failed: {result.summary.totalErrors} errors";
}
[CliCommand("asset_refresh", "Refresh Unity asset database")]
public static string RefreshAssets()
{
AssetDatabase.Refresh();
return "Asset database refreshed";
}
}Once compiled, your commands are available via CLI just like the built-ins:
unity command build_player --platform Windows --outputPath C:/Builds/
unity command asset_refresh-
Package Development:
# Work directly in package directory cd /path/to/com.unity.pipeline # Unity will detect changes automatically # No package reinstallation needed
-
CLI Development:
# In unity-hub directory npm run build:cli # Test changes unity pipeline list unity command editor_status
The package includes comprehensive tests:
# Run Unity package tests in Unity Editor
# Window → General → Test Runner
# Select EditMode tests for Pipeline package
# Or run them remotely through the Pipeline server
unity command run_tests --mode editorTest Categories:
- Command Registration - Verify attribute-based discovery
- HTTP Server - Test all API endpoints
- Connection Logic - Validate instance discovery
- Integration - End-to-end CLI workflows
"No Unity Editor instances found"
# Check if Unity Editor is running
unity pipeline list
# Verify package is installed
unity pipeline install --project-path /path/to/project"Cannot connect to Pipeline server"
# Check if server is running
curl http://localhost:7801/api/status
# Verify port file exists
ls -la /path/to/project/Library/Pipeline/.unity-pipeline-port"Command not found"
# List available commands
unity command
# Check command spelling and parameters
unity command editor_statusPipeline servers use these port ranges to avoid conflicts:
- Editor server:
7800-7849(test servers use7850-7899) - Runtime server:
7900-7949(test servers use7950-7999)
For Git clone authentication problems:
# Try SSH instead of HTTPS
unity pipeline install --ssh
# Or configure Git credentials for HTTPS
git config --global credential.helper storeEnable verbose logging:
# Check Unity Console for Pipeline server logs
# Look for "Pipeline Server" messages
# Verify HTTP communication
curl -v http://localhost:7801/api/statuscom.unity.package is licensed under the Unity Companion License. See LICENSE.md for more legal information.
We are not accepting pull requests at this time. If you find an issue with the package or would like to request a new feature, please submit a GitHub issue.