hera-agent-unity
August 18, 2026 · View on GitHub
You are an AI coding agent operating in a Unity project that has
hera-agent-unityavailable as a CLI. This document tells you how to use it efficiently. It is meant to be loaded into your project's rules file so every session has it without spending tokens to discover it.Where to put this content.
AGENTS.mdat the project root is the canonical cross-tool agent rules file, standardized by the Agentic AI Foundation (AAIF) under the Linux Foundation (Dec 2025) and adopted by 60,000+ open-source repositories. OpenAI Codex, Claude Code, Cursor, GitHub Copilot, Gemini CLI, and 30+ tools read this file by default.For multi-tool projects, the cleanest pattern is
AGENTS.mdas the single source of truth plus a one-line stub in tool-specific paths. Tool-specific files only matter when a tool requires a different format (Cursor's.mdcYAML frontmatter is the main case).Recommended layout — multi-tool projects:
- Drop the full guide (or its lean subset) into
AGENTS.mdat the project root.- For tools that need their own format, drop a short stub that defers to
AGENTS.md:
CLAUDE.md→> See AGENTS.md.(Claude Code readsAGENTS.mdnatively since late 2025).cursor/rules/hera-agent-unity.mdc→ frontmatter + the same body (Cursor also supports plainAGENTS.mdas a fallback).github/copilot-instructions.md→ repository-wide pointer toAGENTS.md(Copilot uses nearest-file precedence;.github/skills/is for Agent Skills)GEMINI.md+.agents/agents.md+.agents/skills/hera-agent-unity/SKILL.md→ AntiGravity project entry + workspace handoff + on-demand skill.continuerules→ identical bodyPer-tool target paths (2026-current):
Tool Canonical path Notes OpenAI Codex / AGENTS.md-aware tools AGENTS.mdCross-tool standard. Supports layering: ~/.codex/AGENTS.md→ repo root → subtree →AGENTS.override.md.Claude Code AGENTS.md(orCLAUDE.md)Reads AGENTS.mdnatively.CLAUDE.mdstill works for path-scoped rules and imports.Cursor .cursor/rules/*.mdcYAML frontmatter required for activation. .cursorrules(single-file) is deprecated and ignored by Agent mode.GitHub Copilot .github/copilot-instructions.mdNearest-file precedence. Optional: .github/instructions/*.instructions.mdwithapplyTofrontmatter;.github/skills/for Agent Skills.Google AntiGravity GEMINI.md,.agents/agents.md,.agents/skills/*/SKILL.md.agents/is the native workspace extension directory for agents, workflows, and skills.Continue.dev .continuerulesPlain markdown. Other Tool-specific rules file Most accept plain markdown. Two ways to populate the target file:
- Static — copy the matching stub from
examples/rules/(one file per tool, already formatted).- Dynamic — let the CLI generate it from this guide:
# AGENTS.md / CLAUDE.md / Copilot / Continue.dev — compact always-loaded rules hera-agent-unity doctor --agent-rules --compact >> AGENTS.md # Cursor — compact rules with activation frontmatter hera-agent-unity doctor --agent-rules --compact --format cursor > .cursor/rules/hera-agent-unity.mdc # AntiGravity — compact project entry + full on-demand skill hera-agent-unity doctor --agent-rules --compact >> GEMINI.md hera-agent-unity doctor --agent-rules --format antigravity > .agents/skills/hera-agent-unity/SKILL.md
hera-agent-unity is a CLI that drives a running Unity Editor over HTTP. Common uses: execute C# inside the Editor, read console logs, query the active scene, run tests, capture screenshots, batch several commands in one round-trip. Each call is a tool round-trip; response bytes become your input tokens, so reads cost as much as your own writes.
Quick links:
- Full command catalog →
docs/COMMANDS.md - Architecture →
docs/ARCHITECTURE.md - Custom tool authoring →
hera-agent-unity help custom-tools - Setup / install → README
0. Bootstrap on Discovery
When the user invites you to engage hera-agent-unity — in any language, any phrasing — do not ask follow-up questions and do not stop at "yes, it looks installed". Run the discovery sequence and report.
Trigger phrases (treat as equivalent):
find hera-agent-unity·hera-agent-unity 찾아봐is hera-agent-unity installed?·설치돼 있어?check the editor connection·에디터 붙어있어?connect to unity·unity에 연결해줘- Anything mentioning hera-agent-unity or hera-agent in a check / discovery / "are you set up?" context.
Sequence — run all three, in order, without prompting:
hera-agent-unity doctor --json— verifies the binary is on PATH, no duplicate installs, and the connector can see at least one Unity instance. JSON envelope is parseable.hera-agent-unity status— confirms the active editor's port, project path, Unity version, PID, and current state (ready,compiling, …).hera-agent-unity list --compact— discovers what tools (built-in + custom[HeraTool]classes) this project exposes with the smallest practical payload, so subsequent prompts can be answered without re-scanning.
Report shape (one line first, then optional details):
Connected: <project name> · port=<N> · unity=<version> · state=<ready|compiling> · tools=<count>
If a step fails, do not silently skip — surface the failure verbatim:
doctorsays binary missing → tell the user to install it (curl … | shor the README link), do not proceed.doctorsays Unity is unreachable → tell the user to open the Editor with the UPM package, do not proceed.statusreturns no instances → same.
After a successful bootstrap, you may proceed to whatever the user actually wanted. The bootstrap output replaces "let me check if it's installed" — that line costs tokens and tells the user nothing they can act on.
1. Quick Rules (must-follow)
Numbered so you can grep "[Rule N]" when in doubt.
[Rule 1] Default to no return (or return null;) in exec. Side-effecting code (create objects, set properties, save scenes) should not return a verbose status string. The OK response is 3 bytes (OK\n); a hand-crafted summary string ships hundreds of bytes back into your context. The trailing return is optional — snippets without one resolve to null automatically.
// Bad — your status string costs ~200 tokens
return $"Created Canvas with {n} buttons under {parent.name}";
// Good — same work, 3 bytes
return null;
// Also good — omit the return entirely
new GameObject("X");
Caveat:
return;(no value) still does NOT compile becauseExecute()returnsobject. Writereturn null;for early exits, orthrow new Exception("...")for hard failures (see Rule 8).
The CLI emits compact JSON automatically for non-human commands (anything outside
install/uninstall/status/update/doctor). Pass--compact-jsonor setHERA_AGENT_COMPACT_JSON=1to force compact on a TTY too.
Full Access remains the
execdefault. Add--security-mode restrictedonly when the snippet can stay within platform APIs and should be denied file, network, process, reflection, native, threading,UnityEditor, and project-assembly access. Restricted mode is defense in depth; arbitrary-code permission, approval, the operation ledger, and the strict contract still apply.
[Rule 2] Never return a UnityEngine.Object directly. Transform, GameObject, Component, Scene, Material, etc. expand to thousands of bytes of reflected properties.
// Bad — Transform serializes to 9KB+ (position, rotation, matrices, ...)
return GameObject.Find("Canvas").transform;
// Good — name + id is what you actually wanted
var go = GameObject.Find("Canvas");
return new { name = go.name, instanceID = go.GetInstanceID() };
Default --depth is 3, which fully reflects Unity Objects in the response. Pass --depth 1 (or 2) when you want the leanest payload — depths 1–2 collapse Unity Objects to the shallow form {name, type, instanceID}. Set --depth 3 only when you have a specific reason to inspect the property tree.
[Rule 3] Branch on the code field of error responses, not on the message text. Messages get tweaked across versions; code is the stable enum-like contract.
// Error envelope shape
{
"success": false,
"code": "EXEC_COMPILE_ERROR", // <-- branch on this
"message": "Your C# snippet did not compile. L1 CS1525: ... (+2 more)",
"data": { "compile_errors": [ ... ] }
}
[Rule 4] Batch related operations into a single exec call. Each call has fixed envelope + HTTP overhead; one exec that creates Canvas + 3 buttons is cheaper than three separate calls. For coarser composition (editor refresh + console, etc.) use the batch command.
[Rule 5] For console reads, the default --lines is 20 (sane cap). Use --lines 0 only when you actually need every entry. Use --type error when you don't care about warnings/logs.
[Rule 6] Runtime errors from exec return user-filtered stack traces by default. If you suspect the framework itself is the cause (e.g. Unity internal exception), pass --stacktrace full to see all frames.
[Rule 7] Use the right tool for the job — see §2. exec is the universal hammer but it costs csc compile time and is harder to inspect. Dedicated commands (scene info, console, status, describe_type, find_method) are faster and cheaper.
[Rule 8] When you want a logical failure to be visible at the CLI exit-code layer, either throw (throw new System.Exception("missing") → EXEC_RUNTIME_ERROR, exit non-zero) or run with --strict so that any Debug.LogError/LogException/LogAssert raised during the snippet flips the response to EXEC_LOGGED_ERROR. Without one of these, Debug.LogError("..."); return null; is indistinguishable from a clean run by exit code — only the Unity console sees it.
// Off (default) — agent sees success even though "missing" was logged
// hera-agent-unity exec "Debug.LogError(\"missing\"); return null;"
// → exit 0, success
// On — same code surfaces as EXEC_LOGGED_ERROR
// hera-agent-unity exec "Debug.LogError(\"missing\"); return null;" --strict
// → exit 1, code=EXEC_LOGGED_ERROR
[Rule 9] Do not put your current machine's absolute paths in shared docs,
rules, examples, generated Markdown, or checked-in scripts. Prefer
repo-relative paths, documented environment variables, or explicit CLI flags.
For Unity Hub editor inventory, write paths with %UNITY_HUB_EDITOR% and state
that the default Windows resolver is %ProgramFiles%\Unity\Hub\Editor; let
users override the real root with -HubRoot or their own environment.
[Rule 10] Keep CLI and UPM connector versions separate. hera-agent-unity version reports the Go CLI release tag (vX.Y.Z). Unity Package Manager reports
the connector package version from AgentConnector/package.json (for example
0.0.N). Do not call the UPM package vX.Y.Z, do not assume the two numbers
match, and do not use a git lock hash as the package version. A lock hash only
identifies the connector source commit that Unity resolved.
[Rule 11] Separate Unity-level input QA from physical OS click QA. Use
input inspect / input click / input submit / input scroll / input drag
for uGUI EventSystem behavior, and use input keyboard / input mouse, a
bounded strict input sequence, or bounded input record / input replay in Play
Mode when the project already has the optional Input System package. These
commands synthesize Unity events or device state; they do not prove that a
physical OS/window click worked. If Computer Use still cannot capture Unity
screenshot state and no native OS/window input backend is available, record the
physical-click criterion as BLOCKED even when Unity-level input QA passes.
[Rule 12] CLI v0.1.0+ includes an experimental MCP adapter that remains
default-off and stdio-only. The normal CLI remains the production default. Use
HERA_MCP_ENABLED=1 hera-agent-unity mcp only with an intentionally configured
MCP client and keep stdout free of shell banners and diagnostics. Once enabled,
Compact is the exposure default: search tool/action names, describe only the
selected action contract, then call. Profile and Full are explicit larger-schema
opt-ins. Missing approval or operation-ledger features must fail closed. Full
setup and compatibility rules are in docs/MCP.md.
[Rule 13] Treat a Unity port as a temporary connection endpoint, not an
Editor identity. At the start of Unity work, run the bootstrap sequence and
confirm the selected project's full path. When several heartbeats exist, prefer
--project <full-path>; exact normalized paths win, a legacy substring is
accepted only when it identifies one project, and an ambiguous match fails.
Supplying both --project and --port requires both to identify the same
Editor. If a request loses its response or times out, Hera fresh-reads the
heartbeat before retrying and distinguishes a domain-reload port change, an
Editor restart, a lost target, and a port reused by another project. Never
blindly resend a mutation: only idempotent calls or operation-ledger-backed
operations may retry.
[Rule 14] Continue approval-gated CLI work with the exact request that was
preflighted. In a non-interactive shell, both typed call and established
commands return APPROVAL_REQUIRED; repeat the same command and append
--approve <token>. Do not change the project, tool, action, arguments, or
operation ID, because the short-lived token is bound to all of them and is
single-use, so the repeat must carry the original arguments again. Never
approve automatically on the user's behalf: the --yes flag and
HERA_AGENT_APPROVE belong to the operator's own shell or CI job, so read the
approval summary and ask instead of adding them to a command yourself.
[Rule 15] Treat TEST_RUN_PENDING as a slow Unity Test Runner result, not
as proof that the Editor is unresponsive. The error data contains the exact
port and run_id; continue the same run with hera-agent-unity --port <port> test --resume <run_id> --timeout <milliseconds>. Resume polls the durable
result without sending run_tests again. A stale heartbeat can occur while
Unity's main thread is busy recording test results, so check the pending run,
the OS process, and port reachability before declaring the Editor lost. For an
immediate, non-waiting check, use task list and retain its task_id, then use
task status <task_id>; both read the same local durable state as MCP Tasks and
do not contact or mutate Unity.
1.5 Ultra Hera
Ultra Hera helps AI check its Unity work. Hera does not do the AI work by itself. This setting tells AI agents how carefully they should check Unity work after using Hera.
Modes are saved in asset-config.json as loopEngineeringMode:
off: AI does not have to check again after using Hera.light(default): use the Light loop for every Unity coding, Editor, and Inspector task.ultra: use the Light loop for every task, then upgrade to the Ultra loop for strict requests or important Unity work.
Light loop:
- Confirm the goal in one sentence.
- Observe only the needed current state in a compact way.
- Change code, scene, or Inspector state.
- Verify compile or state.
- Check console errors.
- Re-read only the changed target.
- If it failed, fix and repeat up to 1-2 times.
- Report short final evidence.
Representative Light commands:
hera-agent-unity status
hera-agent-unity console --type error --lines 20
hera-agent-unity editor refresh --compile
hera-agent-unity find_gameobjects --ids
hera-agent-unity manage_components get ...
hera-agent-unity exec --depth 1 ...
Light Mode's goal is: do not finish in a wrong state. PlayMode, screenshots, and full tests are not required by default.
Ultra loop:
- Split the goal into success criteria.
- Take a before-change state snapshot.
- Apply the change.
- Compile.
- Confirm console errors are 0.
- Re-read Inspector, GameObject, or asset state.
- Run PlayMode or Unity tests.
- If needed, capture a screenshot; use
--overlayfor ScreenSpaceOverlay canvases. - Classify the failure cause and repeat.
- Report final evidence and remaining risk.
Representative Ultra commands:
hera-agent-unity editor refresh --compile
hera-agent-unity console --type error --lines 50
hera-agent-unity test --mode EditMode
hera-agent-unity test --mode PlayMode
hera-agent-unity editor play --wait
hera-agent-unity screenshot --view game
hera-agent-unity screenshot --overlay --output_path built.png
Use Ultra when the user asks for strict verification, for example 정확히 검증해줘, 플레이해서 확인해줘, UI 맞춰줘, or 인스펙터까지 확실히 봐줘.
2. Tool Selection Cheatsheet
When you can do something with a dedicated command, use it instead of exec. Dedicated commands skip csc compilation (5–15s cold, ~500ms warm).
| You want to … | Use | Notes |
|---|---|---|
| Active scene name / path / dirty | scene info | Returns active + loaded scenes in one shot. |
| Open / save / close a scene | scene load <path> / scene save / scene close | Modes: single (default), additive. |
| Read recent console errors | console --type error | Default 20 entries; pagination via --lines + --since. |
| Clear console | console --clear | Idempotent. |
| Check if Editor is in play mode | status | Returns state field (ready/compiling/playing/paused). |
| Enter / exit play mode | editor play [--wait] / editor stop | --wait blocks until fully entered. |
| Force recompile | editor refresh --compile | Waits until compile finishes or --timeout (60s default) elapses — raise --timeout for big projects, or use refresh_unity --compile request to fire-and-forget. |
| Trigger a menu item | menu "Window/General/Console" | File/Quit is blocked for safety. |
| Capture screenshot | screenshot [--view game] / screenshot --view game --annotate_ui / screenshot --annotations_only / screenshot --physics_only / screenshot --isolated --target /Player | Default scene view, 1920×1080. Game View evidence returns bounded uGUI or camera-visible 3D collider identities with explicit input/image coordinates; metadata-only modes skip PNG work. |
| Drive Unity input for QA | input inspect --path ... / input click --path ... / input keyboard --key space / call input --json '{"action":"sequence",...}' / bounded record + replay | Sends uGUI EventSystem events or optional Input System device state inside Unity. Neither is a physical OS click. |
| Run EditMode / PlayMode tests | test [--mode PlayMode] [--filter ...] | Filter by namespace, class, or full test name. |
| Profiler hierarchy snapshot | profiler hierarchy --depth N | Sort by self/total/calls, filter by --min ms. |
| Liveness probe (no Unity round-trip) | ping | Cheaper than status — heartbeat file only. |
| List all tools | list --compact or list --names | 30s in-memory + on-disk cache. Both forms return a flat names array; use list only when you need one-line descriptions. |
| Invoke a strict tool with typed JSON | call <tool> --json '{...}' or pipe JSON | Validates against the live strict schema before execution. Use --validate-only or --explain for no-execution checks. |
| Run multiple commands in one HTTP round-trip | batch --file <path.json> or pipe JSON | Sequential. fail_fast on first error by default. |
| Compile-check without executing | exec --check "<code>" | Returns success on clean compile, EXEC_COMPILE_ERROR otherwise. No side effects. |
| List loaded assemblies | list_assemblies [--filter <substr>] [--include_system] [--include_version] | Returns bare name strings by default; --filter to scope, --include_version for {name, version} objects. |
| Inspect a type's signature + known Unity pitfalls | describe_type <name> [--members methods] [--limit N] | Cheaper than exec reflection. |
| Search methods across assemblies by name | find_method <pattern> [--namespace ns] [--limit N] | Pattern is a substring; --limit defaults to 50. |
| Find / create / move project assets | manage_assets find --type Texture2D --filter icon / manage_assets create --type GameConfig --path Assets/Config/Game.asset | Compact AssetDatabase operations constrained to Assets/; create authors a ScriptableObject .asset (optional --params '{"properties":{...}}'). Use before falling back to exec for basic asset work. |
| Build a uGUI layout | manage_ui create --element panel --parent /Canvas | Creates Canvas/EventSystem scaffolding and uGUI elements; use manage_components for properties. |
| See overlay UI | screenshot --overlay --output_path built.png | Renders active ScreenSpaceOverlay canvases to PNG. |
| Anything else (read prop, custom C#) | exec "<code>" | Falls back here when no dedicated command exists. |
Compile-check only (validate syntax/types without executing):
hera-agent-unity exec "var x = SomeType.SomeMethod();" --check
Useful when you're not sure a refactor compiles before issuing a destructive call.
Input QA (input) — use this for Unity-level behavior when physical screen-coordinate automation is unavailable. EventSystem actions resolve targets by hierarchy path, instance ID, or normalized screen/canvas position, then dispatch uGUI events (click, pointer_down, pointer_up, submit, scroll, drag). Start with input state, then input inspect --path /Canvas/Button --details true. Projects with the optional Input System package may use input state --backend inputsystem, input keyboard --key space, input mouse --mode click --position 640,360, a strict call input --json '{"action":"sequence","steps":[...]}', or bounded record / replay actions in Play Mode. A sequence accepts 1..32 keyboard/mouse steps, validates the whole plan before mutation, and releases sequence-owned held controls on completion or failure. record writes a hera.input-recording/1 JSON file under the project or system temp directory; replay validates the full bounded file and reuses the same cleanup guarantees. Hera adds no Input System dependency, does not create devices, and releases held controls on Play Mode exit. Evidence should be classified precisely:
- Windows Git Bash: MSYS rewrites arguments beginning with
/as filesystem paths. Prefix hierarchy-path calls withMSYS_NO_PATHCONV=1, for exampleMSYS_NO_PATHCONV=1 hera-agent-unity input inspect --path /Canvas/Button. - Unity EventSystem input QA: PASS/FAIL based on
inputresults, console logs, state reads, Play Mode tests, or UI callbacks. - Unity Input System QA: PASS/FAIL based on keyboard/mouse command output and observed gameplay device state in Play Mode.
- Physical OS click QA: BLOCKED if Computer Use still cannot capture Unity screenshot state and Hera has no native OS/window input backend for that action.
Building a uGUI reference layout — use manage_ui create, manage_ui set_anchor, manage_ui set_rect, and manage_components in small verified batches. Use screenshot --overlay after each material layout pass. Keep fill bars start-anchored, center text within its container, and use real project art or an explicitly labeled placeholder for bespoke visuals.
3. Common Patterns (Cookbook)
Each pattern is the shortest viable form. Compose, don't copy whole blocks.
3.1 Inspect scene state in one call
hera-agent-unity scene info
Returns { active: {name, path, isDirty}, loaded: [...] }. Don't exec this — scene info is dedicated.
3.2 Create N GameObjects with consistent naming
hera-agent-unity exec "
var root = new GameObject(\"MyRoot\");
for (int i = 0; i < 50; i++)
new GameObject(\"Item_\" + i).transform.SetParent(root.transform, false);
return null;
"
Bulk creation is one exec. Don't loop the CLI.
3.3 Bulk-modify existing children
hera-agent-unity exec "
var parent = GameObject.Find(\"MyRoot\");
foreach (Transform t in parent.transform) t.position += new Vector3(0, 1, 0);
return null;
"
Note: GameObject.Find ignores inactive objects. If you SetActive(false) then Find, you get null and NullReferenceException. Use Resources.FindObjectsOfTypeAll<GameObject>().FirstOrDefault(g => g.name == "X") if you need inactive lookup.
3.4 Pipe code via stdin (avoid shell escape hell)
echo 'return Application.dataPath;' | hera-agent-unity exec
Or load from a file:
hera-agent-unity exec --file scripts/probe.cs
Positional / stdin / --file precedence: positional > stdin > --file.
3.5 Read just the most recent error
hera-agent-unity console --type error --lines 5
If empty, no errors. If you need the full stack of one entry, re-run with --stacktrace full.
3.6 Compile-check before a risky exec
hera-agent-unity exec --check "var x = MyType.MaybeRenamed();"
On EXEC_COMPILE_ERROR, fix and retry. No side effects on success — you still need a separate exec (without --check) to actually run.
3.7 Run several commands in one HTTP round-trip
echo '{"commands":[
{"command":"editor", "params":{"action":"refresh", "compile":true}},
{"command":"console", "params":{"type":"error", "lines":10}}
], "options":{"fail_fast":true}}' | hera-agent-unity batch
fail_fast: true (default) stops at the first failing step. Use fail_fast: false when you want every step attempted. Batch is sequential — no branching, no result piping between steps. For control flow, use one larger exec or chain CLI calls.
3.8 Inspect before you exec
When unsure about a Unity API method's signature, ask the connector instead of guessing:
hera-agent-unity describe_type UnityEditor.AssetDatabase --members methods --limit 30
hera-agent-unity find_method "Refresh" --namespace UnityEditor --limit 20
Costs a fraction of a wrong exec round-trip plus a stack trace.
4. Pitfalls
4.1 GameObject.Find is active-only
Find walks the active object graph. An object you just SetActive(false)'d is invisible to it; subsequent Find returns null → NullReferenceException on the next member access.
Workaround: Resources.FindObjectsOfTypeAll<GameObject>().FirstOrDefault(g => g.name == "X" && g.scene.IsValid()).
4.2 Cold csc compile is slow
The first exec per Unity session pays csc startup (5–15s on Windows). Subsequent unique exec bodies are ~500ms–2s (csc warm in OS cache). Identical bodies hit the in-memory assembly cache and skip compile entirely (~10ms). Don't infer the tool is broken from one slow first call.
4.3 Domain reload cancels in-flight HTTP
If your exec triggers a script recompile or asset import that causes a domain reload, the HTTP connection drops. hera-agent-unity auto-retries the request transparently (up to ~5s) but a connection that was mid-execute may complete after you get a disconnection message. Use editor refresh --compile explicitly when you need to be sure compilation is done before continuing.
4.4 --params JSON shape
When using --params '{"k":"v"}', explicit --k v flags override the JSON. Don't set the same key in both.
4.5 Cursor / bash $(...) / CI: no $null | workaround needed
In non-TTY shells, stdin is open but never delivers EOF. The CLI's stdin reader detects this (os.ModeNamedPipe + IsRegular guard) and skips the read, so you can call exec exactly like you would in an interactive terminal:
hera-agent-unity exec "return Application.productName;"
No $null | prefix. No </dev/null redirect. If you do see exec hang in Cursor or CI, you're on an outdated binary — run hera-agent-unity update.
4.6 console --clear cannot be undone
Logs cleared can't be recovered. If you're debugging, read first, then clear.
4.7 Custom tools must be in an Editor assembly
[HeraTool] classes only auto-register if their assembly is loaded by the Editor. Runtime-only assemblies are invisible. If list doesn't show your new tool, check that the file lives in an Editor/ folder or that the asmdef has Editor in includePlatforms.
4.8 The agent_hint field on stderr
Tools occasionally emit a one-line hint: to stderr when there's a non-obvious next action (e.g. "scene is dirty; save before close"). Read stderr alongside stdout — 2>&1 merges them.
4.9 humanCategories whitelist drives output mode
The CLI classifies commands as human-target (install / uninstall / status / update / doctor) or AI-target (everything else, including help and version). AI-target commands automatically emit compact JSON and suppress decorative stderr. If you author a new top-level command and add it to humanCategories, agents will get indented output for it — usually unintended.
4.10 batch has no conditional or data passing
By design. Each step's result is reported but not piped to the next step. If you need "do X only if Y succeeded", issue separate calls. Don't try to encode logic into batch JSON. fail_fast: true (default) is the only branching primitive.
4.11 list_assemblies without --filter returns hundreds of entries
Use --filter Unity / --filter MyGame / etc. to scope. Same for find_method — always pass --limit and --namespace when you can. The connector won't paginate; oversized responses just inflate your context.
4.12 return; (no value) does not compile in exec
Your snippet is wrapped in static object Execute() { ... }. A bare return; triggers CS0126 ("an object of a type convertible to 'object' is required"). Use return null; for early exits, or omit the return entirely (it falls through to null). throw also works and is preferred when the early exit represents a failure (exit non-zero via EXEC_RUNTIME_ERROR).
// Bad — CS0126
if (canvas == null) { Debug.LogError("missing"); return; }
// Good — explicit null
if (canvas == null) { Debug.LogError("missing"); return null; }
// Better when this is actually a failure — surfaces as EXEC_RUNTIME_ERROR
if (canvas == null) throw new System.Exception("BootCanvas not found");
4.13 PowerShell exec quoting
PowerShell's 'single quotes' do not interpret backslash escapes — bash-style \" lands as a literal \ in the snippet, then csc reports CS1056: Unexpected character '\\'. PowerShell's "double quotes" interpret $, backtick, and a few others as PowerShell syntax, so most C# snippets don't survive that either. Three patterns always work in PowerShell:
# 1. Multi-line, or contains quotes — stdin pipe + here-string (preferred)
@'
var iap = AssetDatabase.FindAssets("t:IAPRewardEntry").Length;
return iap;
'@ | hera-agent-unity exec
# 2. Short, single-line — single-quoted string, write " directly (no escaping)
hera-agent-unity exec 'return AssetDatabase.FindAssets("t:IAPRewardEntry").Length;'
# 3. Long or reusable — load from disk
hera-agent-unity exec --file scripts\probe.cs
Anti-patterns that fail in PowerShell:
hera-agent-unity exec "var x = ...; return x;"— PowerShell interprets$, backtick,;inside double quotes.hera-agent-unity exec 'var s = \"a\";'—\"is literal inside single quotes; csc rejects the\.$code = @'...'@in one shell call, thenhera-agent-unity exec $codein a separate shell call (e.g. agents that issue each command as a fresh PowerShell process) —$codeevaporates between calls. Chain inside one invocation:$code = @'...'@; hera-agent-unity exec $code.
bash equivalent: same idea, replace @'...'@ with <<'EOF' heredoc or single-quoted '...' string. Avoid \" unless the outer wrapper is "...".
4.14 Custom [HeraTool] namespace collisions
When you author a new tool under AgentConnector/Editor/Tools/, two type-name collisions reliably trigger CS0104 on Unity's compiler the moment both using System; and using UnityEditor; are in scope (which most tools need):
Object—System.ObjectvsUnityEngine.Object. Qualify destroys asUnityEngine.Object.Destroy(...)/UnityEngine.Object.DestroyImmediate(...), or alias once:using Object = UnityEngine.Object;.PackageInfo—UnityEditor.PackageInfo(legacy AssetStore type) vsUnityEditor.PackageManager.PackageInfo. Alias once:using PackageInfo = UnityEditor.PackageManager.PackageInfo;.
Other pairs worth aliasing pre-emptively when you reach for them: Random (System.Random vs UnityEngine.Random — different semantics) and Debug (System.Diagnostics.Debug vs UnityEngine.Debug). Grep for bare Object / PackageInfo / Random / Debug once before triggering the first compile to skip a hotfix round-trip.
4.15 PowerShell --params JSON quoting
The same shell-escape failure mode as §4.13 hits --params '{...}' payloads on PowerShell. The JSON inside must reach the CLI with raw double-quotes intact — PowerShell does not let you bash-style escape them, and the bash-style attempt silently produces invalid JSON in --params: invalid character '\\' ....
# Works — single-quoted outer string keeps " literal
hera-agent-unity manage_components set --component_id 12345 `
--params '{"property":"m_CenterOfMass","value":[0,1,0]}'
# Fails — backslash-escaped " survive into the JSON as literal '\"'
hera-agent-unity manage_components set --component_id 12345 `
--params "{\"property\":\"m_CenterOfMass\",\"value\":[0,1,0]}"
bash equivalent: same pattern — single-quoted outer is the safe form, no \" rewriting.
Or sidestep --params entirely for simple values by splitting the keys: --property m_CenterOfMass --value 0,1,0 ships the same Vector3 through the scalar-friendly flag (comma strings are accepted alongside JSON arrays for Vector2/3/4, Quaternion, Color, Vector2Int, Vector3Int). Reserve --params for nested envelopes that the scalar flags cannot represent — {"value": {"asset_path": "..."}}, {"value": {"instance_id": -12345}}, deeply-nested arrays.
5. Reference (skim on demand)
5.1 Major commands
| Command | Purpose | Key flags |
|---|---|---|
call <tool> | Validate and invoke a live strict tool contract | --json, --file, stdin, --profile, --validate-only, --explain |
exec <code> | Run C# in Editor | --usings, --check, --depth N, --stacktrace {none|user|full}, --strict, --no-cache, --security-mode full|restricted |
console | Read/clear log entries | --type error,warning,log, --lines N, --stacktrace, --clear, --since N |
scene info / load / save / close / list | Scene management | --mode single|additive|additive_without_loading (load) |
editor launch | restart | play | stop | pause | refresh | Editor lifecycle | exact --project, --hub-root (bootstrap); --wait (play); --compile, --force (refresh) |
menu "<path>" | Execute menu item | (none) |
screenshot | Capture view, overlay canvases, isolated target, or inspect bounded Game View UI/3D physics metadata | --view scene|game, --overlay, --annotate_ui, --annotations_only, --annotate_physics, --physics_only, --physics_grid_size, --max_physics_hits, --isolated, --target, --angles, --width, --height, --output_path |
test | Run tests or resume an existing run | --mode EditMode|PlayMode, --filter <ns.class>, --resume <run_id> |
task list / task status <task_id> | Inspect durable test/package work without contacting Unity | --project <full-path> or --port N |
profiler hierarchy | Profiler sample | --depth, --root, --frames, --min ms, --sort total|self|calls |
reserialize [paths...] | Force YAML reserialize | (no args = whole project) |
log "<msg>" | Write to Unity console | --level log|warning|error |
list | List registered tools (names → name+desc → schema) | --names / --compact (names only), --tool <name> (full schema) |
batch | Run multiple commands in one HTTP request | --file path.json, or pipe JSON; options.fail_fast |
list_assemblies | List loaded assembly names | --filter, --include_system, --include_version |
manage_assets | AssetDatabase file/folder operations + ScriptableObject authoring | find, mkdir, create, copy, move, delete |
describe_type <name> | Type info + Unity-pitfalls | --members fields|properties|methods|all, --limit N |
find_method <pat> | Search methods across assemblies | --namespace, --limit (default 50) |
asset-config set-csc <path> / set-dotnet <path> | Persist a default csc / dotnet path | (no flags) |
status / ping | Editor state / liveness | (none) |
doctor | Self-diagnostic | --json, --agent-rules (this guide's TL;DR subset) |
mcp | Unreleased experimental default-off stdio adapter | --profile, --exposure, --mrtr, --allow-arbitrary-code |
5.2 Response envelope
Every command returns this JSON over HTTP (the CLI then prints just data to stdout for compactness):
{
"success": true,
"message": "human-readable summary",
"code": "OPTIONAL_STABLE_ENUM", // present on errors; absent on most successes
"data": <command-specific>, // null for void ops (return null;)
"suggestions": ["next step", ...], // optional, on errors
"agent_hint": "one-line nudge", // optional, written to stderr by CLI
"timings": { "compile_ms": 12, "execute_ms": 3, "serialize_ms": 1 }
}
Common code values you might branch on:
EXEC_COMPILE_ERROR—data.compile_errors: [{line, col, error_code, message}, ...].lineis relative to the user snippet (1-based); errors that fall inside the internal wrapper (e.g. a bad--usingsnamespace) report the raw csc line as a fallback.EXEC_RUNTIME_ERROR—data.exception_type,data.stack_trace(user-filtered unless--stacktrace full). The synthetic wrapper frame is collapsed toat (your snippet)in user-filtered mode; pass--stacktrace fullto see the raw__CliDynamic.Executeframe.EXEC_LOGGED_ERROR—--strictmode only.data.logged_errors: [{type, message}, ...],data.returnedis the value the snippet would have returned.EXEC_RESTRICTED_SOURCE_DENIED/EXEC_RESTRICTED_METADATA_DENIED/EXEC_RESTRICTED_IL_DENIED—--security-mode restrictedrejected the snippet before compile, before assembly load, or after load but before invocation.data.stageanddata.violationidentify the failed layer.EXEC_CSC_NOT_FOUND/EXEC_DOTNET_NOT_FOUND—suggestions[]tells the user how to recoverEXEC_COMPILE_TIMEOUT— 30s csc timeoutUNKNOWN_COMMAND— typo'd command name.data.did_you_mean: [...]lists up to 3 commands within Levenshtein distance 2; act on the first match before re-runninglist --compact.READCONSOLE_INIT_FAILED— Unity internal API drift;data.unity_versionfor triage
5.3 Environment variables
Most have a --flag equivalent (column 2).
| Variable | Equivalent flag / effect |
|---|---|
HERA_AGENT_PORT=N | --port N |
HERA_AGENT_PROJECT=<path> | --project <path> |
HERA_AGENT_TIMEOUT_MS=N | --timeout N (default 60000) |
HERA_AGENT_QUIET=1 | --quiet |
HERA_AGENT_DEBUG=1 | --debug |
HERA_AGENT_COMPACT_JSON=1 | --compact-json |
HERA_AGENT_VERBOSE=1 | --verbose |
HERA_MCP_ENABLED=1 | Enable the experimental stdio MCP adapter. |
HERA_MCP_PROFILE=core | Select the explicit MCP Profile exposure profile. |
HERA_MCP_EXPOSURE=compact | Select compact, profile, or full. |
HERA_MCP_MRTR=1 | Enable negotiated Form elicitation approval. |
HERA_MCP_MAX_INLINE_BYTES=N | Positive complete inline MCP result limit (default 32768). |
HERA_AGENT_NARRATE=1 | --narrate |
HERA_AGENT_NO_PATH_CHECK=1 | Silence per-command PATH-mismatch warning (useful from wrapper binaries). |
GITHUB_TOKEN | Auth token for update from a private release repo. |
5.4 Output-control flags (pro-only)
The default for AI-target commands (§4.9) is already the quietest path: compact JSON to stdout, nothing to stderr unless there's a real error. Per-call overrides:
--quiet— Suppress decorative stderr (banners, progress). For when you want only the envelope.--verbose— Add per-phase timings + progress lines to stderr. For triaging slow calls.--debug— Dump full HTTP request/response bodies + discovery info. Wire-level detail; noisy.--narrate— ForcewaitForAliveprogress messages on AI-target commands. For cold-start triage.
5.5 What --depth actually does
Controls how deep exec's return-value serializer walks an object graph.
--depth | Behavior |
|---|---|
1 | Primitives + one level of fields/properties. Unity Objects → shallow {name, type, instanceID}. |
2 | Adds nested fields. Unity Objects still shallow. |
3 (default) | Full reflection on Unity Objects too. Use sparingly — Transform at depth 3 is ~9KB. |
8 | Hard maximum. |
If you find yourself wanting --depth 3 for a Transform, ask whether you really need transform.position etc. — usually returning the specific fields (return new { x = t.position.x, ... }) is both clearer and an order of magnitude cheaper.
6. When this doc is wrong
If something here contradicts what hera-agent-unity <cmd> --help says, trust --help. This guide is a curated subset, not the authoritative reference. The catalog at docs/COMMANDS.md is also authoritative for flag tables.
If you find a real bug or want to suggest a pattern, file an issue at https://github.com/NotNull92/hera-agent-unity/issues.
7. Developing hera-agent-unity itself (co-development)
Scope: this section is ONLY for AI agents building hera-agent-unity (its Go CLI + C# connector). It is NOT about using the CLI in your own Unity project — if you copied this
AGENTS.mdinto a downstream project to use hera, ignore this section; everything above still applies.
7.1 Canonical rule-document hierarchy
The repository has two hand-authored rule sources with separate responsibilities:
| File | Responsibility |
|---|---|
CLAUDE.md | Repository-development constitution, locked architecture decisions, and the completed-item ledger. |
AGENTS.md | Canonical cross-tool project rules and the source for distributable Hera agent guides. |
The following files are generated and must not be edited independently:
| Generated file | Derivation |
|---|---|
AGENT.md | Distributable usage guide generated from the user-facing portion of AGENTS.md. |
cmd/AGENT.md | Byte-identical copy of AGENT.md kept inside cmd/ for go:embed. |
.cursor/rules/hera-agent-unity.mdc | Cursor frontmatter plus the distributable guide. |
.github/copilot-instructions.md | Copilot stub pointing to AGENTS.md. |
GEMINI.md | AntiGravity entry stub pointing to AGENTS.md and the generated skill. |
.agents/agents.md | AntiGravity workspace handoff pointing to the canonical rules. |
.agents/skills/hera-agent-unity/SKILL.md | AntiGravity skill frontmatter plus the distributable guide. |
Regenerate or verify them from the repository root:
go run ./tools/sync-agent-guides
go run ./tools/sync-agent-guides --check
hera-agent-unity is co-developed by Claude (Claude Code) and Codex. The two agents collaborate to build one polished tool — one catches what the other misses:
- git history is the shared handoff channel. Codex tracks project state through
git commits, not chat context. So development work is committed with clear
conventional-commit messages, and larger features leave a
docs/CODEX_HANDOFF_*.mddoc so the next agent can pick up. - cross-verification: one agent implements, the other reviews/verifies. Neither self-approves its own work.
- both follow
CLAUDE.md(design intent, checklists, the locked "이미 처리된 항목" table) and thisAGENTS.md, and respect 🔒 locked design decisions. - accuracy over guesses: verify against the live Editor via
hera-agent-unity, and pull per-version facts from binary reflection rather than assuming Unity behavior. - anything adapted from outside ships fully hera-native. Studying an external
tool, paper, or article to design a hera capability is fine and encouraged — but
what lands in the repo must read as purpose-built Unity tooling, not as a port:
- no origin narration anywhere shipped or committed — not in tool
Descriptions, agent-rules strings, code comments,CLAUDE.md,CHANGELOG.md, or commit messages. Describe what the capability does, never where the idea came from. - no foreign frame of reference. Drop "ported/adapted/derived from", and drop comparisons to the source domain ("unlike the web", "the CSS equivalent"). State Unity facts directly — if the source domain has to be named to explain the rule, the rule has not been naturalized yet.
- re-derive, do not translate. Predicates, thresholds, and vocabulary are
re-authored against real Unity APIs (
manage_componentsproperties,unity_docsentries per version bucket) and verified live, so the result is correct for Unity rather than merely converted. - carry no foreign code or verbatim text. Ideas and methods are free to learn from; source files, data files, and copied prose are not — keep the repo clean of them so no third-party license terms attach to what hera ships.
- name things in hera's own vocabulary (
ui_slop,game_feel,unity_docs), never after the external tool.
- no origin narration anywhere shipped or committed — not in tool
- bundled knowledge and every agent-facing string is written in English. That
covers the
tools/build-*-docs/*.jsonlsources behindData/*.jsonl.gz.bytes,[HeraTool]descriptions,agent_hinttext,doctor --agent-rulessections, and response fields. The consumers are multilingual coding agents and the existing bundles are already English, so mixing languages only splits the surface. A tell whose subject is Korean typesetting still describes itself in English; Korean prose belongs in this repo's own docs (CLAUDE.md), not in shipped data. - verify facts against the binary, and only claim what the check covers.
Check version-specific behavior in the Editor that produced the evidence, and
phrase compatibility claims as the buckets actually measured. Do not lock a
claim in
CLAUDE.mdthat the evidence does not reach.
7.2 Connector UPM compatibility release gate (development only)
Any change to Connector C# code, asmdefs, package dependencies, test sources, or
AgentConnector/package.json requires a clean UPM-install compile check in all
supported compatibility buckets, not only the Unity version where the bug was
reported:
6000.0–6000.26000.3–6000.46000.5+
Use the exact representative Editor installations recorded in
docs/UNITY_EDITOR_VERSION_INVENTORY.md; update that inventory first when the
representative changes. A missing or unavailable bucket is BLOCKED, never a
PASS, and evidence from one bucket must not be described as full compatibility.
For every bucket, install the same Connector candidate as a normal UPM dependency
in a new disposable blank project, or a disposable project whose Library was
reset, then require all of the following:
- initial script compilation reaches
readywithin a bounded timeout; console --type errorreports zero compiler/package errors;- the normal
HeraAgent.Editorcompiler response contains zero sources underAgentConnector/Editor/Tests/, and noHeraAgent.Editor.Testsassembly is produced by the ordinary install; - a separate test-enabled pass compiles
HeraAgent.Editor.Testsindependently, after whichtestablesis removed from the normal-install manifest; and - a timeout, stuck
compilingstate, or abnormally long Roslyn/csc process is recorded as a release-blocking failure rather than hidden by retries.
This gate prevents the Connector 0.0.74 regression where test sources were folded
into the production assembly and long schema assertions caused pathological
Roslyn 4.10 compile times. It is repository-development policy only. Do not copy
it into doctor --agent-rules, AGENT.md, cmd/AGENT.md, examples/rules/, UPM
usage documentation, or any downstream user's Claude/Codex rules.
7.3 Feature admission gate (development only)
A new top-level tool, action, MCP profile exposure, or always-loaded agent-rule section needs evidence in the same change:
- Name the reproducible user failure or missing workflow it prevents.
- Explain why an existing tool action, flag, projection,
exec, or on-demand skill cannot solve it cleanly. - Add strict input/output and safety contracts plus regression tests.
- Record live Unity evidence when Editor behavior changes.
- Measure tool/action counts, profile payload, compact agent-rules size, new dependencies, and distribution impact.
- Regenerate and review
docs/metrics/catalog-payload-baseline.jsonwhen the canonical built-in catalog changes.
Use a disposable blank Unity project and run:
go run . --project $env:HERA_UNITY_PROJECT list --catalog `
--schema_version hera.tool-catalog/1 > catalog.json
go run ./tools/catalog-payload-report `
--catalog catalog.json `
--compare docs/metrics/catalog-payload-baseline.json `
--fail-on-change
A review_required comparison means the surface differs from the reviewed
baseline and needs an explicit decision. A built binary exits 3; go run
returns non-zero and prints exit status 3. This is not a blanket ban on useful
growth. Prefer adding an
action or flag to an existing coherent tool over creating another top-level
name. This section is repository-development policy only and must not enter the
generated downstream agent guides.