Repository navigation
Releases: DiegoLopez0208/RpgMakerMVUltimate-MCP
Release list
v5.21.0
Fixed
- Refuse filesystem escapes through symlinks and Windows junctions, including new files under linked parents.
- Validate System JSON and known fields before selecting a project; reject MZ engine files while supporting partial MV data.
- Match Unicode names in offline text search, including CJK and normalized Unicode.
- Report skipped/unreadable files and incomplete analysis; empty search results now explain their limits.
- Refuse stale-read writes, competing file writers and failed backups; use unique temporary files and backup identifiers. Multi-file edits are still not transactions.
- Update Sharp, the MCP SDK and test dependencies; make high-severity npm audit findings fail CI, with Windows and Linux test jobs.
Added
- Read-only
--doctor [projectPath]environment check and three first-result recipes.
Changed
- Require Node.js 20.19.0+; align docs with 18 tools and lexical search.
v5.20.0
Upgrade notes. No bridge reinstall is needed. playtest now starts the game with its own NW.js profile under .mcp-cache/nw-profile (add it to your .gitignore if the project is under git) and closes the game an earlier playtest started; pass keepRunning: true to keep the old one. Tool results for take_screenshot and run_playtest now include the PNG as an image block; set RPGMV_INLINE_IMAGES=0 to get paths only. analyze_project validate can report new warnings (missing-asset, asset-case), and generate_map and the manage_map_event presets can return warnings; none of them blocks the write, except presets that point at a troop, item, weapon, armor or switch that does not exist, which are now refused.
Added
manage_systemactionstop_playtestcloses the game this server started for the active project, together with the processes it spawned.manage_systemactionresize_list{section, size}sets the highest valid switch or variable id (1 to 5000), like the editor's "Change Maximum". MV ignores ids past the end of these arrays, so an event writing variable 40 into a 20-variable project did nothing and gave no error. Growing adds empty names; shrinking past a named id is refused unlessforce: true.manage_systemactionslist_backupsandrestore_backup. Every write already kept the previous version in.mcp-backups/; now they can be listed ({file?}, grouped by file, newest first) and put back ({file, backup?}, default the newest, which undoes the last write). Restoring is a write too, so the version it replaces is backed up and a restore can be undone. Coversdata/*.jsonandplugins.js; a backup that is not valid JSON is refused.create_database_entrytakesentries(up to 200 data objects) instead ofdata, to create many items, skills or enemies in one call. It returns{count, created[]}; a failing entry stops the run and the error names the entries already created.analyze_projectvalidate reports image and audio files the project lacks (missing-asset, warning). It reads the names the engine would request, from map battlebacks, parallax and BGM/BGS, event graphics, audio, picture and battleback commands, actors, enemies, animations, tilesets andSystem.json, and compares them withimg/andaudio/. Each distinct file is reported once with the first place that uses it and how many times. A file that exists only under a different letter case is reported asasset-case, since it loads on Windows and fails on a case-sensitive web host. A folder that does not exist is skipped, and RTP assets that are not copied into the project show up too, so the message says so. The counts feedbySeverityand theoverviewhealth.generate_mapchecks that the map can be walked. Every mode exceptblankandduplicatenow returnswalkabilityper generated map (walkable tiles, size of the main walkable area, tiles cut off from it) andwarningswhen the map has no walkable tile, only a tiny one, or more than a fifth of its walkable tiles unreachable from the main area. The map is still written; the warning says what to fix.manage_map_eventpresets validate before writing. A troop, item, weapon, armor or switch that does not exist, or a position outside the map, is refused. A tile the player cannot stand on, a tile that already holds an event, and a teleport or door destination that is unwalkable or whose map does not exist yet come back aswarningson the created event. Action-button doors may sit on wall tiles without a warning.- Screenshots and renders come back as pictures.
take_screenshot(live game, or headless withmapId) andrun_playtestframes are now attached to the tool result as MCPimageblocks next to the JSON, so the client model can see the map instead of only reading a file path. Only PNGs this server wrote under.mcp-cache/rendersor.mcp-cache/screenshotsare attached, at most 4 per result and 1.5 MB each; setRPGMV_INLINE_IMAGES=0to get paths only.
Changed
create_pluginkeepsjs/plugins.jsin the editor's format. It used to rewrite the whole file as indented JSON under a different header. It now keeps the file's own header and line endings and writes one compact entry per line, so adding or replacing a plugin changes only that line.populateplaces events on free tiles of the main walkable area instead of random coordinates that could land in a wall or an unreachable pocket. It stops with a warning when the area is full, and refuses a map with no free tile. Explicitopts.x/opts.yare used as given.- The
set_starting_positiondescription now says what it already did: a tile the player cannot stand on is moved to the nearest walkable one, reported asrelocated.
Fixed
- Relaunching a playtest restarted nothing. NW.js, like Chromium, runs a single instance per profile: a second
game.exeon the shared profile handed its command to the game already open and exited, so the pid returned was dead and the old game kept running (with the old code and its bridge connection) until someone killed it by hand.playtestnow gives each project its own profile under.mcp-cache/nw-profile, remembers the game it started, and closes that one first. A pid is only killed if it is still a process of the executable that was launched, so a pid the system has since reused is never touched;keepRunning: trueleaves the earlier game open. The result carriesreplacedPid.
v5.19.0
Upgrade notes. Projects that use the live bridge must reinstall its plugin (manage_system action install_bridge_plugin) and restart the playtest: older plugins are refused, and bridge_status says so in lastAuthError. delete_database_entry now refuses entries that are still referenced; pass dryRun: true to see where, or force: true to delete anyway. Node.js 20 or newer is required.
Added
edit_mapactionpaintwrites one tile into a list of cells or a rectangle of one layer. An autotile is written as its kind, and the painted cells and their eight neighbours get the shapes the editor would give them, so shorelines and wall edges join up; the rest of the map keeps the shapes it was saved with. Layer 5 takes region ids.analyze_projectvalidate reportsinvalid-autotile: autotile cells whose shape the engine has no table entry for, which make MV throw while drawing the map.edit_mapactionrepair_autotilesfixes only those cells.manage_systemactionexport_webbuilds an HTML5 deployment folder and a ZIP withindex.htmlat its root, ready for itch.io: runtime scripts, fonts, data and assets, with encrypted assets copied byte for byte and no saves or backups.prune: truedrops img/audio/movies files that no data or script references; it is off by default because assets a plugin names only at runtime would be lost. The output must be outside the project and new, empty or a previous export; an existing<outDir>.zipthat a previous export did not write is never overwritten. Ported from #20, which pruned by default and overwrote any zip at that path.run_playtest, a new tool. Plays a script of steps headless with the real engine in Chromium:loada map with party, gold, switches and items;startEvent;advanceTextreturns the lines shown;choose;walkreports where the player ended or which tile blocked;press;wait;autoBattlefights to the end (fast-forwarded unlessrealtime);screenshot;eval. No editor, NW.js or running game is needed, and project data is not changed. Long runs send MCP progress notifications when the client asks with a progressToken.take_screenshotwithmapIdrenders a map headless, the whole map or a view centred on a tile, as the engine draws it (autotiles, events, optional switches and autorun events). WithoutmapIdit still captures the live playtest.- Both need the optional
playwright-coreand a cached Chromium (orRPGMAKER_MCP_CHROMIUM); without them only these two calls fail, with instructions. PNGs go under.mcp-cache/renders/. Ported from #20 without its caller-chosen output path. - The server closes headless browsers and the live bridge when the client disconnects or on SIGINT/SIGTERM.
RPGMV_TOOLSETnarrows the advertised tools to profiles:core(always on),events,mapgen,media. A data-only session withcorelists 11 tools in about 11k tokens instead of 17 in about 18k. Calling a tool outside the toolset says which profile to add; an unknown profile stops the server at startup.- A test fails when the default tool list grows past 72 KB (about 20k tokens) or any single tool past 16 KB, and every advertised tool must belong to exactly one profile. CI now also runs lint and the stdio protocol check.
build_event_commandskindmove_route: Set Movement Route from named steps (the engine'sROUTE_*names, withtimesto repeat). Writes the route on the 205 row and one 505 row per step without the end marker, as the editor does; #20's version included the end marker.update_database_entrywithentity: "troops"andaddPageinserts one battle-event page from a compact trigger (turn,enemyHpBelow,actorHpBelow,switchId,turnEnd) without resending the other pages. Pages that could never run are refused. Also available as the legacyadd_troop_pagetool.build_event_commandsgains 16 kinds: gold, items/weapons/armors, party members, actor HP/MP/EXP/level/state/recover all, audio, screen fade/tint/flash/shake, pictures, animations, balloons, battle processing with result branches, shops, name input, and the troop commands enemy appear, enemy state and abort battle.insert_event_commandschecks the references they introduce (items, actors, states, animations, variables). Ported from the builders in #20 and checked slot by slot against the MV 1.6.1 interpreter.show_textcan wrap:wrap: truereflows the lines and splits them into four-line boxes;"hard"keeps each line as a break. Without it, lines longer than about 55 characters (38 with a face) produce a warning.npm run test:mv-effects: runs the new builders through a local MV interpreter with spied game objects and checks every call it makes.build_event_commands: project-independent, read-only builders for MV dialogue, choices, conditions, switches/self switches, variables, transfers, common-event calls, flow control, and plugin commands. Nested branches compose without mutating input arrays; schemas reject invalid fields and MZ-only formats.insert_event_commands: validated insertion into map-event, common-event, and troop-page lists. Supports dry-run previews, safe nested insertion boundaries, relevant reference checks (including weapon and armor conditions), and the existing atomic backup-protected writer. The result reports the new list length and its command codes;verbose: trueadds the full before/after lists. When Cancel rows (code 403) are accepted both empty and with the[6, null]parameters the editor saves.- Stdio protocol verification for both normal and legacy tool listings, plus an optional headless check against a locally installed MV interpreter. The runtime check does not redistribute engine files or modify game projects.
Changed
delete_database_entryrefuses to delete an entry that is still referenced, listing every place: events, common events, troop pages, page conditions, map encounters, the starting party, actor classes and starting equipment, class learnings, enemy actions and drops, troop members, traits, item and skill effects, animations.dryRun: truepreviews the list;force: truedeletes anyway and returnsbrokenReferences. Deleting used to null the entry silently and leave the game to fail at runtime.- Event command names, parameter counts, numeric slots and block structure now come from one table (
eventCommandTable.ts) shared by the validator, the event outline and the numeric normaliser.
Security
- The live bridge only accepts a game from its own project. The plugin sends the realpath of the folder it runs from with the token, and a game from another folder is refused. Windows paths compare without case. Reinstall the plugin (
manage_systemactioninstall_bridge_plugin) and restart the playtest: an older plugin is refused, andbridge_statussays so in its newlastAuthErrorfield. reload_databasecan no longer write arbitrary globals in the game. The plugin accepted anyfileandglobalVarpair; it now only reloads the database files the server allows, each into its own global (the same list, baked into the plugin).- The plugin waits for the server to confirm authentication before sending anything else, and ignores commands until then.
bridge_statusno longer returns the session token.- Telemetry frames over 64 KB are dropped and the buffer is capped at 2 MB; a message assembled from fragments is capped like a single frame; high-frequency frames are dropped in the game when the socket backs up.
- Bridge commands are refused with out-of-range arguments (teleport coordinates and direction, button hold time, timeout), and the plugin refuses a transfer while an event, message or reload is in progress.
- A bridge left running for another project is stopped before any telemetry, command, screenshot or recording reaches it.
Fixed
- Generated maps no longer crash the tile renderer on waterfalls. Only A1 kinds 5 and 7 were treated as waterfalls; the engine also counts 9, 11, 13 and 15. Those got floor shapes up to 47 from the autotile pass, but the waterfall table has 4, so ShaderTilemap threw while drawing them. Waterfalls now keep a valid shape, and an out-of-range one (as in maps generated before this fix) is reset to 0 the next time the map's autotiles are recomputed. Found by the headless renderer on a real project.
- A second bridge cannot overwrite a live one's handshake, and a dead one's does not block. The handshake is created exclusively; one left by a server that was killed (its pid is gone) is replaced, one held by a live process is refused with its pid. Stopping only deletes a handshake the bridge itself wrote.
- A reloaded game (F5) is no longer stuck behind its previous connection: the newest authenticated game replaces the older one, and
bridge_statuscounts only live connections. - Shop Processing is written the way the engine reads it. The engine reads the 302 row as the first good and purchase-only from its fifth slot; the builder wrote
[0, purchaseOnly], so purchase-only was ignored, a purchase-only shop gained item #1, and shops from the map generator carried an empty first good. - Reference checks now see what shops sell. Shops were read as if 302 held a list of goods, so items, weapons and armors sold in shops were invisible to validation and reference search.
- Show Animation references the right animation. References and the event outline read the wait flag (
parameters[2]) instead of the animation id (parameters[1]). - Change Weapons/Armors and Show Animation write their last flag (include equipment, wait) like the editor does.
- A client with no active project can now call
set_project_path; the previous transport guard refused the very call needed to select one. Pure command builders also work without a project. Project-dependent calls remain guarded.
v5.18.0
Added
record_video, a new tool. Records the live playtest canvas through the authenticated bridge:action:"start"begins a silent WebM capture inside the game runtime,action:"stop"saves it under.mcp-cache/recordings/and returns{path, bytes, mimeType, durationMs, name}. Only the game canvas is captured, never the desktop.fps(1-60) andbitrateKbps(250-10000) control the artifact.bridge_commandcan drive the game, not just watch it. Two new instructions:interact, andpress_buttonwithbutton(ok,cancel,menu, and the four directions) and adurationMshold of 30-1000 ms. Until now the bridge could teleport the player and reload data but had no way to press anything, so a playtest could be set up and never actually played.
Fixed
- A refused bridge command no longer returns as if it had worked.
bridgeCommandreturned the game'serrorframe inside a{sent: true}envelope, so a rejection read as success at the call site. It now throws with the game's own message.
Changed
record_videoandpress_buttonnumeric arguments are validated with the same strict integer fields introduced in 5.17.0, against the ranges the tool definitions advertise. They were declared as the permissiveidLike, which would have carriedfps: "abc"through toNumber()and handedMediaRecordera NaN frame rate.
v5.17.0
Reported as #15. Both defects in that report reproduce, and chasing them turned up the reason they shipped at all.
Fixed
generate_mapthrew away the tileset of every template it cloned.createMapFromTemplatewrotetilesetId: params.tilesetId || 1, ignoring the map it had just copied. The tileset selects which tile tables a map renders with, so cloningtemplateId: 33— an interior authored for Inside (3) — produced a map flagged as Overworld and rendered as garbage in the engine. 105 of the 111 bundled templates carry a tilesetId other than 1, so this was the normal case, not an edge one. The procedural path had the same hole from the other direction: a forcedtemplateIdskips the tileset hard-filter that protects the auto-pick, sotheme:"town"withtemplateId:33cloned Inside tile data onto an Outside map.generateFromTemplateandcloneTemplateForThemenow return the tileset their data belongs to, and both create paths resolve valid explicit override → cloned template's tileset → theme default. Auto-picked templates are unaffected, and a test pins that.- The template test suite could not see the code it was testing.
knowledge/was resolved with one hardcoded relative path that only matched the built layout, so loaded fromsrc/every template lookup silently returnednull.tests/mapGenerator.test.tstherefore imported fromdist/on purpose — testing whatever was last compiled rather than the working tree — andtests/integration.test.tsasserted thatmode:"template"throws, codifying the failure as expected behaviour. The whole template write path had no coverage, which is exactly where the tileset bug lived. Resolution now probes both layouts. The fix also drops a dependency onimport.meta.dirname, which isundefinedbelow Node 20.11 whileengines.nodesays>=18. - Event presets persisted numeric strings where the editor writes numbers. Agents routinely pass stringly-typed numbers, the preset handlers copied
x/yand command parameters straight out of their arguments, andvalidateConsolidateddiscarded the args Zod had already coerced — its own comment said "this only checks, it never rewrites them". Sopreset:"door"withx:"1",destX:"3"wrote"x": "1"andparameters: [0, 1, "3", "4", 0, 0]. MV coerces most of that at runtime, but the file is then non-canonical: the editor rewrites those values on the next save, and type-aware tooling, diffs and$dataMapconsumers see mixed types. Fixed in three layers — the router forwards the parsed args,ManageMapEventSchemadeclares the numeric fields the presets actually take, andwriteMapJsonruns a newnormalizeMapEvents()as a last line of defence. - A resized template clone silently lost every event it carried, because the resize branch returned
[]regardless ofkeepEvents. Events now survive a resize; any that fall outside the new bounds becomenullrather than being spliced out, since the engine reads an event's id from its index in the array and compacting would renumber everything after the hole. createMapBatchleft a half-written run on disk when a later spec was invalid. Every spec is validated before any is written.
Changed
- Numbers are parsed strictly, and junk is now an error rather than a wrong value.
toNumusedparseInt, which accepts anything beginning with digits and truncates the rest:"2.5"became 2,"2x"became 2,"0x10"became 16,"1e3"became 1.z.coerce.number()was worse in the other direction —null,""and" "all became 0 before any handler ran, so a blank coordinate was accepted and written as 0. Both are replaced by parsers that take a whole number or a string holding exactly one.widthandheightadditionally have to fall within 1..256, the editor's own cap; they previously went straight intoNumber(), so"2.5"produced a map with"width": 2.5and a fractional-length data array, and"1e3"produced a 1000-tile side without comment. - An explicit
tilesetIdis validated against the project'sTilesets.jsonand rejected before any file is written, listing the ids that do exist, instead of|| 1quietly turning0,"abc"or a nonexistent id into the Overworld tileset.
Notes
normalizeMapEvents() coerces only the slots RPG Maker MV defines as numeric, per opcode, from an explicit table. There is deliberately no blanket recursion: 101[0] is a face image filename, 401/405 are message text, 102[0] holds the choice labels, 108/118/119 are comments and label names, 320[1] is an actor name, 355/655 are script lines, 356 is a plugin command, and 122's operand is JavaScript when operandType is 4. Coercing those would corrupt messages and break plugins. Opcodes not in the table pass through untouched.
v5.16.1
Fixed
- The live bridge never connected on a real playtest. Two independent causes, both about how the runtime is launched.
Utils.isOptionValidonly inspectsnw.App.argv[0], butplaytestspawnsgame.exe <projectPath> test, so the project path sat inargv[0]and the test token inargv[1]: the plugin guard was never true and the plugin returned on its first statement. And recent NW.js servesindex.htmlfrom achrome-extension://origin, where the pathname says nothing about where the project lives, so the handshake file was never found. The guard now scans every argument, and the project root falls back toprocess.cwd()— withplaytestspawningcwd: projectPathso that fallback is correct by construction rather than by luck. Without it the game inherited the MCP server's directory and looked for the handshake in the wrong place entirely. - Editor-only bits in the bundled template shadow layers. MV renders the shadow layer as a four-bit quadrant mask; four cells across Map012, Map014, Map015 and Map025 carry values outside 0-15 (for example
0x40000005). Harmless in-engine, but it produced non-canonical project data and made the project's own validator reportinvalid_shadow. Normalised with& 0x0fon both materialisation paths. - Three cases where
||swallowed a legitimate zero. MV trigger0is Action Button, sotrigger || 1silently turned every explicitly-Action-Button transfer into Player Touch.opts.x || …replaced a requested column or row0with a random coordinate.count || 3turned a request for zero events into three. process && process.cwdin the generated plugin threwReferenceErrorrather than short-circuiting when the global was absent.
Added
- A credential-free diagnostic log at
.mcp-cache/bridge-plugin.log, because a socket that fails before it connects cannot report why it failed. npm run lintas a real gate. The baseline was 102 errors; test files now scopeno-explicit-anyoff (they deliberately inspect untyped MV JSON) and the genuinely unused imports are gone.
Changed
- The bridge tests now execute the generated plugin against stubbed engine globals and assert what it did — whether it opened a socket, which path it read the handshake from, whether the token ever reached disk — instead of asserting on its source text, which passed for any refactor that broke the behaviour and failed for any that preserved it.
- The README no longer claims a deployed build can never open a socket. Scanning all of
argvis right given howplaytestlaunches, but it is wider than the engine's own check, and the docs now say so.
v5.16.0
Added
analyze_projectviewbalance. Scores every database entry on a power metric and compares it against its PEERS rather than against thresholds invented by the tool, so it works on a game at any power level: damage per MP for skills, gold per point of ATK+MAT for weapons, gold per point of DEF+MDF for armors, HP per point of EXP for enemies. The comparison is leave-one-out — an entry is judged against statistics it had no hand in creating, which is what stops a badly broken entry from dragging the mean toward itself until it stops looking unusual. Narrow withcategory, loosen or tighten withthresholdSd(default 2). Each outlier carries a sentence about what it means for the player.- A damage-formula reader that never executes anything (
src/utils/formulaEval.ts). Tokenise, shunting-yard, evaluate: arithmetic, unary minus, parentheses, game variables and the Math functions damage formulas actually use. A formula it cannot read statically comes back with a reason rather than a number, andbalancelists those underunreadableFormulasinstead of scoring them as zero damage — scoring them zero would pull every average down and hide the outliers the analysis exists to find. validatenow reads dialogue text.\N[id]is resolved by the engine at draw time rather than from any structural parameter, so an id pointing at no actor passed every existing check and the editor showed nothing wrong; the line simply rendered in-game with a hole where the name should be. References are collected from every text-bearing command: 401 and 405 lines, 102 choice arrays, and 402 branch labels.
Notes
Both features are ideas taken from the Efold499e fork, reimplemented rather than ported. Two deliberate departures:
- that version scores a damage formula by substituting the six stat names and passing the rest to
eval(). Substitution sanitises the tokens it knows and leaves the rest of the string intact, so a craftedSkills.jsonruns arbitrary code in whoever analyses the project — which matters precisely because this is a tool you point at projects you did not write. - it also validates
\I[n]againstItems.json. That escape is an icon index (processEscapeCharactercase'I'callsprocessDrawIcon, which indexes IconSet.png), not an item id, so the check flags every valid icon on a project with few items. Icon indices are still collected here; nothing validates them against the database.
v5.15.0
Added
- The live bridge: the game talks back.
playtestused to be fire-and-forget.manage_systemgainsinstall_bridge_plugin,bridge_start,bridge_status,bridge_telemetry,bridge_command,bridge_screenshotandbridge_stop. The generatedjs/plugins/McpBridge.jsconnects out to a loopback WebSocket and streams exceptions (with stack traces),console.error/warn, scene changes, player position, the event command currently executing, FPS and heap.bridge_command "reload_map"re-reads the current map from disk and rebuilds the scene without losing party state, using the engine's own reload seam (a reserved transfer withGame_Player._needsMapReload) rather than hand-rebuildingSpriteset_Map;reload_databasehot-reloads one data file, and refusesSystem.json/Tilesets.jsonwith an explanation because those need a fresh playtest.bridge_screenshotwrites a PNG foranalyze_image.- Security: the plugin returns immediately unless
Utils.isNwjs() && Utils.isOptionValid('test'), so a deployed build never opens a socket. The server binds127.0.0.1only, refuses upgrades carrying a browserOrigin(cross-site WebSocket hijacking), and requires the session token from.mcp-bridge.json, compared in constant time, within 5 seconds. The command surface is a fixed allowlist with noevalprimitive. - The WebSocket server is hand-rolled over
http+crypto(~230 LOC): no new runtime dependency.
- Security: the plugin returns immediately unless
analyze_projectviewmetrics. Measures a map instead of judging it: flood-fill reachability from the real entry point (stranded walkable tiles, and events with no reachable tile beside them — softlocks, not style notes), dead-space ratio against a band forexpectedinterior/dungeon/exterior, the walkable area thinned with Zhang-Suen and read as a graph (endpoints, junctions, cycles, critical path, linearity), Shannon entropy over 5x5 tile windows for monotony, and — for maps with encounters — how many steps the player is from a shop/inn/save point.manage_systemactionmine_templates. Reads every map in the project and derives, into.mcp-cache/: semantic layouts with the art stripped out (multi-tile props kept whole, doors and points of interest inferred from event commands), a tileset profile per tileset naming the concrete tile the project uses for ground/wall/water/roof by observed frequency, and token adjacency counts. The project itself is not modified. This is what makes generation work on tilesets the bundled RTP templates cannot target.generate_mapmodesemantic. Lays out a mission — entrance, key, locked door, treasure, boss, exit, side rooms — as a graph, then materialises it through a tileset profile, so one layout works on any tileset including DLC and third-party packs. The key always sits on the entrance side of the lock, so the map is solvable by construction; autotile shapes are recomputed from the finished neighbourhood at the end. Returnsmarkersnaming the cell of every mission role. Pass a minedtemplateIdto re-materialise one of the project's own maps onto a different tileset.
Fixed
- The consolidated
manage_systemandgenerate_mapZod schemas rejected every new action and mode with a bareInvalid enum value;validateConsolidatednow knows about them.
v5.14.2 — README docs update
Changed
- Documentation: the README now covers the capabilities added in 5.13–5.14 — write safety (atomic writes, rotated backups,
dryRun), project scaffolding, playtest / open-in-editor, plugin authoring, and full troop/animation CRUD. No code changes.
v5.14.1 — playtest runtime fix
Fixed
playtestnow actually runs the game. 5.14.0 launchednwjs-win/Game.exe, which is a deploy runtime pinned by its ownpackage.jsontowww/index.html— so it ignored the project path and showedERR_FILE_NOT_FOUND. playtest now auto-selects an nwjs runtime whose directory has no siblingpackage.json(e.g.nwjs-win-test/game.exe), so the project path is honored and the project renders. Verified live end-to-end: an MCP-scaffolded project with a generated town map opens and is playable. AgameExeparam allows overriding the runtime.