Interactive C# REPL for Unity — powered by Roslyn
Execute C# code on the fly in Unity Editor & Runtime — no compilation wait, no boilerplate,
full access to your project's live state. Editor zero-config, Runtime just works with HybridCLR.
Features · Installation · Quick Start · REPL Usage · Extending Commands
English | 中文
| Feature | Description | |
|---|---|---|
| >_ | Interactive REPL | Roslyn-based script submissions with persistent session state — variables, using directives, and types survive across executions |
| # | Top-level Syntax | Write statements directly. No class, no Main, no boilerplate |
| @ | Command Framework | Extensible [CommandAction] commands with automatic JSON-to-parameter binding (positional & named args), /batch endpoint for multi-command workflows |
| Tab | Semantic Completion | Real-time member, namespace, and type completions directly from Roslyn |
| 🔓 | Private Member Access | Bypass private / protected / internal access modifiers at compile time for deep inspection |
| ⏸ | Debug While Paused | Submit code while the Editor is paused in Play Mode — inspect transient state captured at the exact frame, without losing the snapshot |
| 📡 | Remote Execution | Compile in the Editor, execute on a connected Player build (IL2CPP via HybridCLR) |
/theme
GameObject.fnd → GameObject.Find
cam.mid → cam.m_InstanceID
var m = cam.transform.localToWorldMatrix
m.mp34 → m.MultiplyPoint3x4
@editor.status()Add via Packages/manifest.json:
{
"dependencies": {
"com.zh1zh1.csharpconsole": "https://github.com/niqibiao/unity-csharpconsole.git"
}
}Or reference as a local package:
{
"dependencies": {
"com.zh1zh1.csharpconsole": "file:../com.zh1zh1.csharpconsole"
}
}Note: Both assembly definitions have
autoReferenced: false. If your code needs to reference this package, addZh1Zh1.CSharpConsole.Runtime(or.Editor) to your asmdef's references explicitly.
Import the package and it just works. The Editor-side HTTP service starts automatically via [InitializeOnLoadMethod] — no initialization code, no settings to tweak, no manual setup. Open the REPL from the Unity menu:
| Menu Item | Target |
|---|---|
| Console > C#Console | Local Editor |
| Console > RemoteC#Console | Remote Editor / Player |
Enable the remote console on a Player build with a single call:
#if DEVELOPMENT_BUILD
Zh1Zh1.CSharpConsole.RuntimeInitializer.ConsoleInitialize();
#endifRuntime execution only depends on HybridCLR's Assembly.Load capability for IL2CPP (no additional configuration needed).
The runtime assembly is gated by
DEVELOPMENT_BUILD || UNITY_EDITOR.
| Port | |
|---|---|
| Editor | 14500 (default) |
| Runtime | 15500 (default) |
If a port is occupied, the service automatically advances to the next available one.
Warning
The service binds all network interfaces and has no authentication, by design: it targets trusted LANs where reaching a teammate's Editor or a device build is a feature. Anyone who can reach the port can execute C# in your Editor — do not expose it to untrusted networks.
The recommended way is through the Unity menu. You can also launch directly:
# Auto-discover running Unity Editors
python "Editor/ExternalTool~/console-client/csharp_repl.py"
# Connect to a specific Editor
python "Editor/ExternalTool~/console-client/csharp_repl.py" --editor --ip 127.0.0.1 --port 14500
# Connect to a Runtime Player (with Editor as compile server)
python "Editor/ExternalTool~/console-client/csharp_repl.py" \
--mode runtime --ip 127.0.0.1 --port 15500 \
--compile-ip 127.0.0.1 --compile-port 14500Python 3.7+ is required. Python dependencies (requests, prompt_toolkit, Pygments) are installed automatically on first launch.
Runtime code is compiled in the Editor and runs in the Player. A compile set uses the Player build's assemblies and defines for compilation, reducing mismatches caused by stripping or different #if branches.
Player builds export to Library/CSharpConsoleCompileSet.zip under the Unity project root by default. Disable export or enable Override to change its path in Edit > Project Settings > C# Console. Custom paths must be relative to the project root, such as Builds/Console.zip or ../Exports/Console.zip; absolute paths are not supported. Browse converts the selected location to a relative path, so it must be on the same drive or network share as the project. Missing folders are created automatically. Existing absolute-path overrides must be changed to relative paths or have Override turned off before exporting.
Each export replaces the ZIP at the selected path. Copy the ZIP elsewhere if you need to keep it for a particular build; the default export is also removed when you delete the project's Library directory. Disabling export leaves existing ZIPs in place.
When the REPL shows [REPL ALIGNMENT REQUIRED], register the ZIP from that Player build:
/compileset <ZIP path or http(s) URL>
Use /compileset skip to compile against the Editor's references instead; the code may then fail in the Player. The Editor remembers your choice for each build. Register again after rebuilding, or to update the compile set.
| Key | Action |
|---|---|
Enter |
Submit input |
Ctrl+Enter |
Insert newline without submitting |
Tab |
Accept completion candidate |
Ctrl+R |
Reverse history search |
Ctrl+C |
Copy selection; otherwise clear input (confirm quit if empty) |
Completion activates automatically as you type. The toolbar shows semantic completion status: on ● / off ○.
| Command | Description |
|---|---|
/completion <0|1> |
Toggle semantic completion |
/theme [name] |
List or switch code-highlighting themes |
/using |
Show default using file path |
/define |
Show preprocessor defines file path |
/reload |
Reload using / define files |
/reset |
Reset the REPL session |
/clear |
Clear the terminal |
/dofile <path> |
Execute a local .cs file |
/compileset <zip|URL|skip> |
Register the Player build's compile set, or skip alignment (runtime mode) |
The REPL supports @-prefixed command expressions that invoke the server-side command framework directly — bypassing Roslyn compilation:
@project.scene.open(scenePath: "Assets/Scenes/SampleScene.unity", mode: "single")
@editor.status()
@session.inspect(sessionId: "session-1")
Tab completion works for both command names and argument names.
62 built-in commands across 15 namespaces, covering editor control, scene manipulation, asset management, and more.
| Namespace | Action | Description |
|---|---|---|
| gameobject | find |
Find GameObjects by name, tag, or component type |
create |
Create a new GameObject (empty or primitive) | |
destroy |
Destroy a GameObject | |
get |
Get detailed GameObject, transform, component, and static-state info | |
modify |
Change name, tag, layer, active state, or static flag | |
set_parent |
Reparent a GameObject | |
duplicate |
Duplicate a GameObject | |
| component | add |
Add a component to a GameObject |
remove |
Remove a component from a GameObject | |
get |
Get serialized field data of a component | |
modify |
Modify serialized fields of a component | |
| transform | set |
Set position, rotation, and/or scale (local or world) |
| scene | hierarchy |
Inspect the scene hierarchy tree, optionally with component info |
| prefab | create |
Create a prefab asset from a scene GameObject |
instantiate |
Instantiate a prefab into the active scene | |
unpack |
Unpack a prefab instance | |
asset_hierarchy |
Get the hierarchy tree of a prefab asset | |
asset_get |
Get detailed info about a GameObject in a prefab asset | |
asset_get_component |
Get serialized properties of a component in a prefab asset | |
asset_modify_component |
Modify serialized fields of a component in a prefab asset | |
asset_add_component |
Add a component to a GameObject in a prefab asset | |
asset_remove_component |
Remove a component from a GameObject in a prefab asset | |
asset_modify_gameobject |
Modify a GameObject's properties in a prefab asset | |
asset_add_gameobject |
Add a child GameObject to a prefab asset | |
asset_remove_gameobject |
Remove a child GameObject from a prefab asset | |
| material | create |
Create a new material asset with a specified shader |
get |
Get material properties from an asset or a Renderer | |
assign |
Assign a material to a Renderer component | |
| scriptableobject | create |
Create a ScriptableObject asset of a given type |
get |
Get serialized field data of a ScriptableObject asset | |
modify |
Modify serialized fields of a ScriptableObject asset | |
| screenshot | scene_view |
Capture the Scene View to an image file |
game_view |
Capture the Game View to an image file | |
| profiler | start |
Start Profiler recording (optional deep profiling) |
stop |
Stop Profiler recording | |
status |
Get current Profiler state | |
save |
Save recorded profiler data to a .raw file |
|
| editor | status |
Get editor, play-mode, and transition state |
playmode.enter |
Enter play mode | |
playmode.exit |
Exit play mode | |
console.clear |
Clear the editor console | |
console.mark |
Write a searchable marker into the editor log and return the log file path | |
test.run |
Start a Unity Test Framework run (EditMode or PlayMode) | |
test.status |
Get the state and results of the latest test run | |
| project | scene.list |
List all scenes in the project |
scene.open |
Open a scene by path | |
scene.save |
Save the current scene | |
selection.get |
Get the current editor selection | |
selection.set |
Set the editor selection | |
asset.list |
List assets by type filter | |
asset.import |
Import an asset by path | |
asset.reimport |
Reimport an asset by path | |
| asset | move |
Move or rename an asset |
copy |
Copy an asset to a new path | |
delete |
Delete one or more assets | |
create_folder |
Create a folder in the Asset Database | |
| session | list |
List active REPL sessions |
inspect |
Inspect a session's state | |
reset |
Reset a session's compiler and executor | |
| command | list |
List all registered commands (built-in + custom) |
registry.snapshot |
Get the command registry snapshot | |
| runtime | info |
Report the responding process's device facts and well-known file paths |
39 actions need the editor and say so when a player receives them. The other 23 also answer in a player:
runtime/info,scene/hierarchy,gameobject/*,transform/set,component/*,screenshot/game_view,profiler/start|stop|status,session/*andcommand/*. A player has no undo stack, so mutations made there cannot be reversed, andcomponent/get|modifyonly covers components the project declares — built-in components keep their state in native properties rather than serialized fields.
prefab/asset_*edit the asset on disk without opening a scene. They address children by the identity selector returned fromasset_hierarchy(gid:<guid>:<localFileId>), not by name path.
In 2.4.0, scene and prefab hierarchy results report nodeCount, truncated, and
truncationReasons: requested_depth, node_limit (5000 nodes), or depth_limit
(depth 128, with the root at 0). depth=-1 retains the safety limits. Component
inspection and hierarchy results return namespace-qualified type names that can
be passed directly as typeName, such as Game.GameEntry.
editor/test.*requirecom.unity.test-frameworkin the consuming project; without it they stay registered but return an explanatory error.
The command framework lets any project add custom commands without modifying the package source — declare a [CommandAction] method and the framework handles discovery, parameter binding, and routing automatically.
See the full guide: Extending Commands
| Dependency | Version |
|---|---|
| Unity | 2022.3+ |
| Python | 3.7+ (on system PATH) |
| Windows Terminal | Optional (falls back to Python directly) |
- unity-cli-plugin — Non-interactive CLI for the same HTTP service, designed for scripting and automation workflows.
- python-prompt-toolkit — Terminal UI library powering the REPL's interactive interface.
- HybridCLR — IL2CPP hot-reload solution enabling
Assembly.Loadin Runtime mode.
This package bundles Roslyn compiler assemblies and dnlib under Editor/Plugins/. See ThirdPartyNotices.md for full attribution and license details.





