hera-agent-unity

August 18, 2026 · View on GitHub

You are an AI coding agent operating in a Unity project that has hera-agent-unity available 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.md at 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.md as 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 .mdc YAML frontmatter is the main case).

Recommended layout — multi-tool projects:

  1. Drop the full guide (or its lean subset) into AGENTS.md at the project root.
  2. For tools that need their own format, drop a short stub that defers to AGENTS.md:
    • CLAUDE.md> See AGENTS.md. (Claude Code reads AGENTS.md natively since late 2025)
    • .cursor/rules/hera-agent-unity.mdc → frontmatter + the same body (Cursor also supports plain AGENTS.md as a fallback)
    • .github/copilot-instructions.md → repository-wide pointer to AGENTS.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 body

Per-tool target paths (2026-current):

ToolCanonical pathNotes
OpenAI Codex / AGENTS.md-aware toolsAGENTS.mdCross-tool standard. Supports layering: ~/.codex/AGENTS.md → repo root → subtree → AGENTS.override.md.
Claude CodeAGENTS.md (or CLAUDE.md)Reads AGENTS.md natively. CLAUDE.md still 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.md with applyTo frontmatter; .github/skills/ for Agent Skills.
Google AntiGravityGEMINI.md, .agents/agents.md, .agents/skills/*/SKILL.md.agents/ is the native workspace extension directory for agents, workflows, and skills.
Continue.dev.continuerulesPlain markdown.
OtherTool-specific rules fileMost accept plain markdown.

Two ways to populate the target file:

  1. Static — copy the matching stub from examples/rules/ (one file per tool, already formatted).
  2. 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:


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:

  1. 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.
  2. hera-agent-unity status — confirms the active editor's port, project path, Unity version, PID, and current state (ready, compiling, …).
  3. 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:

  • doctor says binary missing → tell the user to install it (curl … | sh or the README link), do not proceed.
  • doctor says Unity is unreachable → tell the user to open the Editor with the UPM package, do not proceed.
  • status returns 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 because Execute() returns object. Write return null; for early exits, or throw 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-json or set HERA_AGENT_COMPACT_JSON=1 to force compact on a TTY too.

Full Access remains the exec default. Add --security-mode restricted only 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:

  1. Confirm the goal in one sentence.
  2. Observe only the needed current state in a compact way.
  3. Change code, scene, or Inspector state.
  4. Verify compile or state.
  5. Check console errors.
  6. Re-read only the changed target.
  7. If it failed, fix and repeat up to 1-2 times.
  8. 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:

  1. Split the goal into success criteria.
  2. Take a before-change state snapshot.
  3. Apply the change.
  4. Compile.
  5. Confirm console errors are 0.
  6. Re-read Inspector, GameObject, or asset state.
  7. Run PlayMode or Unity tests.
  8. If needed, capture a screenshot; use --overlay for ScreenSpaceOverlay canvases.
  9. Classify the failure cause and repeat.
  10. 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 …UseNotes
Active scene name / path / dirtyscene infoReturns active + loaded scenes in one shot.
Open / save / close a scenescene load <path> / scene save / scene closeModes: single (default), additive.
Read recent console errorsconsole --type errorDefault 20 entries; pagination via --lines + --since.
Clear consoleconsole --clearIdempotent.
Check if Editor is in play modestatusReturns state field (ready/compiling/playing/paused).
Enter / exit play modeeditor play [--wait] / editor stop--wait blocks until fully entered.
Force recompileeditor refresh --compileWaits 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 itemmenu "Window/General/Console"File/Quit is blocked for safety.
Capture screenshotscreenshot [--view game] / screenshot --view game --annotate_ui / screenshot --annotations_only / screenshot --physics_only / screenshot --isolated --target /PlayerDefault 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 QAinput inspect --path ... / input click --path ... / input keyboard --key space / call input --json '{"action":"sequence",...}' / bounded record + replaySends uGUI EventSystem events or optional Input System device state inside Unity. Neither is a physical OS click.
Run EditMode / PlayMode teststest [--mode PlayMode] [--filter ...]Filter by namespace, class, or full test name.
Profiler hierarchy snapshotprofiler hierarchy --depth NSort by self/total/calls, filter by --min ms.
Liveness probe (no Unity round-trip)pingCheaper than status — heartbeat file only.
List all toolslist --compact or list --names30s 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 JSONcall <tool> --json '{...}' or pipe JSONValidates against the live strict schema before execution. Use --validate-only or --explain for no-execution checks.
Run multiple commands in one HTTP round-tripbatch --file <path.json> or pipe JSONSequential. fail_fast on first error by default.
Compile-check without executingexec --check "<code>"Returns success on clean compile, EXEC_COMPILE_ERROR otherwise. No side effects.
List loaded assemblieslist_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 pitfallsdescribe_type <name> [--members methods] [--limit N]Cheaper than exec reflection.
Search methods across assemblies by namefind_method <pattern> [--namespace ns] [--limit N]Pattern is a substring; --limit defaults to 50.
Find / create / move project assetsmanage_assets find --type Texture2D --filter icon / manage_assets create --type GameConfig --path Assets/Config/Game.assetCompact 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 layoutmanage_ui create --element panel --parent /CanvasCreates Canvas/EventSystem scaffolding and uGUI elements; use manage_components for properties.
See overlay UIscreenshot --overlay --output_path built.pngRenders 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 with MSYS_NO_PATHCONV=1, for example MSYS_NO_PATHCONV=1 hera-agent-unity input inspect --path /Canvas/Button.
  • Unity EventSystem input QA: PASS/FAIL based on input results, 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 nullNullReferenceException 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, then hera-agent-unity exec $code in a separate shell call (e.g. agents that issue each command as a fresh PowerShell process) — $code evaporates 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):

  • ObjectSystem.Object vs UnityEngine.Object. Qualify destroys as UnityEngine.Object.Destroy(...) / UnityEngine.Object.DestroyImmediate(...), or alias once: using Object = UnityEngine.Object;.
  • PackageInfoUnityEditor.PackageInfo (legacy AssetStore type) vs UnityEditor.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

CommandPurposeKey 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
consoleRead/clear log entries--type error,warning,log, --lines N, --stacktrace, --clear, --since N
scene info / load / save / close / listScene management--mode single|additive|additive_without_loading (load)
editor launch | restart | play | stop | pause | refreshEditor lifecycleexact --project, --hub-root (bootstrap); --wait (play); --compile, --force (refresh)
menu "<path>"Execute menu item(none)
screenshotCapture 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
testRun 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 hierarchyProfiler 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
listList registered tools (names → name+desc → schema)--names / --compact (names only), --tool <name> (full schema)
batchRun multiple commands in one HTTP request--file path.json, or pipe JSON; options.fail_fast
list_assembliesList loaded assembly names--filter, --include_system, --include_version
manage_assetsAssetDatabase file/folder operations + ScriptableObject authoringfind, 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 / pingEditor state / liveness(none)
doctorSelf-diagnostic--json, --agent-rules (this guide's TL;DR subset)
mcpUnreleased 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_ERRORdata.compile_errors: [{line, col, error_code, message}, ...]. line is relative to the user snippet (1-based); errors that fall inside the internal wrapper (e.g. a bad --usings namespace) report the raw csc line as a fallback.
  • EXEC_RUNTIME_ERRORdata.exception_type, data.stack_trace (user-filtered unless --stacktrace full). The synthetic wrapper frame is collapsed to at (your snippet) in user-filtered mode; pass --stacktrace full to see the raw __CliDynamic.Execute frame.
  • EXEC_LOGGED_ERROR--strict mode only. data.logged_errors: [{type, message}, ...], data.returned is the value the snippet would have returned.
  • EXEC_RESTRICTED_SOURCE_DENIED / EXEC_RESTRICTED_METADATA_DENIED / EXEC_RESTRICTED_IL_DENIED--security-mode restricted rejected the snippet before compile, before assembly load, or after load but before invocation. data.stage and data.violation identify the failed layer.
  • EXEC_CSC_NOT_FOUND / EXEC_DOTNET_NOT_FOUNDsuggestions[] tells the user how to recover
  • EXEC_COMPILE_TIMEOUT — 30s csc timeout
  • UNKNOWN_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-running list --compact.
  • READCONSOLE_INIT_FAILED — Unity internal API drift; data.unity_version for triage

5.3 Environment variables

Most have a --flag equivalent (column 2).

VariableEquivalent 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=1Enable the experimental stdio MCP adapter.
HERA_MCP_PROFILE=coreSelect the explicit MCP Profile exposure profile.
HERA_MCP_EXPOSURE=compactSelect compact, profile, or full.
HERA_MCP_MRTR=1Enable negotiated Form elicitation approval.
HERA_MCP_MAX_INLINE_BYTES=NPositive complete inline MCP result limit (default 32768).
HERA_AGENT_NARRATE=1--narrate
HERA_AGENT_NO_PATH_CHECK=1Silence per-command PATH-mismatch warning (useful from wrapper binaries).
GITHUB_TOKENAuth 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 — Force waitForAlive progress 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.

--depthBehavior
1Primitives + one level of fields/properties. Unity Objects → shallow {name, type, instanceID}.
2Adds nested fields. Unity Objects still shallow.
3 (default)Full reflection on Unity Objects too. Use sparingly — Transform at depth 3 is ~9KB.
8Hard 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.md into 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:

FileResponsibility
CLAUDE.mdRepository-development constitution, locked architecture decisions, and the completed-item ledger.
AGENTS.mdCanonical cross-tool project rules and the source for distributable Hera agent guides.

The following files are generated and must not be edited independently:

Generated fileDerivation
AGENT.mdDistributable usage guide generated from the user-facing portion of AGENTS.md.
cmd/AGENT.mdByte-identical copy of AGENT.md kept inside cmd/ for go:embed.
.cursor/rules/hera-agent-unity.mdcCursor frontmatter plus the distributable guide.
.github/copilot-instructions.mdCopilot stub pointing to AGENTS.md.
GEMINI.mdAntiGravity entry stub pointing to AGENTS.md and the generated skill.
.agents/agents.mdAntiGravity workspace handoff pointing to the canonical rules.
.agents/skills/hera-agent-unity/SKILL.mdAntiGravity 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_*.md doc 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 this AGENTS.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_components properties, unity_docs entries 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.
  • bundled knowledge and every agent-facing string is written in English. That covers the tools/build-*-docs/*.jsonl sources behind Data/*.jsonl.gz.bytes, [HeraTool] descriptions, agent_hint text, doctor --agent-rules sections, 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.md that 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:

  1. 6000.06000.2
  2. 6000.36000.4
  3. 6000.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 ready within a bounded timeout;
  • console --type error reports zero compiler/package errors;
  • the normal HeraAgent.Editor compiler response contains zero sources under AgentConnector/Editor/Tests/, and no HeraAgent.Editor.Tests assembly is produced by the ordinary install;
  • a separate test-enabled pass compiles HeraAgent.Editor.Tests independently, after which testables is removed from the normal-install manifest; and
  • a timeout, stuck compiling state, 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:

  1. Name the reproducible user failure or missing workflow it prevents.
  2. Explain why an existing tool action, flag, projection, exec, or on-demand skill cannot solve it cleanly.
  3. Add strict input/output and safety contracts plus regression tests.
  4. Record live Unity evidence when Editor behavior changes.
  5. Measure tool/action counts, profile payload, compact agent-rules size, new dependencies, and distribution impact.
  6. Regenerate and review docs/metrics/catalog-payload-baseline.json when 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.