Complete command reference for the z3ed CLI tool, including all automation and AI-powered features.
z3ed rom readRead bytes from ROM at specified address.
Syntax:
Notes:
--data-format controls the hex/ascii view of bytes.--format controls the output structure (json/text).Examples:
Output:
z3ed rom writeWrite bytes to ROM at specified address.
Syntax:
Notes:
--sandbox writes to a sandbox copy instead of the original ROM.Examples:
z3ed rom infoShow basic ROM metadata.
Syntax:
Examples:
Copier headers: z3ed and yaze strip a 512-byte copier (SMC/SWC) header when the file size is a 32 KB multiple plus 0x200, or when the size is nonstandard and the SNES internal header is valid only 0x200 bytes into the file (for example LYRA-v038b.smc, 0x10083B bytes). A file whose internal header is valid in place is never stripped. rom info reports copier_header, copier_header_size, copier_header_detection (size_alignment, internal_header_offset, or none) and file_size; size/size_bytes and all ROM offsets exclude the header. Every save writes the header back in front of the data, so an in-place save (or Save As) keeps the file's layout, and saving an unedited ROM reproduces the file byte for byte.
z3ed rom diffCompare two ROM files.
Syntax:
Examples:
z3ed rom compareCompare a ROM against a baseline to identify differences.
Syntax:
Examples:
z3ed rom validateValidate ROM integrity and structure.
Syntax:
Examples:
Output:
z3ed rom generate-goldenGenerate a golden ROM file for testing.
Syntax:
Examples:
These commands operate directly on ROM data (no GUI required).
dungeon-describe-room --room <hex> [--include-objects]dungeon-list-sprites --room <hex>dungeon-list-objects --room <hex>dungeon-list-custom-collision --room <hex> [--tiles <hex,hex,...>] [--nonzero] [--all]dungeon-export-custom-collision-json --out <path> [--room <hex> | --rooms <hex,hex,...> | --all] [--report <path>] [--sandbox]dungeon-import-custom-collision-json --in <path> [--dry-run [--report <path>]] [--sandbox | --mock-rom] [--replace-all --force]dungeon-export-water-fill-json --out <path> [--room <hex> | --rooms <hex,hex,...> | --all] [--report <path>] [--sandbox]dungeon-import-water-fill-json --in <path> [--dry-run [--report <path>]] [--sandbox | --mock-rom] [--strict-masks]dungeon-minecart-audit [--room <hex> | --rooms <hex,hex,...> | --all] [--only-issues]dungeon-map --room <hex> [--layer <0|1|2>] *(overlays Oracle custom collision tiles when present)*dungeon-list-chests --room <hex>dungeon-get-entrance --entrance <hex> [--spawn]entrance-info --entrance <hex> [--spawn]dungeon-export-room --room <hex> --output <file>dungeon-place-sprite --room <hex> --id <hex> --x <int> --y <int> [--subtype <int>] [--layer <0|1>] [--manifest <path>] [--write]dungeon-place-object --room <hex> --id <hex> --x <int> --y <int> [--size <int>] [--layer <0|1|2>] [--manifest <path>] [--write]dungeon-remove-object --room <hex> --index <int> --expect-id <hex> --expect-x <int> --expect-y <int> --expect-size <int> --expect-layer <0|1|2> [--manifest <path>] [--write]dungeon-set-door-type --room <hex> --x <int> --y <int> --type <hex> --expect-type <hex> --manifest <path> [--write]dungeon-list-pot-items --room <hex>dungeon-set-pot-item --room <hex> --index <int> --expect-position <hex> --expect-item <hex> --item <hex> --manifest <path> [--write]dungeon-get-room-tiles --room <hex> *(stubbed)*dungeon-tile-types --room <hex> [--layer upper|lower|both] [--type <hex>] [--no-rows]dungeon-set-room-property --room <hex> --property <name> --value <value> [--manifest <path>]dungeon-tile-types prints the vanilla collision maps the game builds when a room loads on a new file: COLMAPA ($7F2000, from BG1) and COLMAPB ($7F3000, from BG2), 64x64 bytes each, from the room's drawn tilemaps and its blockset's tile attribute table ($7EFE00). Pits are type 0x20. matches lists each cell of --type (default 0x20) with the tile word it came from. The ZScream custom collision map is not merged in; see custom_collision_cells and dungeon-list-custom-collision. Run it on the patched ROM when the patch changes tile types. dungeon-render --overlays=tiletype paints the same pits (red: upper map, orange: lower map only).
dungeon-import-custom-collision-json compares decoded room maps before writing. Replaying an exact source reports zero changed_rooms and write_status/save_status as not-needed; it does not create a backup or save the active ROM after comparison. --sandbox still materializes its copy before the comparison.
dungeon-place-sprite is a dry-run unless --write is supplied. Both modes plan the actual sprite save and reload its result on a detached ROM. A shared or full stream requires a manifest defining dungeon_stream_regions.sprites with copy_on_write and allocator-owned space. Protected payload and pointer writes are blocked. The result reports storage_strategy, the source and planned stream PC offsets, and exact half-open write_ranges. Write mode uses a required backup and rolls back the caller's ROM if saving fails.
Sprite subtype values whose low three bits are 7 select the overlord format; they require an overlord ID (0x01-0x1A). In particular, an 0xA3 cart with subtype 7, 15, 23, or 31 is refused. Sprite-list terminators and hidden key markers are also refused as ordinary placements. This validation does not renumber cart tracks or change the game engine.
dungeon-place-object is a dry-run unless --write is supplied. Both modes execute the same immutable capacity preflight. In-place edits can proceed without a manifest, but a shared or growing object stream fails closed unless an explicit manifest defines dungeon_stream_regions.objects with the copy_on_write strategy and allocator-owned space. The manifest remains an ownership guard: protected current-stream, allocation, object-pointer, or door-pointer writes are rejected before either dry-run or write can mutate ROM bytes.
dungeon-remove-object uses the same dry-run, manifest, allocator, backup, and transaction path. It requires the stream index plus the expected ID, coordinates, size, and layer so stale automation fails before mutation. The command rejects table-backed objects and chest objects because removing those requires coordinated edits outside the ordinary room object stream.
dungeon-set-door-type changes only the fixed-width type byte of an existing door selected by exact tile coordinates. It is a dry-run unless --write is supplied. The command requires the expected current type as a compare-and-swap guard and an object-stream manifest using copy_on_write. Before either mode succeeds it verifies the runtime and manifest pointer tables agree, inventories all 296 object pointers, rejects selected-stream aliases/overlaps and any other room door pointer into that stream, cross-checks the room model against the raw door-pointer entry, and checks the exact one-byte write against manifest ownership. Write mode uses a one-byte write fence, required backup, pre-save model/raw readback, and atomic ROM replacement. The focused unit test independently reopens the saved ROM and validates the persisted door.
Use dungeon-describe-room first to discover each door's exact tile_x, tile_y, index, and raw type_id. Pass those tile coordinates and type_id to dungeon-set-door-type; the command reports the matched index and old/new symbolic names but never selects a door by display-name text.
dungeon-list-pot-items reports each existing entry's zero-based stream index, raw position, tile coordinates, item ID, and item-byte PC/SNES address. dungeon-set-pot-item changes only that fixed-width item byte. It is a dry-run unless --write is supplied and requires an exact index plus expected raw position/item compare-and-swap and a pot-item stream manifest. The setter's dry-run and write modes inventory the complete manifest pointer table and fail closed if any room has an invalid or malformed stream. They also reject selected stream aliases, overlaps, and pointers landing inside the selected stream, then cross-check the raw three-byte entries, room model, and 0xFFFF terminator. Write mode uses a one-byte write fence, required backup, pre-save readback, and atomic ROM replacement. It does not add, remove, reposition, or repack pot items.
dungeon-set-room-property accepts layout/layout_id values 0-7 and floor1/floor2 values 0-15. These properties are persisted in the two-byte object-stream header with per-field read/modify/write; unrelated bits and the object payload remain unchanged. A shared stream requires the same manifest-backed copy_on_write capability described above. The manifest is optional for compatibility, but when supplied it is enforced for every supported property before mutation. Palette, blockset, spriteset, effect, tag, and hole-warp changes validate the complete serializer footprint: the selected room's 14-byte header and its two-byte message ID. Successful non-mock writes require a completed destination backup before replacement.
The collision and water-fill JSON import commands validate without mutation when --dry-run is present. Without it, they immediately save the active ROM with a required backup. --sandbox redirects that save and backup to the sandbox copy; the source ROM and its directory remain unchanged. --mock-rom is an in-memory test mode and is rejected when combined with --sandbox. --report is accepted only with a non-sandbox --dry-run; write-mode and sandbox imports reject it before ROM or sandbox work. Report paths must not alias the active ROM, including through symlinks or hardlinks. Reports are staged in exclusive same-directory temporary files and published with rename/replace after a final path-identity check. Collision/water JSON exports use the same ROM-alias checks; when --out and --report are published together, a partial publication is rolled back before the command returns. Exports may use --sandbox; their explicit --out and --report artifacts must remain separate from both the sandbox ROM and its source ROM.
When --spawn is set, the ID must be 0x00-0x06. Both entrance commands report the dedicated spawn fields (quadrant, overworld_door_tilemap, and the linked regular entrance_id) and omit the regular-entrance-only door field.
Example:
Add --include-objects for a read-only objects array in ROM-stream order. Each record reports hexadecimal object_id, subtype (1, 2, or 3), x, y, size, and zero-based stream_index. The reported object_count then equals the array length. Lightable torches and pushable blocks are omitted because they come from separate global tables rather than the encoded room object stream. Without the flag, the legacy output and count are unchanged.
overworld-describe-map --screen <hex>overworld-get-tile --map <hex> --x <area_tile_x> --y <area_tile_y>overworld-set-tile --map <hex> --x <area_tile_x> --y <area_tile_y> --tile <hex> [--write] [--allow-project-rom]overworld-find-tile --tile <hex> [--map <hex>] [--world <light|dark|special>]overworld-list-warps [--screen <hex>]overworld-list-sprites [--screen <hex>] [--phase <0|1|2>]overworld-list-items --map <hex>overworld-get-entrance --entrance <hex>overworld-tile-stats [--map <hex>] [--world <light|dark|special>] [--tile <hex>] [--top <n>]overworld-render --screen <hex> --out <file.png> [--overlays <list>] [--phase <0|1|2>] [--scale <float>]overworld-add-sprite --screen <hex> --phase <0|1|2> --id <hex> --x <tile> --y <tile> [--replace-index <n>] [--write]overworld-move-sprite --screen <hex> --phase <0|1|2> --index <n> --x <tile> --y <tile> [--expect-id <hex>] [--write]overworld-remove-sprite --screen <hex> --phase <0|1|2> --index <n> [--expect-id <hex>] [--write]Example:
IDs (--map, --screen, --tile) are hex. --x/--y are decimal tile16 coordinates (a 0x prefix selects hex) relative to the top-left of the screen's parent area: 0-31 on a small area and 0-63 on each doubled axis of a large, wide, or tall area (overworld-describe-map reports area_tiles). A child screen of a large area resolves to its parent, so --map 0x41 --x 40 and --map 0x40 --x 40 name the same tile. Output reports the resolved parent_area, the screen holding the tile with screen_x/screen_y (0-31), and world_x/world_y (editor canvas tile). overworld-find-tile matches carry the same x/y, so they can be passed straight back.
overworld-tile-stats counts tile16 placements in the loaded world grids. Each screen is one 32x32 block of 1024 tiles, so a multi-screen area appears as its screens, each with its parent_area. Output: screens_scanned, total_tiles, unique_tiles; worlds (screens, total and unique tiles per world); screens (unique tiles and the most placed top_tile per screen); and tile_counts, most placed first, with the number of screens using each tile (--top, default 32; 0 lists every tile). --map (or --screen) limits the scan to one screen and --world to one world. --tile adds a tile total and a tile_count per screen; use overworld-find-tile for positions.
overworld-set-tile is dry-run by default. Both modes rebuild the tile32 table and compressed screens exactly as the editor's overworld save does (tile16 definitions are untouched), fail closed if a changed byte falls outside the overworld save regions (bytes_outside_save_ranges), reload the result, and fail with DATA_LOSS unless every world's tile grid reads back with only the requested change. The output reports changed_bytes/changed_ranges; the tile32 table is rebuilt, so one tile can move ~100 KB on an expanded ROM. --write then saves with a required backup and checks the file matches the verified image. It refuses a ROM inside an Oracle of Secrets checkout unless --allow-project-rom.
The game keeps one overworld sprite list per game state ("phase"): 0 = beginning, 1 = first part, 2 = second part. Vanilla picks them by $7EF3C5 (< 2, 2, >= 3) and only has phase-0 lists for the Light World. ZSCustomOverworld v3 gives every phase 160 entries. Oracle of Secrets (LoadOverworldSprites_Interupt + Oracle_CheckIfNight) uses phase 0 for GameState 0-1, phase 1 for GameState 2 in daytime, and phase 2 at night (GameState >= 2) or at GameState 3.
overworld-list-sprites still lists every phase by default; each entry now carries phase, phase_name, list_index, and tile (16px units within the parent area). --phase filters. --screen of a child resolves to its parent.
overworld-renderRenders the whole area containing --screen from current ROM data. A child of a large, wide, or tall area resolves to its parent. --out (alias --output) must not alias the ROM. Overlays: sprites, entrances, exits, holes, items, grid, all (all excludes grid). Labels: sprite ids in hex (<phase>:<id> when every phase is drawn; phase 0 green, 1 red, 2 blue), E<entrance>, X<exit room>, H<hole entrance>, I<item>. JSON output lists every marker with area-local pixel coordinates.
overworld-add-sprite, overworld-move-sprite, and overworld-remove-sprite edit exactly one phase list of one parent area. They are dry-run by default and print the list before/after, exact duplicate entries, the pointer slot, and every PC/SNES byte range to be written. The planner changes as few bytes as possible:
in-place: the list is not shared and does not grow.grow-in-place: the bytes after the list are unreferenced by any pointer.relocate: the list is copied to an unreferenced run and only this slot's pointer changes (other slots sharing the old list keep it).RESOURCE_EXHAUSTED): no unreferenced run fits; nothing is written.The region ends at the room sprite pointer table ($09:C298 operand), which ZScream may move below $09:D62E. --replace-index swaps an entry for the new sprite in one edit. --write applies through the fenced sprite writer, saves with a required backup, reopens the file, and fails with DATA_LOSS if the list does not read back or any byte outside the plan changed. --write refuses a ROM inside an Oracle of Secrets checkout (Roms/ next to Oracle_main.asm) unless --allow-project-rom is given.
gfx-sheet-inventory [--reserved <ids>] [--flagged <ids>] [--labels-csv <file>] [--out <file.json>] lists all 223 sheets with storage, empty 16x16 blocks, reserved/flagged state, and the blocksets, spritesets, overworld areas and rooms that use each sheet. Reserved and flagged sheets also come from the project's [graphics_sheets] section (--project-context) and the hack manifest.graphics-doctor [--sheet <id>] [--verbose] checks pointers, decoded sizes, overlapping sheets and group-table references.Example:
Project rules (.yaze file):
gui-place-tile --tile <hex> --x <x> --y <y>gui-click (--target <path> | --widget-key <key>) [--click-type <left|right|middle|double>]gui-type (--target <path> | --widget-key <key>) --text <text> [--clear-first]gui-wait [--condition <condition>] [--widget-key <key>] [--timeout-ms <ms>] [--poll-interval-ms <ms>]gui-assert [--condition <condition>] [--widget-key <key>]gui-discover-tool [--window <name>] [--type <type>]gui-summarize-widgetsgui-screenshot [--region <region>]z3ed test-listList available tests.
z3ed test-runExecute tests.
Syntax:
z3ed test-statusCheck test execution status.
resource-list --type <type>resource-search --query <text> [--type <type>]message-list, message-read, message-search --query <text>message-encode --text <text>, message-decode --hex <hex_bytes>message-export-org --output <path>, message-import-org --file <path>message-export-bundle --output <path> [--range <all|vanilla|expanded>]message-import-bundle --file <path> [--apply] [--strict] [--range <all|vanilla|expanded>] [--project <path>] (--apply requires the matching project)message-write --id <id> --text <text> --project <path>message-export-bin --output <path> [--range expanded]message-export-asm --output <path> [--range expanded]dialogue-list, dialogue-read, dialogue-search --query <text>hex-search --pattern <hex>message-import-bundle --apply fails closed unless --project identifies the active headerless ROM and provides a loaded Hack Manifest. Before changing either bank, the command validates every selected ID, the manifest's vanilla count and expanded bounds, the vanilla stream's single standalone [BANK], and the exact half-open ROM write ranges against the project write policy. A command argument such as [W:80] is not a bank switch. A successful apply creates a required backup, atomically replaces the ROM, reopens it independently, and reports readback_verified=true only after every planned byte matches. WebAssembly builds reject apply mode because browser storage cannot satisfy that durability contract.
Use scoped help directly from the CLI:
Agent commands are routed through the agent subcommand:
Use z3ed agent with no args for a full list of agent subcommands.
These commands connect to a running Mesen2-OoS instance via the local socket (/tmp/mesen2-<pid>.sock). They do not require a ROM argument. The debug alias is the recommended front door (Mesen2 is the default backend).
z3ed mesen-gamestateRead ALTTP game state from Mesen2.
Syntax:
Examples:
z3ed mesen-spritesList active sprites from Mesen2.
Syntax:
Examples:
z3ed mesen-cpuRead CPU register state.
Syntax:
Examples:
z3ed mesen-memory-readRead emulator memory.
Syntax:
Examples:
z3ed mesen-memory-writeWrite emulator memory (hex bytes).
Syntax:
Examples:
z3ed mesen-disasmDisassemble code at an address.
Syntax:
Examples:
z3ed mesen-traceFetch execution trace.
Syntax:
Examples:
z3ed mesen-breakpointManage breakpoints.
Syntax:
Examples:
z3ed mesen-controlControl emulation state.
Syntax:
Examples:
AI workflows are available via the z3ed agent commands (see Agent CLI).
These options work with all commands:
--rom <file> - Specify ROM file to use--sandbox - Run ROM commands against a sandbox copy--format <json|text> - Output format--data-format <hex|ascii|both> - Byte view for read commands--tui / --interactive - Removed (use z3ed help and command-scoped --help)--list-commands - List all available commands--export-schemas - Export command schemas as JSON--version - Show version information--self-test - Run CLI self-test--verbose - Enable verbose output--quiet - Suppress non-error output--ai_provider <name> - AI provider (auto, ollama, gemini, openai, anthropic, mock)--ai_model <name> - Provider-specific model override--gemini_api_key <key> - Gemini API key--anthropic_api_key <key> - Anthropic API key--ollama_host <url> - Ollama host URL--openai_base_url <url> - OpenAI-compatible base URL (LM Studio: http://localhost:1234)--gui_server_address <host:port> - GUI automation server--prompt_version <name> - Prompt version override--use_function_calling <true|false> - Enable Gemini function calling--mesen-socket <path> - Override Mesen2 socket path--help - Show help for command--http-port <port> - HTTP API port (if enabled in build)--http-host <host> - HTTP API display host (printed URLs only)Environment variable shortcuts for AI providers: GEMINI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENAI_BASE_URL/OPENAI_API_BASE, OLLAMA_HOST, OLLAMA_MODEL.
0 - Success1 - Error (invalid args, missing ROM, failed command)