#include "cli/modern_cli.h" #include #include #include "absl/flags/flag.h" #include "absl/flags/declare.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "app/core/asar_wrapper.h" #include "app/rom.h" #include "cli/z3ed_ascii_logo.h" ABSL_DECLARE_FLAG(std::string, rom); ABSL_DECLARE_FLAG(bool, quiet); namespace yaze { namespace cli { ModernCLI::ModernCLI() { SetupCommands(); } void ModernCLI::SetupCommands() { commands_["patch apply-asar"] = { .name = "patch apply-asar", .description = "Apply Asar 65816 assembly patch to ROM", .usage = "z3ed patch apply-asar [--rom=] [--output=]", .handler = [this](const std::vector& args) -> absl::Status { return HandleAsarPatchCommand(args); } }; commands_["patch apply-bps"] = { .name = "patch apply-bps", .description = "Apply BPS patch to ROM", .usage = "z3ed patch apply-bps [--rom=] [--output=]", .handler = [this](const std::vector& args) -> absl::Status { return HandleBpsPatchCommand(args); } }; commands_["patch extract-symbols"] = { .name = "patch extract-symbols", .description = "Extract symbols from assembly file", .usage = "z3ed patch extract-symbols ", .handler = [this](const std::vector& args) -> absl::Status { return HandleExtractSymbolsCommand(args); } }; commands_["rom info"] = { .name = "rom info", .description = "Show ROM information", .usage = "z3ed rom info [--rom=]", .handler = [this](const std::vector& args) -> absl::Status { return HandleRomInfoCommand(args); } }; commands_["agent"] = { .name = "agent", .description = "πŸ€– AI-Powered Conversational Agent for ROM Inspection\n" " Interact naturally with Zelda3 ROM data using embedded labels\n" " ✨ Features: Natural language queries, automatic label lookup, context-aware responses", .usage = "\n" "╔═══════════════════════════════════════════════════════════════════════════╗\n" "β•‘ πŸ€– AI AGENT COMMANDS β•‘\n" "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n" "\n" "πŸ’¬ CONVERSATIONAL MODE (Recommended for testing):\n" " z3ed agent test-conversation [--rom=] [--verbose] [--file=]\n" " β†’ Interactive AI testing with all embedded Zelda3 labels\n" " β†’ Ask about rooms, sprites, entrances, items naturally\n" " β†’ Example: 'What sprites are in room 5?' or 'List all dungeons'\n" "\n" "πŸ’‘ SIMPLE CHAT MODE (Multiple input methods):\n" " # Single message\n" " z3ed agent simple-chat \"\" --rom=\n" " \n" " # Interactive session\n" " z3ed agent simple-chat --rom=\n" " \n" " # Piped input\n" " echo \"What is room 5?\" | z3ed agent simple-chat --rom=\n" " \n" " # Batch file (one question per line)\n" " z3ed agent simple-chat --file=questions.txt --rom=\n" "\n" "🎯 ADVANCED CHAT MODE:\n" " z3ed agent chat \"\" [--host=] [--port=]\n" " β†’ Full conversational AI with Ollama/Gemini\n" " β†’ Supports multi-turn conversations\n" "\n" "πŸ“Š TEST MANAGEMENT:\n" " z3ed agent test run --prompt \"\" [--timeout=]\n" " z3ed agent test status --test-id= [--follow]\n" " z3ed agent test list [--category=] [--status=]\n" " z3ed agent test results --test-id= [--format=yaml|json]\n" " z3ed agent test suite \n" "\n" "πŸ” OTHER COMMANDS:\n" " z3ed agent gui discover [--window=] [--type=]\n" " z3ed agent describe [--resource=] [--format=json|yaml]\n" "\n" "🏰 EMBEDDED LABELS (Always Available):\n" " β€’ 296+ room names β€’ 256 sprite names β€’ 133 entrance names\n" " β€’ 100 item names β€’ 160 overworld maps β€’ 48 music tracks\n" " β€’ 60 tile types β€’ 26 overlord names β€’ 32 graphics sheets\n" "\n" "πŸ“š No separate labels file needed - all names built into z3ed!\n", .handler = [this](const std::vector& args) -> absl::Status { return HandleAgentCommand(args); } }; commands_["chat"] = { .name = "chat", .description = "Unified chat entrypoint with text, markdown, or JSON output", .usage = "z3ed chat [--mode=simple|gui|test] [--format=text|markdown|json|compact]" " [--prompt \"\"] [--file=] [--quiet]", .handler = [this](const std::vector& args) -> absl::Status { return HandleChatEntryCommand(args); } }; commands_["proposal"] = { .name = "proposal", .description = "Review and manage AI-generated change proposals", .usage = "z3ed proposal [options]", .handler = [this](const std::vector& args) -> absl::Status { return HandleProposalCommand(args); } }; commands_["collab"] = { .name = "collab", .description = "🌐 Collaboration Server Management\n" " Launch and manage the WebSocket collaboration server for networked sessions", .usage = "\n" "╔═══════════════════════════════════════════════════════════════════════════╗\n" "β•‘ 🌐 COLLABORATION SERVER COMMANDS β•‘\n" "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n" "\n" "πŸš€ SERVER MANAGEMENT:\n" " z3ed collab start [--port=]\n" " β†’ Start the WebSocket collaboration server\n" " β†’ Default port: 8765\n" " β†’ Server will be accessible at ws://localhost:\n" "\n" "πŸ“Š SERVER STATUS:\n" " z3ed collab status\n" " β†’ Check if collaboration server is running\n" " β†’ Show active sessions and participants\n" "\n" "πŸ’‘ USAGE:\n" " 1. Start server: z3ed collab start\n" " 2. In YAZE GUI: Debug β†’ Agent Chat\n" " 3. Select 'Network' mode and connect to ws://localhost:8765\n" " 4. Host or join a session to collaborate!\n", .handler = [this](const std::vector& args) -> absl::Status { return HandleCollabCommand(args); } }; commands_["widget"] = { .name = "widget", .description = "Discover GUI widgets exposed through automation APIs", .usage = "z3ed widget discover [--window=] [--type=]", .handler = [this](const std::vector& args) -> absl::Status { return HandleWidgetCommand(args); } }; commands_["widget discover"] = { .name = "widget discover", .description = "Inspect UI widgets using the automation service", .usage = "z3ed widget discover [--window=] [--type=]" " [--format=text|json]", .handler = [this](const std::vector& args) -> absl::Status { return HandleWidgetCommand(args); } }; commands_["project build"] = { .name = "project build", .description = "Build the project and create a new ROM file", .usage = "z3ed project build", .handler = [this](const std::vector& args) -> absl::Status { return HandleProjectBuildCommand(args); } }; commands_["project init"] = { .name = "project init", .description = "Initialize a new z3ed project", .usage = "z3ed project init ", .handler = [this](const std::vector& args) -> absl::Status { return HandleProjectInitCommand(args); } }; commands_["rom generate-golden"] = { .name = "rom generate-golden", .description = "Generate a golden file from a ROM for regression testing", .usage = "z3ed rom generate-golden ", .handler = [this](const std::vector& args) -> absl::Status { return HandleRomGenerateGoldenCommand(args); } }; commands_["rom diff"] = { .name = "rom diff", .description = "Compare two ROM files and show the differences", .usage = "z3ed rom diff ", .handler = [this](const std::vector& args) -> absl::Status { return HandleRomDiffCommand(args); } }; commands_["rom validate"] = { .name = "rom validate", .description = "Validate the ROM file", .usage = "z3ed rom validate [--rom=]", .handler = [this](const std::vector& args) -> absl::Status { return HandleRomValidateCommand(args); } }; commands_["dungeon export"] = { .name = "dungeon export", .description = "Export dungeon data to a file", .usage = "z3ed dungeon export --to ", .handler = [this](const std::vector& args) -> absl::Status { return HandleDungeonExportCommand(args); } }; commands_["dungeon list-objects"] = { .name = "dungeon list-objects", .description = "List all objects in a dungeon room", .usage = "z3ed dungeon list-objects ", .handler = [this](const std::vector& args) -> absl::Status { return HandleDungeonListObjectsCommand(args); } }; commands_["gfx export-sheet"] = { .name = "gfx export-sheet", .description = "Export a graphics sheet to a file", .usage = "z3ed gfx export-sheet --to ", .handler = [this](const std::vector& args) -> absl::Status { return HandleGfxExportCommand(args); } }; commands_["gfx import-sheet"] = { .name = "gfx import-sheet", .description = "Import a graphics sheet from a file", .usage = "z3ed gfx import-sheet --from ", .handler = [this](const std::vector& args) -> absl::Status { return HandleGfxImportCommand(args); } }; commands_["command-palette"] = { .name = "command-palette", .description = "Show the command palette", .usage = "z3ed command-palette", .handler = [this](const std::vector& args) -> absl::Status { return HandleCommandPaletteCommand(args); } }; commands_["palette"] = { .name = "palette", .description = "Manage palette data (export/import)", .usage = "z3ed palette [options]", .handler = [this](const std::vector& args) -> absl::Status { return HandlePaletteCommand(args); } }; commands_["palette export"] = { .name = "palette export", .description = "Export a palette to a file", .usage = "z3ed palette export --group --id --to ", .handler = [this](const std::vector& args) -> absl::Status { return HandlePaletteExportCommand(args); } }; commands_["palette import"] = { .name = "palette import", .description = "Import a palette from a file", .usage = "z3ed palette import --group --id --from ", .handler = [this](const std::vector& args) -> absl::Status { return HandlePaletteImportCommand(args); } }; commands_["overworld get-tile"] = { .name = "overworld get-tile", .description = "Get a tile from the overworld", .usage = "z3ed overworld get-tile --map --x --y ", .handler = [this](const std::vector& args) -> absl::Status { return HandleOverworldGetTileCommand(args); } }; commands_["overworld find-tile"] = { .name = "overworld find-tile", .description = "Search overworld maps for a tile ID", .usage = "z3ed overworld find-tile --tile [--map ] [--world light|dark|special] [--format json|text]", .handler = [this](const std::vector& args) -> absl::Status { return HandleOverworldFindTileCommand(args); } }; commands_["overworld describe-map"] = { .name = "overworld describe-map", .description = "Summarize metadata for an overworld map", .usage = "z3ed overworld describe-map --map [--format json|text]", .handler = [this](const std::vector& args) -> absl::Status { return HandleOverworldDescribeMapCommand(args); } }; commands_["overworld list-warps"] = { .name = "overworld list-warps", .description = "List overworld entrances and holes with coordinates", .usage = "z3ed overworld list-warps [--map ] [--world light|dark|special] [--type entrance|hole|exit|all] [--format json|text]", .handler = [this](const std::vector& args) -> absl::Status { return HandleOverworldListWarpsCommand(args); } }; commands_["overworld set-tile"] = { .name = "overworld set-tile", .description = "Set a tile in the overworld", .usage = "z3ed overworld set-tile --map --x --y --tile ", .handler = [this](const std::vector& args) -> absl::Status { return HandleOverworldSetTileCommand(args); } }; commands_["sprite create"] = { .name = "sprite create", .description = "Create a new sprite", .usage = "z3ed sprite create --name [options]", .handler = [this](const std::vector& args) -> absl::Status { return HandleSpriteCreateCommand(args); } }; } void ModernCLI::ShowHelp() { std::cout << GetColoredLogo() << std::endl; std::cout << std::endl; std::cout << "\033[1m\033[36mUSAGE:\033[0m" << std::endl; std::cout << " z3ed [options] [arguments]" << std::endl; std::cout << std::endl; std::cout << "\033[1m\033[36mGLOBAL OPTIONS:\033[0m" << std::endl; std::cout << " --tui Launch interactive Text User Interface" << std::endl; std::cout << " --rom= Specify ROM file path" << std::endl; std::cout << " --verbose, -v Show detailed debug output" << std::endl; std::cout << " --version Show version information" << std::endl; std::cout << " --help, -h Show this help message" << std::endl; std::cout << std::endl; // Categorize commands std::cout << "\033[1m\033[36mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mπŸ€– AI Agent\033[0m" << std::endl; std::cout << " chat Unified chat entrypoint (text/json/markdown)" << std::endl; std::cout << " agent simple-chat Natural language ROM queries" << std::endl; std::cout << " agent test-conversation Interactive testing mode" << std::endl; std::cout << " \033[90mβ†’ z3ed help chat, z3ed help agent\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1m🧠 Proposals\033[0m" << std::endl; std::cout << " proposal run Execute AI-driven sandbox plan" << std::endl; std::cout << " proposal list Show pending proposals" << std::endl; std::cout << " proposal diff Review latest sandbox diff" << std::endl; std::cout << " proposal accept Apply sandbox changes" << std::endl; std::cout << " \033[90mβ†’ z3ed help proposal\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mπŸͺŸ GUI Automation\033[0m" << std::endl; std::cout << " widget discover Inspect GUI widgets via automation" << std::endl; std::cout << " \033[90mβ†’ z3ed help widget\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mπŸ”§ ROM Patching\033[0m" << std::endl; std::cout << " patch apply-asar Apply Asar 65816 assembly patch" << std::endl; std::cout << " patch apply-bps Apply BPS binary patch" << std::endl; std::cout << " patch extract-symbols Extract symbols from assembly" << std::endl; std::cout << " \033[90mβ†’ z3ed help patch\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mπŸ“¦ ROM Operations\033[0m" << std::endl; std::cout << " rom info Display ROM information" << std::endl; std::cout << " rom diff Compare two ROM files" << std::endl; std::cout << " rom generate-golden Create golden test file" << std::endl; std::cout << " \033[90mβ†’ z3ed help rom\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mπŸ—ΊοΈ Overworld\033[0m" << std::endl; std::cout << " overworld get-tile Get tile at coordinates" << std::endl; std::cout << " overworld set-tile Place tile at coordinates" << std::endl; std::cout << " overworld find-tile Search for tile occurrences" << std::endl; std::cout << " overworld describe-map Show map metadata" << std::endl; std::cout << " overworld list-warps List entrances and exits" << std::endl; std::cout << " \033[90mβ†’ z3ed help overworld\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1m🏰 Dungeon\033[0m" << std::endl; std::cout << " dungeon export Export dungeon data" << std::endl; std::cout << " dungeon import Import dungeon data" << std::endl; std::cout << " \033[90mβ†’ z3ed help dungeon\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1m🎨 Graphics\033[0m" << std::endl; std::cout << " gfx export-sheet Export graphics sheet" << std::endl; std::cout << " gfx import-sheet Import graphics sheet" << std::endl; std::cout << " palette export Export palette data" << std::endl; std::cout << " palette import Import palette data" << std::endl; std::cout << " \033[90mβ†’ z3ed help gfx, z3ed help palette\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1m\033[36mQUICK START:\033[0m" << std::endl; std::cout << " z3ed --tui" << std::endl; std::cout << " z3ed chat \"What is room 5?\" --rom=zelda3.sfc --format=markdown" << std::endl; std::cout << " z3ed patch apply-asar patch.asm --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << "\033[90mFor detailed help: z3ed help \033[0m" << std::endl; } void ModernCLI::PrintTopLevelHelp() const { const_cast(this)->ShowHelp(); } void ModernCLI::PrintCategoryHelp(const std::string& category) const { const_cast(this)->ShowCategoryHelp(category); } void ModernCLI::PrintCommandSummary() const { const_cast(this)->ShowCommandSummary(); } void ModernCLI::ShowCategoryHelp(const std::string& category) { std::cout << GetColoredLogo() << std::endl; std::cout << std::endl; if (category == "agent") { std::cout << "\033[1m\033[36mπŸ€– AI AGENT COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mDESCRIPTION:\033[0m" << std::endl; std::cout << " Natural language interface for ROM inspection using embedded labels." << std::endl; std::cout << " Query rooms, sprites, entrances, and game data conversationally." << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1magent simple-chat\033[0m [\"\"]" << std::endl; std::cout << " Single-shot or interactive chat mode" << std::endl; std::cout << " Options: --rom=, --verbose" << std::endl; std::cout << " Examples:" << std::endl; std::cout << " z3ed agent simple-chat \"What sprites are in room 5?\" --rom=zelda3.sfc" << std::endl; std::cout << " echo \"List all dungeons\" | z3ed agent simple-chat --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1magent test-conversation\033[0m" << std::endl; std::cout << " Interactive testing mode with full context" << std::endl; std::cout << " Options: --rom=, --verbose, --file=" << std::endl; std::cout << std::endl; std::cout << " \033[1magent chat\033[0m \"\"" << std::endl; std::cout << " Advanced multi-turn conversation mode" << std::endl; std::cout << " Options: --host=, --port=" << std::endl; std::cout << std::endl; std::cout << "\033[1mTIPS:\033[0m" << std::endl; std::cout << " β€’ Use --verbose to see detailed API calls and responses" << std::endl; std::cout << " β€’ Set GEMINI_API_KEY environment variable for Gemini" << std::endl; std::cout << " β€’ Use --ai_provider=gemini or --ai_provider=ollama" << std::endl; std::cout << std::endl; } else if (category == "chat") { std::cout << "\033[1m\033[36mπŸ’¬ CHAT ENTRYPOINT\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mDESCRIPTION:\033[0m" << std::endl; std::cout << " Launch the embedded agent in text, markdown, or JSON-friendly modes." << std::endl; std::cout << std::endl; std::cout << "\033[1mMODES:\033[0m" << std::endl; std::cout << " chat --mode=simple Quick REPL with --format=text|markdown|json|compact" << std::endl; std::cout << " chat --mode=batch --file=F Run prompts from file (one per line)" << std::endl; std::cout << " chat --mode=gui Launch the full FTXUI conversation experience" << std::endl; std::cout << " chat --mode=test Execute scripted agent conversation for QA" << std::endl; std::cout << std::endl; std::cout << "\033[1mOPTIONS:\033[0m" << std::endl; std::cout << " --format=text|markdown|json|compact Control response formatting" << std::endl; std::cout << " --prompt \"\" Send a single message and exit" << std::endl; std::cout << " --file questions.txt Batch mode input" << std::endl; std::cout << " --quiet Suppress extra banners" << std::endl; std::cout << std::endl; } else if (category == "proposal") { std::cout << "\033[1m\033[36m🧠 PROPOSAL WORKFLOWS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << " proposal run --prompt \"\" Plan and execute changes in sandbox" << std::endl; std::cout << " proposal list Show pending proposals" << std::endl; std::cout << " proposal diff [--proposal-id=X] Inspect latest diff/log" << std::endl; std::cout << " proposal accept --proposal-id=X Apply sandbox changes to main ROM" << std::endl; std::cout << " proposal commit | proposal revert Persist or undo sandbox changes" << std::endl; std::cout << std::endl; std::cout << "\033[1mTIPS:\033[0m" << std::endl; std::cout << " β€’ Run `z3ed proposal list` frequently to monitor progress" << std::endl; std::cout << " β€’ Use `--prompt` to describe tasks in natural language" << std::endl; std::cout << " β€’ Sandbox artifacts live alongside proposal logs" << std::endl; std::cout << std::endl; } else if (category == "widget") { std::cout << "\033[1m\033[36mπŸͺŸ GUI WIDGET DISCOVERY\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << " widget discover [--window=] [--type=]" << std::endl; std::cout << " Enumerate UI widgets available through automation hooks" << std::endl; std::cout << " Options: --format=table|json, --limit , --include-invisible, --include-disabled" << std::endl; std::cout << std::endl; std::cout << "\033[1mTIPS:\033[0m" << std::endl; std::cout << " β€’ Requires the YAZE GUI to be running locally" << std::endl; std::cout << " β€’ Combine with `z3ed proposal run` for automated UI tests" << std::endl; std::cout << std::endl; } else if (category == "patch") { std::cout << "\033[1m\033[36mπŸ”§ ROM PATCHING COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mDESCRIPTION:\033[0m" << std::endl; std::cout << " Apply patches and extract symbols from assembly files." << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mpatch apply-asar\033[0m " << std::endl; std::cout << " Apply Asar 65816 assembly patch to ROM" << std::endl; std::cout << " Options: --rom=, --output=" << std::endl; std::cout << " Example: z3ed patch apply-asar custom.asm --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mpatch apply-bps\033[0m " << std::endl; std::cout << " Apply BPS binary patch to ROM" << std::endl; std::cout << " Options: --rom=, --output=" << std::endl; std::cout << " Example: z3ed patch apply-bps hack.bps --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mpatch extract-symbols\033[0m " << std::endl; std::cout << " Extract symbol table from assembly file" << std::endl; std::cout << " Example: z3ed patch extract-symbols code.asm" << std::endl; std::cout << std::endl; std::cout << "\033[1mRELATED:\033[0m" << std::endl; std::cout << " z3ed help rom ROM operations and validation" << std::endl; std::cout << std::endl; } else if (category == "rom") { std::cout << "\033[1m\033[36mπŸ“¦ ROM OPERATIONS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mrom info\033[0m" << std::endl; std::cout << " Display ROM header and metadata" << std::endl; std::cout << " Example: z3ed rom info --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mrom diff\033[0m" << std::endl; std::cout << " Compare two ROM files byte-by-byte" << std::endl; std::cout << " Example: z3ed rom diff --src=original.sfc --modified=hacked.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mrom generate-golden\033[0m" << std::endl; std::cout << " Create golden test reference file" << std::endl; std::cout << " Example: z3ed rom generate-golden --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mrom validate\033[0m" << std::endl; std::cout << " Validate ROM checksum and structure" << std::endl; std::cout << " Example: z3ed rom validate --rom=zelda3.sfc" << std::endl; std::cout << std::endl; } else if (category == "overworld") { std::cout << "\033[1m\033[36mπŸ—ΊοΈ OVERWORLD COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mDESCRIPTION:\033[0m" << std::endl; std::cout << " Inspect and modify overworld map data, tiles, and warps." << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1moverworld get-tile\033[0m" << std::endl; std::cout << " Get tile ID at specific coordinates" << std::endl; std::cout << " Example: z3ed overworld get-tile --x=10 --y=20 --map=0 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1moverworld set-tile\033[0m" << std::endl; std::cout << " Place tile at coordinates" << std::endl; std::cout << " Example: z3ed overworld set-tile --x=10 --y=20 --tile=0x42 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1moverworld find-tile\033[0m" << std::endl; std::cout << " Search for all occurrences of a tile" << std::endl; std::cout << " Example: z3ed overworld find-tile --tile=0x42 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1moverworld describe-map\033[0m" << std::endl; std::cout << " Show map metadata and properties" << std::endl; std::cout << " Example: z3ed overworld describe-map --map=0 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1moverworld list-warps\033[0m" << std::endl; std::cout << " List all entrances and exits" << std::endl; std::cout << " Example: z3ed overworld list-warps --rom=zelda3.sfc" << std::endl; std::cout << std::endl; } else if (category == "dungeon") { std::cout << "\033[1m\033[36m🏰 DUNGEON COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mdungeon export\033[0m" << std::endl; std::cout << " Export dungeon room data to JSON" << std::endl; std::cout << " Example: z3ed dungeon export --room=5 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mdungeon import\033[0m" << std::endl; std::cout << " Import dungeon data from JSON" << std::endl; std::cout << " Example: z3ed dungeon import --file=room5.json --rom=zelda3.sfc" << std::endl; std::cout << std::endl; } else if (category == "gfx" || category == "graphics") { std::cout << "\033[1m\033[36m🎨 GRAPHICS COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mgfx export-sheet\033[0m" << std::endl; std::cout << " Export graphics sheet to PNG" << std::endl; std::cout << " Example: z3ed gfx export-sheet --sheet=0 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mgfx import-sheet\033[0m" << std::endl; std::cout << " Import graphics from PNG" << std::endl; std::cout << " Example: z3ed gfx import-sheet --file=custom.png --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << "\033[1mRELATED:\033[0m" << std::endl; std::cout << " z3ed help palette Palette manipulation commands" << std::endl; std::cout << std::endl; } else if (category == "palette") { std::cout << "\033[1m\033[36m🎨 PALETTE COMMANDS\033[0m" << std::endl; std::cout << std::endl; std::cout << "\033[1mCOMMANDS:\033[0m" << std::endl; std::cout << std::endl; std::cout << " \033[1mpalette export\033[0m" << std::endl; std::cout << " Export palette data" << std::endl; std::cout << " Example: z3ed palette export --palette=0 --rom=zelda3.sfc" << std::endl; std::cout << std::endl; std::cout << " \033[1mpalette import\033[0m" << std::endl; std::cout << " Import palette data" << std::endl; std::cout << " Example: z3ed palette import --file=colors.pal --rom=zelda3.sfc" << std::endl; std::cout << std::endl; } else { std::cout << "\033[1m\033[31mUnknown category: " << category << "\033[0m" << std::endl; std::cout << std::endl; std::cout << "Available categories: agent, chat, proposal, widget, patch, rom, overworld, dungeon, gfx, palette" << std::endl; std::cout << std::endl; std::cout << "Use 'z3ed --help' to see all commands." << std::endl; } } void ModernCLI::ShowCommandSummary() const { std::cout << GetColoredLogo() << std::endl; std::cout << std::endl; std::cout << "\033[1m\033[36mCOMMANDS OVERVIEW\033[0m" << std::endl; std::cout << std::endl; for (const auto& [key, info] : commands_) { std::string headline = info.description; const size_t newline_pos = headline.find('\n'); if (newline_pos != std::string::npos) { headline = headline.substr(0, newline_pos); } if (headline.empty()) { headline = info.usage; } if (headline.size() > 80) { headline = absl::StrCat(headline.substr(0, 77), "…"); } std::string label = info.name; if (label.size() > 24) { label = absl::StrCat(label.substr(0, 23), "…"); } std::cout << " " << absl::StrFormat("%-24s%s", label, headline) << std::endl; } std::cout << std::endl; std::cout << "Use \033[90mz3ed help \033[0m for detailed information." << std::endl; } absl::Status ModernCLI::Run(int argc, char* argv[]) { if (argc < 2) { ShowHelp(); return absl::OkStatus(); } std::vector args; args.reserve(argc - 1); for (int i = 1; i < argc; ++i) { args.emplace_back(argv[i]); } // Handle "help " command if (args.size() >= 1 && args[0] == "help") { if (args.size() == 1) { ShowHelp(); return absl::OkStatus(); } ShowCategoryHelp(args[1]); return absl::OkStatus(); } const CommandInfo* command_info = nullptr; size_t consumed_tokens = 0; if (args.size() >= 2) { std::string candidate = absl::StrCat(args[0], " ", args[1]); auto it = commands_.find(candidate); if (it != commands_.end()) { command_info = &it->second; consumed_tokens = 2; } } if (command_info == nullptr && !args.empty()) { auto it = commands_.find(args[0]); if (it != commands_.end()) { command_info = &it->second; consumed_tokens = 1; } } if (command_info == nullptr) { ShowHelp(); std::string joined = args.empty() ? std::string() : absl::StrJoin(args, " "); return absl::NotFoundError( absl::StrCat("Unknown command: ", joined.empty() ? "" : joined)); } std::vector command_args(args.begin() + consumed_tokens, args.end()); return command_info->handler(command_args); } CommandHandler* ModernCLI::GetCommandHandler(const std::string& name) { // This is not ideal, but it will work for now. if (name == "patch apply-asar") { static AsarPatch handler; return &handler; } if (name == "palette") { static Palette handler; return &handler; } if (name == "command-palette") { static CommandPalette handler; return &handler; } return nullptr; } absl::Status ModernCLI::HandleAsarPatchCommand(const std::vector& args) { AsarPatch handler; return handler.Run(args); } absl::Status ModernCLI::HandleBpsPatchCommand(const std::vector& args) { ApplyPatch handler; return handler.Run(args); } absl::Status ModernCLI::HandleRomInfoCommand(const std::vector& args) { RomInfo handler; return handler.Run(args); } absl::Status ModernCLI::HandleExtractSymbolsCommand(const std::vector& args) { // Use the AsarWrapper to extract symbols yaze::core::AsarWrapper wrapper; RETURN_IF_ERROR(wrapper.Initialize()); auto symbols_result = wrapper.ExtractSymbols(args[0]); if (!symbols_result.ok()) { return symbols_result.status(); } const auto& symbols = symbols_result.value(); std::cout << "🏷️ Extracted " << symbols.size() << " symbols from " << args[0] << ":" << std::endl; std::cout << std::endl; for (const auto& symbol : symbols) { std::cout << absl::StrFormat(" %-20s @ $%06X", symbol.name, symbol.address) << std::endl; } return absl::OkStatus(); } absl::Status ModernCLI::HandleAgentCommand(const std::vector& args) { Agent handler; return handler.Run(args); } absl::Status ModernCLI::HandleCollabCommand(const std::vector& args) { if (args.empty()) { return absl::InvalidArgumentError( "Usage: z3ed collab [options]\n" " start - Start the collaboration server\n" " status - Check server status"); } const std::string& subcommand = args[0]; if (subcommand == "start") { std::string port = "8765"; // Parse port argument for (size_t i = 1; i < args.size(); ++i) { if (absl::StartsWith(args[i], "--port=")) { port = args[i].substr(7); } else if (args[i] == "--port" && i + 1 < args.size()) { port = args[++i]; } } // Determine server directory std::string server_dir; if (const char* yaze_root = std::getenv("YAZE_ROOT")) { server_dir = std::string(yaze_root); } else { // Assume we're in build directory, server is ../yaze-server server_dir = ".."; } std::cout << "πŸš€ Starting collaboration server on port " << port << "...\n"; std::cout << " Server will be accessible at ws://localhost:" << port << "\n\n"; // Build platform-specific command std::string command; #ifdef _WIN32 // Windows: Use cmd.exe to run npm start command = "cd /D \"" + server_dir + "\\..\\yaze-server\" && set PORT=" + port + " && npm start"; #else // Unix: Use bash script command = "cd \"" + server_dir + "/../yaze-server\" && PORT=" + port + " node server.js &"; #endif int result = std::system(command.c_str()); if (result != 0) { std::cout << "⚠️ Note: Server may not be installed. To install:\n"; std::cout << " cd yaze-server && npm install\n"; return absl::InternalError("Failed to start collaboration server"); } std::cout << "βœ“ Server started (may take a few seconds to initialize)\n"; return absl::OkStatus(); } if (subcommand == "status") { // Check if Node.js process is running (platform-specific) int result; #ifdef _WIN32 // Windows: Use tasklist to find node.exe result = std::system("tasklist /FI \"IMAGENAME eq node.exe\" 2>NUL | find /I \"node.exe\" >NUL"); #else // Unix: Use pgrep result = std::system("pgrep -f 'node.*server.js' > /dev/null 2>&1"); #endif if (result == 0) { std::cout << "βœ“ Collaboration server appears to be running\n"; std::cout << " Connect from YAZE: Debug β†’ Agent Chat β†’ Network mode\n"; } else { std::cout << "β—‹ Collaboration server is not running\n"; std::cout << " Start with: z3ed collab start\n"; } return absl::OkStatus(); } return absl::InvalidArgumentError(absl::StrFormat("Unknown subcommand: %s", subcommand)); } absl::Status ModernCLI::HandleProjectBuildCommand(const std::vector& args) { ProjectBuild handler; return handler.Run(args); } absl::Status ModernCLI::HandleProjectInitCommand(const std::vector& args) { ProjectInit handler; return handler.Run(args); } absl::Status ModernCLI::HandleRomGenerateGoldenCommand(const std::vector& args) { std::string rom_file = absl::GetFlag(FLAGS_rom); if (!args.empty()) { rom_file = args[0]; } if (rom_file.empty()) { return absl::InvalidArgumentError("ROM file required (use --rom= or provide as argument)"); } Open handler; return handler.Run({rom_file}); } absl::Status ModernCLI::HandleDungeonExportCommand(const std::vector& args) { DungeonExport handler; return handler.Run(args); } absl::Status ModernCLI::HandleDungeonListObjectsCommand(const std::vector& args) { DungeonListObjects handler; return handler.Run(args); } absl::Status ModernCLI::HandleGfxExportCommand(const std::vector& args) { GfxExport handler; return handler.Run(args); } absl::Status ModernCLI::HandleGfxImportCommand(const std::vector& args) { GfxImport handler; return handler.Run(args); } absl::Status ModernCLI::HandleCommandPaletteCommand(const std::vector& args) { CommandPalette handler; return handler.Run(args); } absl::Status ModernCLI::HandlePaletteExportCommand(const std::vector& args) { PaletteExport handler; return handler.Run(args); } absl::Status ModernCLI::HandlePaletteCommand(const std::vector& args) { Palette handler; return handler.Run(args); } absl::Status ModernCLI::HandlePaletteImportCommand(const std::vector& args) { PaletteImport handler; return handler.Run(args); } absl::Status ModernCLI::HandleRomDiffCommand(const std::vector& args) { RomDiff handler; return handler.Run(args); } absl::Status ModernCLI::HandleRomValidateCommand(const std::vector& args) { RomValidate handler; return handler.Run(args); } absl::Status ModernCLI::HandleOverworldGetTileCommand(const std::vector& args) { OverworldGetTile handler; return handler.Run(args); } absl::Status ModernCLI::HandleOverworldFindTileCommand(const std::vector& args) { OverworldFindTile handler; return handler.Run(args); } absl::Status ModernCLI::HandleOverworldDescribeMapCommand(const std::vector& args) { OverworldDescribeMap handler; return handler.Run(args); } absl::Status ModernCLI::HandleOverworldListWarpsCommand(const std::vector& args) { OverworldListWarps handler; return handler.Run(args); } absl::Status ModernCLI::HandleOverworldSetTileCommand(const std::vector& args) { OverworldSetTile handler; return handler.Run(args); } absl::Status ModernCLI::HandleSpriteCreateCommand(const std::vector& args) { SpriteCreate handler; return handler.Run(args); } absl::Status ModernCLI::HandleChatEntryCommand( const std::vector& args) { std::string mode = "simple"; std::optional prompt; std::vector forwarded; for (size_t i = 0; i < args.size(); ++i) { const std::string& token = args[i]; if (absl::StartsWith(token, "--mode=")) { mode = absl::AsciiStrToLower(token.substr(7)); continue; } if (token == "--mode" && i + 1 < args.size()) { mode = absl::AsciiStrToLower(args[i + 1]); ++i; continue; } if (absl::StartsWith(token, "--prompt=")) { prompt = token.substr(9); continue; } if (token == "--prompt" && i + 1 < args.size()) { prompt = args[i + 1]; ++i; continue; } if (token == "--quiet" || token == "-q") { absl::SetFlag(&FLAGS_quiet, true); continue; } if (!absl::StartsWith(token, "--") && !prompt.has_value()) { prompt = token; continue; } forwarded.push_back(token); } const std::string normalized_mode = absl::AsciiStrToLower(mode); auto has_batch_file = [&forwarded]() { for (const auto& token : forwarded) { if (absl::StartsWith(token, "--file") || token == "--file") { return true; } } return false; }; std::vector agent_args; if (normalized_mode == "gui" || normalized_mode == "visual" || normalized_mode == "tui") { if (prompt.has_value()) { return absl::InvalidArgumentError( "GUI chat mode launches the interactive TUI and does not accept a --prompt value."); } agent_args.push_back("chat"); } else if (normalized_mode == "test" || normalized_mode == "qa") { if (prompt.has_value()) { return absl::InvalidArgumentError( "Test conversation mode does not accept an inline prompt."); } agent_args.push_back("test-conversation"); } else { if (normalized_mode == "batch" && !has_batch_file()) { return absl::InvalidArgumentError( "Batch chat mode requires a --file= option."); } agent_args.push_back("simple-chat"); if (prompt.has_value()) { agent_args.push_back(*prompt); } } agent_args.insert(agent_args.end(), forwarded.begin(), forwarded.end()); return HandleAgentCommand(agent_args); } absl::Status ModernCLI::HandleProposalCommand( const std::vector& args) { if (args.empty()) { ShowCategoryHelp("proposal"); return absl::OkStatus(); } std::string subcommand = absl::AsciiStrToLower(args[0]); std::vector forwarded(args.begin() + 1, args.end()); std::vector agent_args; if (subcommand == "run" || subcommand == "plan") { agent_args.push_back(subcommand); } else if (subcommand == "list") { agent_args.push_back("list"); } else if (subcommand == "diff" || subcommand == "show") { agent_args.push_back("diff"); } else if (subcommand == "accept") { agent_args.push_back("accept"); } else if (subcommand == "commit") { agent_args.push_back("commit"); } else if (subcommand == "revert" || subcommand == "reject") { agent_args.push_back("revert"); } else if (subcommand == "test") { agent_args.push_back("test"); } else { return absl::InvalidArgumentError( absl::StrCat("Unknown proposal command: ", subcommand, ". Valid actions: run, plan, list, diff, show, accept, commit, revert.")); } agent_args.insert(agent_args.end(), forwarded.begin(), forwarded.end()); return HandleAgentCommand(agent_args); } absl::Status ModernCLI::HandleWidgetCommand( const std::vector& args) { if (args.empty()) { ShowCategoryHelp("widget"); return absl::OkStatus(); } std::vector forwarded(args.begin(), args.end()); std::string subcommand = absl::AsciiStrToLower(forwarded[0]); std::vector agent_args; if (subcommand == "discover") { agent_args.push_back("gui"); agent_args.insert(agent_args.end(), forwarded.begin(), forwarded.end()); } else { return absl::InvalidArgumentError( absl::StrCat("Unknown widget command: ", forwarded[0], ". Try 'z3ed widget discover'.")); } return HandleAgentCommand(agent_args); } } // namespace cli } // namespace yaze