README
¶
unity-ctx
Go CLI that gives AI coding agents a token-safe read/write interface to Unity scenes, prefabs, and assets — no raw YAML, no silent corruption.
한국어 | English
Unity's serialization format is hostile to automation: a single scene file can exceed an agent's token budget, editing raw YAML by hand risks corrupt serialization, and there is no safe dry-run path. unity-ctx fixes this with a query-first command surface designed for AI agents.
Who is unity-ctx for?
unity-ctx is a context layer, not an editor replacement. Two distinct users:
- AI coding agents — automated callers that need compact context, unambiguous output prefixes, and dry-run-first mutation. unity-ctx is built for them.
- Human developers — keep using the Unity Editor for scene authoring. unity-ctx does not compete with the Editor; it makes the automated path safe.
If your agent is iterating on a Unity project, unity-ctx's whole job is making each read and write legible without blowing the token budget or corrupting a scene file.
Problems Solved
| Problem | Solution |
|---|---|
| Scene files exceed token budget | summarize and context-pack emit compact, token-bounded output |
| Raw YAML is unsafe to edit | All mutations are dry-run by default; --write required to commit |
| Object names are ambiguous | query resolves names to fileIDs; mutations target by fileID |
| Prefab GUID unknown | Returns UNKNOWN with NEED_PREFAB_GUID — never guesses |
| Prefab blast radius unknown | prefab impact scans all referencing scenes and prefabs before mutation |
| Placement position is guesswork | scene suggest ranks candidates near an anchor using a bounds manifest |
| No way to preview a scene write | scene diff summarizes a patch plan before scene apply commits it |
| Token cost of a file is unknown | bench measures raw vs summarize vs context-pack reduction |
Installation
From source (requires Go 1.22+):
go install github.com/Kubonsang/unity-ctx/cmd/unity-ctx@latest
Or build locally:
git clone https://github.com/Kubonsang/unity-ctx.git
cd unity-ctx
go build -o unity-ctx ./cmd/unity-ctx
No external runtime dependencies.
Building from source (v0.6+): unity-ctx depends on the unity-fileid-graph safety kernel (version pinned in
go.mod), fetched automatically from GitHub by the Go toolchain — no manual setup. The produced binary remains a single static executable with no runtime dependencies.
Supported File Types
| Extension | Namespace |
|---|---|
.unity |
scene |
.prefab |
prefab |
.asset, .mat |
asset |
Using with Your Own Unity Project
unity-ctx requires no configuration file. You pass file paths directly on the command line.
Read commands — point to any .unity, .prefab, or .asset file anywhere on disk:
unity-ctx scene summarize /Users/me/MyUnityProject/Assets/Scenes/GameLevel.unity
unity-ctx prefab inspect /Users/me/MyUnityProject/Assets/Prefabs/Player.prefab --component Rigidbody
unity-ctx asset get /Users/me/MyUnityProject/Assets/Configs/GameConfig.asset --field startingHealth
Mutation commands that need --project (prefab impact, prefab set) — pass the Unity project root, the directory that contains Assets/, ProjectSettings/, and Packages/. The prefab path must be under that root's Assets/ tree:
unity-ctx prefab impact Assets/Prefabs/Player.prefab \
--project /Users/me/MyUnityProject
unity-ctx prefab set Assets/Prefabs/Player.prefab \
--project /Users/me/MyUnityProject \
--id 11400000 --field moveSpeed --value 5.0
Getting a prefab's GUID — required for scene patch and scene suggest --out. meta guid reads it from the .meta file next to the prefab (and patch/suggest auto-resolve it the same way when --prefab-guid is omitted):
unity-ctx meta guid Assets/Prefabs/Chair.prefab --project /Users/me/MyUnityProject
# OK guid=abc123def456... file=... meta=...
Safety Integration (v0.6)
Every write path is validated by the unity-fileid-graph safety kernel — a lossless Unity YAML block parser plus fileID graph integrity checker — at three points:
pre_check target file before planning → ERROR blocks (BLOCKED, exit 3)
temp_check candidate bytes before commit → ERROR blocks (BLOCKED, exit 3)
--write atomic write with .bak backup
final_check re-read file after commit → ERROR reports WRITE_COMMITTED (exit 1) with the backup path
- A file that is already structurally broken (duplicate fileIDs, missing component blocks, mismatched back-references, ...) is never mutated.
WARNfindings (unknown class IDs, unsupported shapes) are surfaced on the summary line and inCHECKdetail lines but do not block.BLOCKEDis a safety verdict, not a tool failure — do not work around it by editing the YAML directly.refsexposes the kernel's PPtr/GUID reference evidence (unity-ctx prefab refs Enemy.prefab --json) for blast-radius analysis.
Placement pipeline — scene scan requires the Unity Editor to be running with the project open. All other placement commands (suggest, patch, diff, apply) work without the Editor:
# 1. With Editor open — generate bounds manifest
unity-ctx scene scan Assets/Scenes/GameLevel.unity \
--mode editor \
--project /Users/me/MyUnityProject \
--out /tmp/GameLevel.bounds.json
# 2–4. No Editor needed
unity-ctx scene suggest Assets/Scenes/GameLevel.unity \
--manifest /tmp/GameLevel.bounds.json \
--prefab Assets/Prefabs/Chair.prefab \
--near SpawnPoint_01 \
--prefab-guid abc123def456 \
--out /tmp/chair.patch.json
unity-ctx scene diff Assets/Scenes/GameLevel.unity --patch /tmp/chair.patch.json
unity-ctx scene apply Assets/Scenes/GameLevel.unity --patch /tmp/chair.patch.json --write
Paths passed to unity-ctx can be absolute or relative to your current working directory. Running commands from the project root (
cd /Users/me/MyUnityProject) lets you use shortAssets/...paths throughout.
Spatial geometry v0.9
scene scan --mode editor --geometry detailed asks the Unity Editor to emit Spatial Manifest v2: Collider-first compound local OBB drafts, arbitrary named contact frames (plus legacy bottom/back/top aliases), and planar surface candidates. Renderer fallbacks remain unreviewed. A scene compound is reviewed only when every collider is explicitly referenced by a reviewed Unity RoomSurface or a project-owned marker exposing SpatialReviewKind == "room-obstacle", Reviewed == true, and SourceColliders; this generic opt-in covers pillars without a UnityDecoScene dependency. Unsupported reviewed surfaces can prove solid obstacle geometry without becoming contact targets, and mixed reviewed/unreviewed collider compounds fail closed. scene check then proves rotated overlap and finite-surface contact gap/penetration/support. Assets with simultaneous requirements must map every stable requirement ID, for example --contact-surfaces floor=floor-main,wall=wall-north; a legacy single contact cannot approve a multi-contact profile. scene suggest aligns the chosen reviewed frame basis for both floor and wall surfaces and returns deterministic candidates. Manifest v1 remains supported for existing AABB workflows; contact requests against it return UNKNOWN NEED_GEOMETRY_V2.
The MCP server adds read-only unity_spatial_check and unity_suggest_wall tools. Unity remains the final scene authority, raw FBX files are not parsed by Go, and no mutation tool is added to MCP.
Human-reviewed Spatial Contracts
Reusable asset and SupportedBy interaction contracts are separate from scene snapshot manifests. They store normalized compound OBBs, named contact frames, simultaneous contact requirements, revisions, dependency hashes, capture hashes, deterministic evidence, and the local human review.
unity-ctx spatial validate Library/DungeonDecorator/SpatialDrafts/banner.spatial.json
unity-ctx spatial verify-approved Assets/SpatialContracts/Assets/<guid>.spatial.json --json
unity-ctx spatial diff --current Assets/SpatialContracts/Assets/<guid>.spatial.json --draft Library/DungeonDecorator/SpatialDrafts/banner.spatial.json
unity-ctx spatial apply --current Assets/SpatialContracts/Assets/<guid>.spatial.json --draft Library/DungeonDecorator/SpatialDrafts/banner.spatial.json
The public CLI can validate, diff, record non-approval feedback, and dry-run an apply. It cannot create Approved evidence or execute spatial apply --write. The local human-review bridge invokes unity-ctx review-bridge over a one-request stdin protocol with a short-lived Ed25519 grant bound to the exact action, draft content, current-file baseline, capture, reviewer, and destination. SupportedBy grants also bind subject and target geometry hashes resolved from independently ledger-authorized Asset contracts. The operation holds an external destination lock across baseline recheck, write verification, and receipt commit; competing drafts cannot both win. It persists nonce consumption and the original cryptographically verifiable grant receipt in the OS-account local-data directory, never reusable proof in tracked JSON. Later scene scan --contracts and spatial verify-approved re-verify that receipt before consuming an Approved contract; interaction verification also rejects changed dependency geometry with SUPPORT_CONTRACT_STALE. Unsigned legacy approval records must be reviewed again. MCP exposes neither approval nor mutation. See docs/COMMANDS.md for the v2 signing payload and protocol.
Surface Arrangement contracts
unity-ctx arrangement validate <file> requires and strictly validates the stored spec_hash. unity-ctx arrangement hash <file> instead computes the normalized replacement when an edited draft has a missing or stale hash. Canonicalization follows Unity's actual wire semantics: values pass through System.Single precision, numbers use at most six fixed decimals, member IDs use UTF-16 ordinal ordering, and JSON strings use the same HTML-safe escapes. Unicode and long IDs as well as any finite non-negative edge_margin accepted by Unity are therefore accepted here too.
Commands
unity-ctx scene summarize
Compact overview of a scene: object count, component types, PrefabInstance list.
unity-ctx scene summarize Assets/Scenes/Stage01.unity
unity-ctx prefab summarize Assets/Prefabs/Enemy.prefab
unity-ctx asset summarize Assets/Configs/EnemyConfig.asset
unity-ctx scene query
Filter objects by name, fileID, or component type. Use this to resolve a name to a fileID before targeting mutations.
unity-ctx scene query Stage01.unity --name Table_01
unity-ctx scene query Stage01.unity --id 1000
unity-ctx scene query Stage01.unity --type NavMeshAgent
Output:
FOUND id=1000 name=Table_01 type=GameObject
unity-ctx scene inspect
Component fields for a specific object.
unity-ctx scene inspect Stage01.unity --id 1000 --component Rigidbody
unity-ctx prefab inspect Enemy.prefab --component NavMeshAgent
unity-ctx asset inspect EnemyConfig.asset
unity-ctx scene get
Single field value by fileID.
unity-ctx scene get Stage01.unity --id 1000 --component Rigidbody --field mass
unity-ctx prefab get Enemy.prefab --component NavMeshAgent --field speed
unity-ctx asset get EnemyConfig.asset --field maxHealth
unity-ctx scene context-pack
Assembles a token-budgeted context bundle for an agent task. Emits OMITTED lines when the budget is exhausted.
unity-ctx scene context-pack Stage01.unity --task "place a chair near Table_01" --max-tokens 4000
unity-ctx bench
Measures token reduction: raw file vs summarize vs context-pack. Uses ceil(utf8_bytes / 4) as the token estimate — no external tokenizer required.
unity-ctx scene bench Assets/Scenes/Stage01.unity
unity-ctx scene bench Assets/Scenes/Stage01.unity --task "inspect placement safety"
context-pack is measured only when --task is provided.
Benchmarks
Real numbers from bench against the repo fixtures. Token estimate is
ceil(utf8_bytes / 4), so these are reproducible with no tokenizer:
unity-ctx scene bench testdata/scenes/simple_scene.unity \
--task "place a chair near Table_01"
unity-ctx prefab bench testdata/prefabs/enemy.prefab \
--task "inspect placement safety"
| Fixture | raw tokens | summarize | context-pack (--task) |
|---|---|---|---|
testdata/scenes/simple_scene.unity |
175 | 22 (−87%) | 50 (−71%) |
testdata/prefabs/enemy.prefab |
186 | 21 (−89%) | 36 (−81%) |
These fixtures are tiny (sub-1 KB, hand-authored), so the absolute token counts
are small — the point is the ratio. summarize already strips ~88% of the
tokens a raw read would cost, and context-pack stays token-bounded while
keeping task-relevant context. On real Unity scenes (tens of KB to multiple MB)
the same code path produces far larger reductions, because raw YAML grows with
object count while summarize/context-pack output stays compact.
unity-ctx scene scan
Generates a bounds manifest by querying the Unity Editor. Requires the Unity Editor to be running with the project open.
unity-ctx scene scan Stage01.unity \
--mode editor \
--project /Users/me/MyUnityProject \
--out /tmp/Stage01.bounds.json
# Limit to specific prefabs
unity-ctx scene scan Stage01.unity \
--mode editor \
--project /Users/me/MyUnityProject \
--prefabs Assets/Prefabs/Chair.prefab,Assets/Prefabs/Table.prefab \
--out /tmp/Stage01.bounds.json
Required: --mode editor, --project, --out
Output:
OK mode=editor project=/Users/me/MyUnityProject scene=Assets/Scenes/Stage01.unity out=/tmp/Stage01.bounds.json objects=12 prefabs=3 source=editor
unity-ctx scene suggest
Ranks placement candidates near an anchor object. Read-only — does not write the scene. With --out, also writes a patch artifact ready for diff and apply.
# Rank candidates
unity-ctx scene suggest Stage01.unity \
--manifest Stage01.bounds.json \
--prefab Assets/Prefabs/Chair.prefab \
--near Table_01 \
--align grid \
--count 4
# Write patch for the top candidate (v0.5d)
unity-ctx scene suggest Stage01.unity \
--manifest Stage01.bounds.json \
--prefab Assets/Prefabs/Chair.prefab \
--near Table_01 \
--prefab-guid abc-guid-123 \
--out chair.patch.json \
--pick 1
# Wall-backed furniture uses its approved wall and floor requirements
unity-ctx scene suggest Stage01.unity \
--manifest Stage01.spatial.json \
--prefab Assets/Props/Bookcase.fbx \
--align wall --surface-id wall-north \
--contact wall-backed --count 4
Required: --manifest, --prefab, and either --near or --align wall --surface-id ID.
Optional: --count (default 4, max 4), --align floor|grid|wall (default floor), wall-only --contact wall-backed|wall-mounted, --out, --pick (default 1), --prefab-guid.
--pick and --prefab-guid require --out.
Output:
OK manifest=Stage01.bounds.json prefab=Assets/Prefabs/Chair.prefab near=1000 align=floor count=4 candidates=4 clear=4 warn=0
CANDIDATE rank=1 direction=east position=1.4,0,0 status=OK overlap_ids=none anchor_id=1000 anchor_name=Table_01
CANDIDATE rank=2 direction=west position=-1.4,0,0 status=OK overlap_ids=none anchor_id=1000 anchor_name=Table_01
CANDIDATE rank=3 direction=north position=0,0,1.4 status=OK overlap_ids=none anchor_id=1000 anchor_name=Table_01
CANDIDATE rank=4 direction=south position=0,0,-1.4 status=OK overlap_ids=none anchor_id=1000 anchor_name=Table_01
PATCH_OUT rank=1 file=chair.patch.json status=WARN candidate_status=OK
PATCH_OUT statusis the patch planner result (anchor included in overlap check).candidate_statusis the suggest result (anchor excluded). They can differ for the same position —candidate_status=OKwithstatus=WARNis normal.
unity-ctx scene patch
Generates a prefab placement patch plan without writing to the scene.
unity-ctx scene patch Stage01.unity \
--op place_prefab \
--manifest Stage01.bounds.json \
--prefab Assets/Prefabs/Chair.prefab \
--position 5,0,0 \
--prefab-guid abc-guid-123
Required: --op place_prefab, --manifest, --prefab, --position
Optional: --prefab-guid, --project, --json
With --project, the prefab GUID is auto-resolved from <prefab>.meta when --prefab-guid is omitted. If it still cannot be resolved, the command returns UNKNOWN ... NEED_PREFAB_GUID — it never guesses. You can also look the GUID up explicitly with unity-ctx meta guid.
Output:
OK op=place_prefab manifest=Stage01.bounds.json prefab=Assets/Prefabs/Chair.prefab position=5,0,0 overlap_ids=none reserved_fileIDs=2002,2003
PLAN prefab_guid="abc-guid-123" append_ops=append:1:2002:GameObject,append:4:2003:Transform
unity-ctx scene diff
Summarizes a persisted patch plan. Always run this before apply.
unity-ctx scene diff Stage01.unity --patch chair.patch.json
Output:
OK patch=chair.patch.json op=place_prefab append_ops=2 reserved_fileIDs=2002,2003
WARN patch=chair.patch.json op=place_prefab overlap_ids=2000 append_ops=2 reserved_fileIDs=2002,2003
UNKNOWN patch=chair.patch.json op=place_prefab reason=NEED_PREFAB_GUID append_ops=2 reserved_fileIDs=2002,2003
unity-ctx scene apply
Applies a patch plan to the scene. Dry-run by default; UNKNOWN patches are refused.
# Dry-run (default)
unity-ctx scene apply Stage01.unity --patch chair.patch.json
# Commit
unity-ctx scene apply Stage01.unity --patch chair.patch.json --write
--write creates Stage01.unity.bak before modifying the file, then reparses the result and verifies appended fileIDs before reporting success.
Output:
DRY_RUN patch=chair.patch.json op=place_prefab append_ops=2 changed=1 verified=1
WRITE backup=Stage01.unity.bak patch=chair.patch.json op=place_prefab append_ops=2 changed=1 verified=1
unity-ctx scene reposition
Moves an existing scene object: rewrites a Transform's m_LocalPosition in
place. --id is the Transform fileID (class 4 or RectTransform 224), not
the GameObject. Dry-run by default.
unity-ctx scene reposition Stage01.unity --id 1001 --position 1.5,2,-3.4
unity-ctx scene reposition Stage01.unity --id 1001 --position 1.5,2,-3.4 --write
Output:
DRY_RUN id=1001 field=m_LocalPosition old=5,0,3 new=1.5,2,-3.4 changed=1 pre_check=OK temp_check=OK
WRITE backup=Stage01.unity.bak id=1001 field=m_LocalPosition old=5,0,3 new=1.5,2,-3.4 changed=1 verified=1 pre_check=OK temp_check=OK final_check=OK
Only the three axis numbers change — separators, comments, and every other byte
survive. A non-transform target (UNSUPPORTED_TARGET_CLASS) or a field that is
not exactly {x, y, z} of numbers (FIELD_NOT_VECTOR3) is refused rather than
mangled.
unity-ctx scene reparent (v2 patch)
Moves a Transform under a new parent — updates the target's m_Father and both
parents' m_Children atomically. Flows through the same patch → diff → apply
pipeline using a v2 ops[] patch. --new-parent 0 moves the target to the
scene root.
unity-ctx scene patch Stage01.unity --op reparent --id 4001 --new-parent 4002 --json > reparent.patch.json
unity-ctx scene diff Stage01.unity --patch reparent.patch.json
unity-ctx scene apply Stage01.unity --patch reparent.patch.json --write --ack-impact
A reparent that would create a cycle is refused before any write
(BLOCKED phase=plan code=WOULD_CREATE_CYCLE). With --project, apply also
reports inbound cross-file references (WARN REPARENT_HAS_INBOUND_REFS) —
informational only, since the moved fileID stays valid.
unity-ctx scene delete (v2 patch)
Removes a GameObject and its components from a scene (whole subtree with
--cascade), unlinking it from the parent's m_Children or the scene's
SceneRoots. --id is the GameObject fileID.
unity-ctx scene patch Stage01.unity --op delete --id 1001 --cascade --json > delete.patch.json
unity-ctx scene diff Stage01.unity --patch delete.patch.json
unity-ctx scene apply Stage01.unity --patch delete.patch.json --write --ack-impact --project /path/to/project
Deletes are guarded harder than reparent because removal dangles references:
deleting an object with children requires --cascade
(BLOCKED WOULD_ORPHAN_CHILDREN), prefab-instance content is never raw-deleted
(STRIPPED_IN_SUBTREE), a surviving same-file reference blocks
(IN_FILE_REFERENCED), and --write requires --project so every
committed delete is cross-file-verified — any inbound or unaccountable
reference blocks the write (BLOCKED code=CROSS_FILE_REFERENCED).
unity-ctx asset set
Sets a field value in a .asset or .mat file. Dry-run by default.
# Dry-run
unity-ctx asset set EnemyConfig.asset --field maxHealth --value 300
# Commit
unity-ctx asset set EnemyConfig.asset --field maxHealth --value 300 --write
# Target by fileID
unity-ctx asset set EnemyConfig.asset --id 11400000 --field moveSpeed --value 4.0 --write
Output:
DRY_RUN field=maxHealth old=200 new=300 type_hint=int changed=1
WRITE backup=EnemyConfig.asset.bak field=maxHealth old=200 new=300 type_hint=int changed=1 verified=1
OK field=maxHealth old=200 new=200 type_hint=int changed=0 verified=1
.bak is created only when a write actually changes the file. changed=0 returns success without touching the filesystem.
unity-ctx prefab impact
Scans which scenes and prefabs reference a prefab. Run this before prefab set to understand blast radius. Nested traversal is capped at depth 3.
unity-ctx prefab impact Assets/Prefabs/Enemy.prefab \
--project /Users/me/MyUnityProject
# Limit scope to specific scenes
unity-ctx prefab impact Assets/Prefabs/Enemy.prefab \
--project /Users/me/MyUnityProject \
--scenes Assets/Scenes/BossRoom.unity,Assets/Scenes/Stage01.unity
Required: --project
Optional: --scenes (comma-separated), --json
Output:
OK prefab=Assets/Prefabs/Enemy.prefab guid=abc123 scenes=2 scene_refs=3 prefabs=1 prefab_refs=2 nested_depth=1
SCENES Assets/Scenes/BossRoom.unity refs=1 fileIDs=4000 Assets/Scenes/Stage01.unity refs=2 fileIDs=1000,2000
PREFABS Assets/Prefabs/EnemyElite.prefab refs=2 fileIDs=3000,3001
When the depth cap is reached:
WARN IMPACT_DEPTH_LIMIT prefab=Assets/Prefabs/Enemy.prefab depth=3 more_possible=true
unity-ctx prefab set
Sets a prefab field value. Dry-run output includes an impact summary. Writes require --ack-impact.
# Dry-run (impact summary included automatically)
unity-ctx prefab set Assets/Prefabs/Enemy.prefab \
--project /Users/me/MyUnityProject \
--id 11400000 \
--field moveSpeed \
--value 4.0
# Commit
unity-ctx prefab set Assets/Prefabs/Enemy.prefab \
--project /Users/me/MyUnityProject \
--id 11400000 \
--field moveSpeed \
--value 4.0 \
--write --ack-impact
Required: --project, --id, --field, --value
Targeting is fileID-only. --name and --component are not supported for prefab set.
Output (dry-run):
DRY_RUN field=moveSpeed old=3.5 new=4.0 type_hint=float changed=1 impact_status=OK scenes=2 scene_refs=3 prefabs=1 prefab_refs=2 nested_depth=1 ack_required=1 pre_check=OK temp_check=OK
SCENES Assets/Scenes/BossRoom.unity refs=1 fileIDs=4000 Assets/Scenes/Stage01.unity refs=2 fileIDs=1000,2000
PREFABS Assets/Prefabs/EnemyElite.prefab refs=2 fileIDs=3000,3001
scene apply,prefab set, andasset setall carrypre_check/temp_check/final_checkfields and refuse unsafe writes — see Safety Integration above.
unity-ctx meta guid
Resolves a prefab/asset GUID from its sibling .meta file. Never guesses.
unity-ctx meta guid Assets/Prefabs/Chair.prefab --project /Users/me/MyUnityProject
Output:
OK guid=3e8a1f2b4c5d6e7f8a9b0c1d2e3f4a5b file=Assets/Prefabs/Chair.prefab meta=Assets/Prefabs/Chair.prefab.meta
NEED_PREFAB_GUID file=Assets/Prefabs/Chair.prefab reason=meta_not_found
unity-ctx refs
Read-only PPtr/GUID reference evidence for a file, backed by the safety kernel. Lets an agent trace what a file points at without reading raw YAML.
unity-ctx prefab refs Assets/Prefabs/Enemy.prefab
unity-ctx scene refs Assets/Scenes/Stage01.unity --json
Output:
OK refs file=Assets/Prefabs/Enemy.prefab count=2 warnings=0
REF block=1000 class=GameObject field=m_Component[0].component file_id=2000
REF block=3000 class=MonoBehaviour field=m_Script file_id=11500000 guid=a1b2c3d4e5f60718293a4b5c6d7e8f90 type=3
--json adds a refs payload with references[], a warnings count, and issues[] (warning detail).
unity-ctx validate
Read-only fileID graph integrity check — the same check that gates every write path, run on its own so an agent can confirm a file is sound before editing.
unity-ctx prefab validate Assets/Prefabs/Enemy.prefab
unity-ctx scene validate Stage01.unity --json
OK/WARN exit 0; ERROR (broken graph) exits 1.
unity-ctx changes
Structural diff of a file against its <file>.bak — what the last committed set/apply changed — by matching blocks on fileID.
unity-ctx asset changes EnemyConfig.asset
Reports ADDED/REMOVED/CHANGED per object. Exit 1 if no backup exists.
unity-ctx restore
Recover a file from its <file>.bak, undoing the last committed write. Atomic; reports the restored content's integrity (check=).
unity-ctx asset restore EnemyConfig.asset
unity-ctx deps
Forward asset dependencies: the GUIDs a file references, resolved to asset paths under --project. --out writes a Graphviz graph.
unity-ctx prefab deps Assets/Prefabs/Enemy.prefab --project /path/to/project
unity-ctx scene deps Stage01.unity --project . --out deps.dot
Unresolved GUIDs are reported as UNKNOWN, never guessed.
unity-ctx mcp
Run an MCP server over stdio so MCP hosts (Claude Code, etc.) call unity-ctx's read-only commands as native tools.
unity-ctx mcp
claude mcp add unity-ctx -- unity-ctx mcp
Exposes read-only tools (unity_summarize, unity_validate, unity_refs, unity_query, unity_get, unity_deps, unity_impact). Mutations stay behind the CLI's --write contract.
Output Prefixes
Every command produces a single-prefix first line. Automated callers can branch on the prefix without parsing the rest.
| Prefix | Meaning |
|---|---|
OK |
Success, no warnings |
WARN |
Success with a condition to review (e.g., placement overlap) |
ERROR |
Failure — command did not complete |
UNKNOWN |
Insufficient information to proceed (e.g., missing GUID) |
DRY_RUN |
Mutation previewed, no file written |
WRITE |
File written and verified |
FOUND |
Query matched at least one object |
OMITTED |
Token budget exhausted, content was skipped |
CANDIDATE |
One ranked placement suggestion |
PLAN |
Patch plan detail line |
PATCH_OUT |
Patch artifact written by suggest --out |
SCENES / PREFABS |
Impact analysis result lines |
BLOCKED |
Write refused by a graph-integrity failure (code=GRAPH_CHECK_FAILED) |
CHECK |
Per-phase safety-check detail line |
REF |
Reference evidence line from refs |
NEED_PREFAB_GUID |
GUID could not be resolved from .meta |
Exit codes: 0 = OK / WARN / UNKNOWN / NEED_PREFAB_GUID, 1 = ERROR, 2 = tool execution / usage error, 3 = BLOCKED. BLOCKED exits 3 — a distinct code so an agent never mistakes a safety refusal (file untouched) for a completed write; NEED_PREFAB_GUID stays 0 as a missing precondition, not a refusal.
Recommended Agent Flow
# Inspect a scene
unity-ctx scene summarize Stage01.unity
unity-ctx scene query Stage01.unity --name Table_01 # → get fileID
unity-ctx scene inspect Stage01.unity --id 1000 --component Rigidbody
# Place a prefab (GUID auto-resolved from .meta via --project)
unity-ctx scene scan Stage01.unity --mode editor --project /path/to/project --out stage01.bounds.json
unity-ctx scene suggest Stage01.unity --manifest stage01.bounds.json --prefab Chair.prefab --near 1000 --project /path/to/project --pick 1 --out chair.patch.json
unity-ctx scene diff Stage01.unity --patch chair.patch.json
unity-ctx scene apply Stage01.unity --patch chair.patch.json --write # runs pre/temp/final graph checks
# Modify a prefab field
unity-ctx prefab impact Enemy.prefab --project /path/to/project
unity-ctx prefab set Enemy.prefab --project /path/to/project --id 11400000 --field moveSpeed --value 4.0
unity-ctx prefab set Enemy.prefab --project /path/to/project --id 11400000 --field moveSpeed --value 4.0 --write --ack-impact
Design Principles
- No raw YAML in prompts — every command emits compact, structured text
- Dry-run first —
setandapplyrequire explicit--writeto modify files - UNKNOWN over guessing — uncertain states (
NEED_PREFAB_GUID,AMBIGUOUS_NAME) are reported, not assumed - fileID targeting — mutations target by fileID; name-based targeting emits
WARNorERROR AMBIGUOUS_NAME - Stable output — all commands produce deterministic output; tests may assert exact strings
- No external runtime dependencies — core commands require only Go 1.22+
Development
# Run all tests
go test ./...
# Build
go run ./cmd/unity-ctx --help
Documentation
| Audience | Document |
|---|---|
| Full command contract — flags, output, exit codes | docs/COMMANDS.md |
| AI agent using the CLI — operating manual | docs/AGENT-USAGE.md |
| AI agent contributing to the codebase | AGENTS.md |
| Testing guide | docs/TESTING.md |
| Roadmap | docs/ROADMAP.md |
Known Limitations
These are current gaps, documented honestly.
scene scan requires Unity Editor.
scan is the only command that requires a running Unity Editor instance. All other commands — including suggest, patch, diff, apply, impact, and all read commands — work without the Editor. If the Editor is not running, scan fails with ERROR SCAN_EDITOR_FAILED.
Wall suggestions require reviewed v2 evidence.
--align wall requires a reviewed SurfacePatch and an approved, GUID/dependency-hash-matched Spatial Contract. Missing or stale evidence returns UNKNOWN; it never falls back to AABB or an invented contact policy.
prefab set does not support --name or --component targeting.
Only --id (fileID) is accepted. Use prefab inspect to find the fileID first.
scene scan --mode only supports editor.
Standalone bounds generation without the Editor is not yet implemented.
Nested prefab traversal is capped at depth 3.
prefab impact and prefab set emit WARN IMPACT_DEPTH_LIMIT when the cap is reached. References beyond depth 3 may exist and are not reported.
scene reposition edits the raw Transform, not prefab-instance overrides.
For an object that is a prefab instance, the effective position lives in
PrefabInstance.m_Modifications; repositioning the raw Transform may have no
visual effect. Works as expected on plain (non-instance) scene objects.
Structural mutations are scene-only and one op per patch.
reposition/reparent/delete operate on .unity files only; a v2 patch
carries exactly one op. Reparent endpoints must be plain Transform (class 4)
blocks — RectTransform and prefab-instance (stripped) endpoints are refused.
Status
Currently at v0.9.1 — Human-reviewed Spatial Contracts: Spatial Manifest v2 adds reviewed surfaces, compound OBBs, contact checks, and deterministic wall suggestions; reusable Asset/Interaction contracts and Surface Arrangement specs add cryptographically verified human approval without exposing approval or mutation through MCP. Existing manifest v1 and dry-run-first workflows remain supported. See docs/ROADMAP.md for the full roadmap.
Next milestone: v1.0 Agent Harness Release — sample Unity project, CI examples, installer.
License
Apache 2.0 — see LICENSE.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
unity-ctx
command
|
|
|
internal
|
|
|
deps
Package deps resolves the external asset dependencies of a Unity file by mapping the GUIDs it references to asset paths within a project.
|
Package deps resolves the external asset dependencies of a Unity file by mapping the GUIDs it references to asset paths within a project. |
|
mcp
Package mcp exposes unity-ctx's read-only commands as a Model Context Protocol (MCP) server over stdio, so MCP hosts (Claude Code, etc.) can call them as native tools instead of shelling out.
|
Package mcp exposes unity-ctx's read-only commands as a Model Context Protocol (MCP) server over stdio, so MCP hosts (Claude Code, etc.) can call them as native tools instead of shelling out. |
|
safety
Package safety adapts the unity-fileid-graph safety kernel for unity-ctx write paths.
|
Package safety adapts the unity-fileid-graph safety kernel for unity-ctx write paths. |
|
version
Package version holds the build version string, overridable at link time via -ldflags "-X github.com/Kubonsang/unity-ctx/internal/version.Version=<tag>".
|
Package version holds the build version string, overridable at link time via -ldflags "-X github.com/Kubonsang/unity-ctx/internal/version.Version=<tag>". |
|
xref
Package xref scans a Unity project for inbound PPtr references to a set of fileIDs inside a target asset.
|
Package xref scans a Unity project for inbound PPtr references to a set of fileIDs inside a target asset. |