Execute raw C# in a running Unity Editor via the Roslyn-based CSharpConsole REPL.
Always prefer the unity-cli-command skill first.
Then check the snippet library (cs snippets search <description>) before writing ad-hoc code. After solving a non-trivial task that's likely to recur, consider distilling it into a snippet — see the unity-cli-snippets skill.
Running
cs: below,csis shorthand forpython "$HOME/.unity-cli-plugin/current/cli/cs.py"— one stable path, run verbatim without changing directory. If it's not installed yet, run the unity-cli-setup skill once first.
Usage
Inline code:
python "$HOME/.unity-cli-plugin/current/cli/cs.py" exec --json --project "$(pwd)" "<C# code>"
From a file (avoids shell quoting hazards for long/complex snippets):
python "$HOME/.unity-cli-plugin/current/cli/cs.py" exec --json --project "$(pwd)" --file path/to/snippet.cs
All examples below use the inline form, showing only the C# code portion for brevity.
REPL Features
This is a Roslyn REPL, not a simple eval. Non-obvious capabilities:
- Top-level syntax — no
class/Mainboilerplate; write statements directly - Expression auto-return — the last expression value is returned in the result; prefer over
Debug.Log - Cross-submission state — variables,
usings, and types persist acrossexeccalls within the session - Private member access — compiler bypasses
private/protected/internalat compile time - Pre-loaded usings —
SystemandUnityEngineare available by default. Addusing System.Linq;orusing System.Collections.Generic;explicitly when needed (they persist in the session)
Patterns
Expression evaluation
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
// inline chains work too:
var cam = Camera.main; cam.fieldOfView
Multi-step with cross-submission state
// Call 1: store a reference
var player = GameObject.Find("Player");
// Call 2: `player` is still alive
player.transform.position
Private member access (no reflection needed)
var go = GameObject.Find("Main Camera"); go.m_InstanceID
LINQ queries over live scene
// Prefer FindObjectsByType (Unity 2023.1+); FindObjectsOfType is deprecated.
using System.Linq; UnityEngine.Object.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None).Select(r => $"{r.name}: mass={r.mass}").ToList()
// System.Linq persists — no need to re-import
Resources.FindObjectsOfTypeAll<GameObject>().Where(g => !g.activeInHierarchy).Select(g => g.name).ToList()
// Resources.FindObjectsOfTypeAll is still current — it returns inactive/asset objects too.
AssetDatabase
using System.Linq; UnityEditor.AssetDatabase.FindAssets("t:Material").Select(g => UnityEditor.AssetDatabase.GUIDToAssetPath(g)).ToList()
Define reusable helpers (persists in session)
// Call 1: define a function
string Dump(Transform t, int d=0) { var s = new string(' ', d*2) + t.name; foreach(Transform c in t) s += "\n" + Dump(c, d+1); return s; }
// Call 2: use it — function persists across submissions
Dump(GameObject.Find("Canvas").transform)
Batch modify
foreach(var r in GameObject.FindGameObjectsWithTag("Debug").SelectMany(g => g.GetComponents<MeshRenderer>())) r.enabled = false;
Session Reset
Reset when variable name collisions or stale state occur:
python "$HOME/.unity-cli-plugin/current/cli/cs.py" command --json --project "$(pwd)" session reset
Notes
- Always use
--jsonfor parseable output - Check
result.okandresult.exitCodefor success/failure - Port is auto-detected from
Temp/CSharpConsole/refresh_state.json