modwright 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +212 -0
- package/assets/claude/agents/mw-explore.md +46 -0
- package/assets/claude/agents/mw-fixture.md +33 -0
- package/assets/claude/agents/mw-rule-author.md +51 -0
- package/assets/claude/agents/mw-scaffold-author.md +48 -0
- package/assets/claude/skills/mw-author.md +59 -0
- package/assets/claude/skills/mw-new-item.md +51 -0
- package/assets/claude/skills/mw-new-spell.md +50 -0
- package/assets/claude/skills/mw-scaffold.md +59 -0
- package/assets/claude/skills/mw-ship.md +57 -0
- package/assets/claude/skills/mw-validate.md +51 -0
- package/assets/claude/skills/mw-verify.md +59 -0
- package/assets/nivalisnights/extract_resources.py +177 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Config.json +5 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapClient.lua +55 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapServer.lua +81 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Log.lua +96 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Probes.lua +1038 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/ProbesClient.lua +74 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Protocol.lua +985 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Watch.lua +175 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/meta.lsx +45 -0
- package/bridges/bg3-se/README.md +99 -0
- package/bridges/bg3-se/bridge.json +68 -0
- package/bridges/cp2077-cet/ModWrightBridge/init.lua +375 -0
- package/bridges/cp2077-cet/ModWrightBridge/probes.lua +1104 -0
- package/bridges/cp2077-cet/ModWrightBridge/protocol.lua +1306 -0
- package/bridges/cp2077-cet/README.md +165 -0
- package/bridges/cp2077-cet/bridge.json +53 -0
- package/dist/core/bridge/arm.js +175 -0
- package/dist/core/bridge/cet-console.js +26 -0
- package/dist/core/bridge/cet-log.js +362 -0
- package/dist/core/bridge/index.js +11 -0
- package/dist/core/bridge/locate.js +35 -0
- package/dist/core/bridge/paths.js +34 -0
- package/dist/core/bridge/pending.js +89 -0
- package/dist/core/bridge/poll.js +155 -0
- package/dist/core/bridge/stage.js +100 -0
- package/dist/core/bridge/status.js +97 -0
- package/dist/core/bridge/types.js +145 -0
- package/dist/core/bridge/validate.js +107 -0
- package/dist/core/build/index.js +6 -0
- package/dist/core/build/manifest.js +47 -0
- package/dist/core/build/paths.js +32 -0
- package/dist/core/build/run.js +342 -0
- package/dist/core/build/stage.js +265 -0
- package/dist/core/build/steps/bg3.js +129 -0
- package/dist/core/build/steps/core.js +195 -0
- package/dist/core/build/steps/cyberpunk.js +95 -0
- package/dist/core/build/steps/listing.js +35 -0
- package/dist/core/build/types.js +13 -0
- package/dist/core/build/zip.js +148 -0
- package/dist/core/compat/check.js +113 -0
- package/dist/core/compat/pe.js +220 -0
- package/dist/core/compat/types.js +1 -0
- package/dist/core/compat/version.js +70 -0
- package/dist/core/deploy/guards.js +292 -0
- package/dist/core/deploy/index.js +5 -0
- package/dist/core/deploy/plan.js +696 -0
- package/dist/core/deploy/reload-apply.js +361 -0
- package/dist/core/deploy/reload.js +358 -0
- package/dist/core/deploy/sources.js +169 -0
- package/dist/core/deploy/types.js +1 -0
- package/dist/core/epic.js +54 -0
- package/dist/core/fsutil.js +191 -0
- package/dist/core/home.js +70 -0
- package/dist/core/knowledge/facts.js +229 -0
- package/dist/core/knowledge/index.js +4 -0
- package/dist/core/knowledge/search.js +112 -0
- package/dist/core/knowledge/store.js +123 -0
- package/dist/core/knowledge/types.js +1 -0
- package/dist/core/ledger/check.js +42 -0
- package/dist/core/ledger/hooks.js +80 -0
- package/dist/core/ledger/index.js +7 -0
- package/dist/core/ledger/query.js +17 -0
- package/dist/core/ledger/record.js +84 -0
- package/dist/core/ledger/status.js +53 -0
- package/dist/core/ledger/store.js +98 -0
- package/dist/core/ledger/types.js +108 -0
- package/dist/core/logs/index.js +4 -0
- package/dist/core/logs/registry.js +50 -0
- package/dist/core/logs/scan.js +195 -0
- package/dist/core/logs/triage.js +160 -0
- package/dist/core/logs/types.js +1 -0
- package/dist/core/lsx.js +10 -0
- package/dist/core/lz4.js +66 -0
- package/dist/core/mods.js +229 -0
- package/dist/core/process.js +49 -0
- package/dist/core/project/build-steps.js +83 -0
- package/dist/core/project/describe.js +74 -0
- package/dist/core/project/index.js +5 -0
- package/dist/core/project/load.js +146 -0
- package/dist/core/project/schema.js +264 -0
- package/dist/core/project/types.js +3 -0
- package/dist/core/prompts.js +72 -0
- package/dist/core/registry.js +35 -0
- package/dist/core/safety/backup.js +206 -0
- package/dist/core/safety/execute.js +188 -0
- package/dist/core/safety/index.js +5 -0
- package/dist/core/safety/plan.js +122 -0
- package/dist/core/safety/restore.js +172 -0
- package/dist/core/safety/types.js +1 -0
- package/dist/core/scaffold/bootstrap.js +7 -0
- package/dist/core/scaffold/catalog.js +28 -0
- package/dist/core/scaffold/claude-assets.js +271 -0
- package/dist/core/scaffold/collisions.js +64 -0
- package/dist/core/scaffold/emit.js +356 -0
- package/dist/core/scaffold/engine.js +680 -0
- package/dist/core/scaffold/ids.js +78 -0
- package/dist/core/scaffold/index.js +10 -0
- package/dist/core/scaffold/jsonpath.js +121 -0
- package/dist/core/scaffold/load.js +81 -0
- package/dist/core/scaffold/render.js +85 -0
- package/dist/core/scaffold/sequence.js +396 -0
- package/dist/core/scaffold/types.js +309 -0
- package/dist/core/steam.js +63 -0
- package/dist/core/surfaceutil.js +36 -0
- package/dist/core/testplan/check.js +67 -0
- package/dist/core/testplan/index.js +5 -0
- package/dist/core/testplan/load.js +100 -0
- package/dist/core/testplan/registry.js +317 -0
- package/dist/core/testplan/run.js +389 -0
- package/dist/core/testplan/types.js +82 -0
- package/dist/core/text/converters/bg3.js +50 -0
- package/dist/core/text/converters/cyberpunk.js +36 -0
- package/dist/core/text/converters/index.js +261 -0
- package/dist/core/text/diff/cr2w.js +180 -0
- package/dist/core/text/diff/index.js +16 -0
- package/dist/core/text/diff/loca.js +43 -0
- package/dist/core/text/diff/lsx.js +126 -0
- package/dist/core/text/handles.js +86 -0
- package/dist/core/text/index.js +6 -0
- package/dist/core/text/json.js +46 -0
- package/dist/core/text/templates/cache.js +71 -0
- package/dist/core/text/templates/index.js +3 -0
- package/dist/core/text/templates/library.js +245 -0
- package/dist/core/text/templates/types.js +51 -0
- package/dist/core/text/toolchain.js +16 -0
- package/dist/core/text/types.js +1 -0
- package/dist/core/text/validate.js +149 -0
- package/dist/core/text/xml.js +58 -0
- package/dist/core/toolchain/datafetch.js +151 -0
- package/dist/core/toolchain/env.js +63 -0
- package/dist/core/toolchain/features.js +170 -0
- package/dist/core/toolchain/index.js +8 -0
- package/dist/core/toolchain/install.js +191 -0
- package/dist/core/toolchain/locate.js +275 -0
- package/dist/core/toolchain/remember.js +84 -0
- package/dist/core/toolchain/run.js +318 -0
- package/dist/core/toolchain/specs.js +351 -0
- package/dist/core/toolchain/types.js +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/userconfig.js +66 -0
- package/dist/core/validate/context.js +139 -0
- package/dist/core/validate/index.js +5 -0
- package/dist/core/validate/registry.js +58 -0
- package/dist/core/validate/run.js +131 -0
- package/dist/core/validate/suppressions.js +91 -0
- package/dist/core/validate/types.js +2 -0
- package/dist/core/yamledit.js +51 -0
- package/dist/index.js +389 -0
- package/dist/server.js +1914 -0
- package/dist/surfaces/baldursgate3/compat.js +164 -0
- package/dist/surfaces/baldursgate3/identity.js +110 -0
- package/dist/surfaces/baldursgate3/index/build.js +331 -0
- package/dist/surfaces/baldursgate3/index/index.js +17 -0
- package/dist/surfaces/baldursgate3/index/info.js +40 -0
- package/dist/surfaces/baldursgate3/index/parsers/loca.js +41 -0
- package/dist/surfaces/baldursgate3/index/parsers/lsx.js +137 -0
- package/dist/surfaces/baldursgate3/index/parsers/stats.js +45 -0
- package/dist/surfaces/baldursgate3/index/parsers/treasuretable.js +57 -0
- package/dist/surfaces/baldursgate3/index/parsers/xmlutil.js +142 -0
- package/dist/surfaces/baldursgate3/index/queries.js +327 -0
- package/dist/surfaces/baldursgate3/index/schema.js +62 -0
- package/dist/surfaces/baldursgate3/index.js +3 -0
- package/dist/surfaces/baldursgate3/logs.js +278 -0
- package/dist/surfaces/baldursgate3/modsettings.js +115 -0
- package/dist/surfaces/baldursgate3/pak.js +153 -0
- package/dist/surfaces/baldursgate3/settings.js +49 -0
- package/dist/surfaces/baldursgate3/surface.js +232 -0
- package/dist/surfaces/baldursgate3/validators/bridge.js +7 -0
- package/dist/surfaces/baldursgate3/validators/files.js +476 -0
- package/dist/surfaces/baldursgate3/validators/items.js +1001 -0
- package/dist/surfaces/baldursgate3/validators/progression.js +734 -0
- package/dist/surfaces/baldursgate3/validators/stats.js +1076 -0
- package/dist/surfaces/baldursgate3/validators/story.js +790 -0
- package/dist/surfaces/baldursgate3/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/compat.js +223 -0
- package/dist/surfaces/cyberpunk2077/index/build.js +231 -0
- package/dist/surfaces/cyberpunk2077/index/index.js +17 -0
- package/dist/surfaces/cyberpunk2077/index/info.js +51 -0
- package/dist/surfaces/cyberpunk2077/index/model.js +52 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweak.js +483 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweakxl-yaml.js +183 -0
- package/dist/surfaces/cyberpunk2077/index/queries.js +504 -0
- package/dist/surfaces/cyberpunk2077/index/resource.js +135 -0
- package/dist/surfaces/cyberpunk2077/index/schema.js +90 -0
- package/dist/surfaces/cyberpunk2077/index.js +3 -0
- package/dist/surfaces/cyberpunk2077/logs.js +742 -0
- package/dist/surfaces/cyberpunk2077/paths.js +12 -0
- package/dist/surfaces/cyberpunk2077/surface.js +218 -0
- package/dist/surfaces/cyberpunk2077/validators/bridge.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/cr2w.js +349 -0
- package/dist/surfaces/cyberpunk2077/validators/garments.js +1181 -0
- package/dist/surfaces/cyberpunk2077/validators/mesh.js +338 -0
- package/dist/surfaces/cyberpunk2077/validators/modsettings.js +120 -0
- package/dist/surfaces/cyberpunk2077/validators/packaging.js +463 -0
- package/dist/surfaces/cyberpunk2077/validators/red4ext.js +318 -0
- package/dist/surfaces/cyberpunk2077/validators/redscript.js +193 -0
- package/dist/surfaces/cyberpunk2077/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/tweaks.js +768 -0
- package/dist/surfaces/cyberpunk2077/validators/vehicles.js +531 -0
- package/dist/surfaces/cyberpunk2077/validators/xl.js +308 -0
- package/dist/surfaces/eldenring/binders.js +111 -0
- package/dist/surfaces/eldenring/compat.js +166 -0
- package/dist/surfaces/eldenring/dcx.js +49 -0
- package/dist/surfaces/eldenring/index.js +13 -0
- package/dist/surfaces/eldenring/loaders.js +132 -0
- package/dist/surfaces/eldenring/logs.js +67 -0
- package/dist/surfaces/eldenring/paramdef.js +256 -0
- package/dist/surfaces/eldenring/params.js +129 -0
- package/dist/surfaces/eldenring/surface.js +121 -0
- package/dist/surfaces/eldenring/tools.js +52 -0
- package/dist/surfaces/eldenring/validators/binders.js +193 -0
- package/dist/surfaces/eldenring/validators/packages.js +159 -0
- package/dist/surfaces/eldenring/validators/params.js +334 -0
- package/dist/surfaces/eldenring/validators/profiles.js +287 -0
- package/dist/surfaces/index.js +34 -0
- package/dist/surfaces/nivalisnights/compat.js +229 -0
- package/dist/surfaces/nivalisnights/index/build.js +310 -0
- package/dist/surfaces/nivalisnights/index/index.js +5 -0
- package/dist/surfaces/nivalisnights/index/queries.js +231 -0
- package/dist/surfaces/nivalisnights/index.js +4 -0
- package/dist/surfaces/nivalisnights/logs.js +473 -0
- package/dist/surfaces/nivalisnights/surface.js +195 -0
- package/dist/surfaces/skyrimse/archives.js +229 -0
- package/dist/surfaces/skyrimse/compat.js +163 -0
- package/dist/surfaces/skyrimse/index.js +12 -0
- package/dist/surfaces/skyrimse/loadorder.js +174 -0
- package/dist/surfaces/skyrimse/logs.js +241 -0
- package/dist/surfaces/skyrimse/plugins.js +149 -0
- package/dist/surfaces/skyrimse/records.js +94 -0
- package/dist/surfaces/skyrimse/surface.js +240 -0
- package/dist/surfaces/skyrimse/validators/archive-records.js +165 -0
- package/dist/surfaces/skyrimse/validators/archives.js +110 -0
- package/dist/surfaces/skyrimse/validators/plugins.js +374 -0
- package/dist/surfaces/skyrimse/validators/records.js +117 -0
- package/dist/surfaces/stardewvalley/compat.js +190 -0
- package/dist/surfaces/stardewvalley/config.js +42 -0
- package/dist/surfaces/stardewvalley/contentpatcher.js +203 -0
- package/dist/surfaces/stardewvalley/index/index.js +229 -0
- package/dist/surfaces/stardewvalley/index/wiki.js +190 -0
- package/dist/surfaces/stardewvalley/index.js +12 -0
- package/dist/surfaces/stardewvalley/logs.js +307 -0
- package/dist/surfaces/stardewvalley/mods.js +182 -0
- package/dist/surfaces/stardewvalley/surface.js +244 -0
- package/dist/surfaces/stardewvalley/validators/content.js +321 -0
- package/dist/surfaces/stardewvalley/validators/fields.js +147 -0
- package/dist/surfaces/stardewvalley/validators/loadorder.js +69 -0
- package/dist/surfaces/stardewvalley/validators/manifest.js +221 -0
- package/dist/surfaces/subnautica2/compat.js +138 -0
- package/dist/surfaces/subnautica2/index.js +5 -0
- package/dist/surfaces/subnautica2/logs.js +129 -0
- package/dist/surfaces/subnautica2/paks.js +173 -0
- package/dist/surfaces/subnautica2/surface.js +151 -0
- package/dist/surfaces/subnautica2/validators/packaging.js +298 -0
- package/dist/surfaces/valheim/compat.js +171 -0
- package/dist/surfaces/valheim/index/index.js +301 -0
- package/dist/surfaces/valheim/index/tables.js +103 -0
- package/dist/surfaces/valheim/index.js +11 -0
- package/dist/surfaces/valheim/logs.js +197 -0
- package/dist/surfaces/valheim/packaging.js +131 -0
- package/dist/surfaces/valheim/plugins.js +270 -0
- package/dist/surfaces/valheim/references.js +121 -0
- package/dist/surfaces/valheim/surface.js +223 -0
- package/dist/surfaces/valheim/validators/packaging.js +218 -0
- package/dist/surfaces/valheim/validators/plugins.js +287 -0
- package/dist/surfaces/valheim/validators/references.js +169 -0
- package/knowledge/baldursgate3/animations.gr2.yaml +166 -0
- package/knowledge/baldursgate3/audio.voice.yaml +182 -0
- package/knowledge/baldursgate3/audio.wwise.yaml +104 -0
- package/knowledge/baldursgate3/charactercreation.heads-hair.yaml +193 -0
- package/knowledge/baldursgate3/communitylibrary.shared-content.yaml +129 -0
- package/knowledge/baldursgate3/compatibilityframework.api.yaml +176 -0
- package/knowledge/baldursgate3/crash.causes.yaml +90 -0
- package/knowledge/baldursgate3/dialogue.dialogbank.yaml +120 -0
- package/knowledge/baldursgate3/dialogue.files.yaml +236 -0
- package/knowledge/baldursgate3/dialogue.osiris-goals.yaml +386 -0
- package/knowledge/baldursgate3/dialogue.yaml +122 -0
- package/knowledge/baldursgate3/feats.classes.yaml +164 -0
- package/knowledge/baldursgate3/items.cloth-physics.yaml +167 -0
- package/knowledge/baldursgate3/items.roottemplates.yaml +320 -0
- package/knowledge/baldursgate3/items.visualbank.yaml +628 -0
- package/knowledge/baldursgate3/journal.quests.yaml +214 -0
- package/knowledge/baldursgate3/loadorder.modsettings.yaml +128 -0
- package/knowledge/baldursgate3/localization.handles.yaml +92 -0
- package/knowledge/baldursgate3/misc.encoding-and-practice.yaml +111 -0
- package/knowledge/baldursgate3/origins.companions.yaml +144 -0
- package/knowledge/baldursgate3/osiris.signatures.yaml +211 -0
- package/knowledge/baldursgate3/packaging.meta.yaml +155 -0
- package/knowledge/baldursgate3/packaging.paks.yaml +79 -0
- package/knowledge/baldursgate3/process.launch.yaml +34 -0
- package/knowledge/baldursgate3/progressions.yaml +92 -0
- package/knowledge/baldursgate3/publishing.modio-nexus.yaml +170 -0
- package/knowledge/baldursgate3/publishing.toolkit-modio.yaml +135 -0
- package/knowledge/baldursgate3/races.progression.yaml +228 -0
- package/knowledge/baldursgate3/scriptextender.console.yaml +257 -0
- package/knowledge/baldursgate3/scriptextender.yaml +252 -0
- package/knowledge/baldursgate3/stats.boosts.yaml +293 -0
- package/knowledge/baldursgate3/stats.equipment-kits.yaml +69 -0
- package/knowledge/baldursgate3/stats.interrupts.yaml +45 -0
- package/knowledge/baldursgate3/stats.itemcombos.yaml +65 -0
- package/knowledge/baldursgate3/stats.spells.yaml +293 -0
- package/knowledge/baldursgate3/stats.weapons.yaml +210 -0
- package/knowledge/baldursgate3/toolchain.divine.yaml +179 -0
- package/knowledge/baldursgate3/toolchain.lslib-formats.yaml +242 -0
- package/knowledge/baldursgate3/toolkit.levels.yaml +177 -0
- package/knowledge/baldursgate3/ui.icons-atlas.yaml +78 -0
- package/knowledge/baldursgate3/ui.icons.yaml +412 -0
- package/knowledge/baldursgate3/ui.mcm.yaml +161 -0
- package/knowledge/baldursgate3/vfx.materials.yaml +174 -0
- package/knowledge/baldursgate3/visuals.characterfix.yaml +73 -0
- package/knowledge/cyberpunk2077/animations.anims.yaml +118 -0
- package/knowledge/cyberpunk2077/anims.archivexl.yaml +163 -0
- package/knowledge/cyberpunk2077/anims.authoring.yaml +579 -0
- package/knowledge/cyberpunk2077/anims.encoder.yaml +156 -0
- package/knowledge/cyberpunk2077/anims.graph.yaml +629 -0
- package/knowledge/cyberpunk2077/anims.method.yaml +360 -0
- package/knowledge/cyberpunk2077/anims.pipeline.yaml +352 -0
- package/knowledge/cyberpunk2077/anims.roundtrip.yaml +222 -0
- package/knowledge/cyberpunk2077/anims.sets.yaml +176 -0
- package/knowledge/cyberpunk2077/archivexl.bodytypes.yaml +149 -0
- package/knowledge/cyberpunk2077/archivexl.dynamic-appearances.yaml +218 -0
- package/knowledge/cyberpunk2077/archivexl.manifest.yaml +214 -0
- package/knowledge/cyberpunk2077/audio.events.yaml +168 -0
- package/knowledge/cyberpunk2077/audio.sounds.yaml +164 -0
- package/knowledge/cyberpunk2077/audio.voicesets.yaml +146 -0
- package/knowledge/cyberpunk2077/cet.sandbox.yaml +219 -0
- package/knowledge/cyberpunk2077/clothing.garments.yaml +283 -0
- package/knowledge/cyberpunk2077/clothing.refits.yaml +215 -0
- package/knowledge/cyberpunk2077/codeware.overview.yaml +111 -0
- package/knowledge/cyberpunk2077/codeware.systems.yaml +192 -0
- package/knowledge/cyberpunk2077/drones.combat.yaml +416 -0
- package/knowledge/cyberpunk2077/drones.control.yaml +552 -0
- package/knowledge/cyberpunk2077/entities.appearance.yaml +166 -0
- package/knowledge/cyberpunk2077/equipmentex.slots.yaml +151 -0
- package/knowledge/cyberpunk2077/fx.effects.yaml +246 -0
- package/knowledge/cyberpunk2077/fx.lights.yaml +171 -0
- package/knowledge/cyberpunk2077/hair.modding.yaml +145 -0
- package/knowledge/cyberpunk2077/items.wiring.yaml +246 -0
- package/knowledge/cyberpunk2077/loadorder.archives.yaml +71 -0
- package/knowledge/cyberpunk2077/mesh.blender.yaml +209 -0
- package/knowledge/cyberpunk2077/nativedb.dump.yaml +98 -0
- package/knowledge/cyberpunk2077/npc.appearance.yaml +275 -0
- package/knowledge/cyberpunk2077/npc.behaviour.yaml +128 -0
- package/knowledge/cyberpunk2077/npc.locomotion.yaml +490 -0
- package/knowledge/cyberpunk2077/packaging.deploy.yaml +175 -0
- package/knowledge/cyberpunk2077/packaging.variants.yaml +176 -0
- package/knowledge/cyberpunk2077/player.control.yaml +918 -0
- package/knowledge/cyberpunk2077/process.assetbuild.yaml +154 -0
- package/knowledge/cyberpunk2077/process.lessons.yaml +276 -0
- package/knowledge/cyberpunk2077/quests.minor.yaml +46 -0
- package/knowledge/cyberpunk2077/quests.scenes.yaml +282 -0
- package/knowledge/cyberpunk2077/quickhacks.tiers.yaml +41 -0
- package/knowledge/cyberpunk2077/red4ext.api.yaml +645 -0
- package/knowledge/cyberpunk2077/red4ext.plugins.yaml +124 -0
- package/knowledge/cyberpunk2077/redhottools.hotreload.yaml +245 -0
- package/knowledge/cyberpunk2077/redscript.compile-log.yaml +76 -0
- package/knowledge/cyberpunk2077/redscript.modules.yaml +118 -0
- package/knowledge/cyberpunk2077/toolchain.wolvenkit.yaml +163 -0
- package/knowledge/cyberpunk2077/tweakdb.loot-recipes.yaml +184 -0
- package/knowledge/cyberpunk2077/tweakdb.melee.yaml +352 -0
- package/knowledge/cyberpunk2077/tweakdb.weapons.yaml +325 -0
- package/knowledge/cyberpunk2077/tweakxl.hotreload.yaml +151 -0
- package/knowledge/cyberpunk2077/tweakxl.yaml-syntax.yaml +208 -0
- package/knowledge/cyberpunk2077/ui.icons.yaml +259 -0
- package/knowledge/cyberpunk2077/ui.inkatlas.yaml +81 -0
- package/knowledge/cyberpunk2077/ui.modsettings.yaml +204 -0
- package/knowledge/cyberpunk2077/vehicles.control.yaml +475 -0
- package/knowledge/cyberpunk2077/vehicles.dashboard-ui.yaml +203 -0
- package/knowledge/cyberpunk2077/vehicles.entities.yaml +195 -0
- package/knowledge/cyberpunk2077/vehicles.wiring.yaml +137 -0
- package/knowledge/cyberpunk2077/wolvenkit.cli.yaml +167 -0
- package/knowledge/cyberpunk2077/workspot.system.yaml +55 -0
- package/knowledge/cyberpunk2077/world.interactions.yaml +41 -0
- package/knowledge/cyberpunk2077/world.loot-container.yaml +167 -0
- package/knowledge/cyberpunk2077/world.sectors.yaml +233 -0
- package/knowledge/eldenring/formats.binders.yaml +237 -0
- package/knowledge/eldenring/launcher.me3.yaml +230 -0
- package/knowledge/eldenring/launcher.modengine2.yaml +119 -0
- package/knowledge/eldenring/params.paramdef.yaml +149 -0
- package/knowledge/eldenring/params.regulation.yaml +142 -0
- package/knowledge/eldenring/saves.online.yaml +118 -0
- package/knowledge/eldenring/toolchain.cli.yaml +256 -0
- package/knowledge/nivalisnights/modding.runtime.yaml +157 -0
- package/knowledge/skyrimse/archives.bsa.yaml +212 -0
- package/knowledge/skyrimse/install.detection.yaml +41 -0
- package/knowledge/skyrimse/loadorder.files.yaml +242 -0
- package/knowledge/skyrimse/plugins.esl.yaml +189 -0
- package/knowledge/skyrimse/plugins.format.yaml +224 -0
- package/knowledge/skyrimse/skse.runtime.yaml +283 -0
- package/knowledge/skyrimse/toolchain.xedit-spriggit.yaml +336 -0
- package/knowledge/stardewvalley/contentpatcher.format.yaml +338 -0
- package/knowledge/stardewvalley/distribution.channels.yaml +236 -0
- package/knowledge/stardewvalley/game.versions.yaml +202 -0
- package/knowledge/stardewvalley/install.layout.yaml +277 -0
- package/knowledge/stardewvalley/smapi.loader.yaml +284 -0
- package/knowledge/stardewvalley/smapi.logs.yaml +329 -0
- package/knowledge/stardewvalley/smapi.manifest.yaml +293 -0
- package/knowledge/stardewvalley/toolchain.build.yaml +261 -0
- package/knowledge/stardewvalley/wiki.dataformat.yaml +130 -0
- package/knowledge/subnautica2/game.build.yaml +331 -0
- package/knowledge/subnautica2/modding.state.yaml +210 -0
- package/knowledge/subnautica2/ue5.containers.yaml +72 -0
- package/knowledge/subnautica2/ue5.paks.yaml +203 -0
- package/knowledge/subnautica2/ue5.tooling.yaml +175 -0
- package/knowledge/subnautica2/ue5.ue4ss.yaml +203 -0
- package/knowledge/valheim/bepinex.loader.yaml +247 -0
- package/knowledge/valheim/bepinex.plugins.yaml +251 -0
- package/knowledge/valheim/game.versions.yaml +321 -0
- package/knowledge/valheim/install.layout.yaml +216 -0
- package/knowledge/valheim/jotunn.library.yaml +334 -0
- package/knowledge/valheim/thunderstore.packaging.yaml +217 -0
- package/knowledge/valheim/toolchain.build.yaml +111 -0
- package/package.json +29 -0
- package/scaffolds/baldursgate3/new-class-injection.yaml +148 -0
- package/scaffolds/baldursgate3/new-item.yaml +264 -0
- package/scaffolds/baldursgate3/new-mod.yaml +174 -0
- package/scaffolds/baldursgate3/new-passive.yaml +100 -0
- package/scaffolds/baldursgate3/new-project.yaml +118 -0
- package/scaffolds/baldursgate3/new-quest-stub.yaml +159 -0
- package/scaffolds/baldursgate3/new-spell.yaml +102 -0
- package/scaffolds/baldursgate3/new-status.yaml +119 -0
- package/scaffolds/baldursgate3/sequences/subclass-kit.yaml +50 -0
- package/scaffolds/cyberpunk2077/edit-tweak.yaml +70 -0
- package/scaffolds/cyberpunk2077/mesh-export-preset.yaml +170 -0
- package/scaffolds/cyberpunk2077/new-entity-patch.yaml +255 -0
- package/scaffolds/cyberpunk2077/new-garment-refit.yaml +279 -0
- package/scaffolds/cyberpunk2077/new-item-chain.yaml +548 -0
- package/scaffolds/cyberpunk2077/new-localization.yaml +130 -0
- package/scaffolds/cyberpunk2077/new-mod-settings.yaml +133 -0
- package/scaffolds/cyberpunk2077/new-player-replacer.yaml +1003 -0
- package/scaffolds/cyberpunk2077/new-project.yaml +157 -0
- package/scaffolds/cyberpunk2077/new-sector-node.yaml +156 -0
- package/scaffolds/cyberpunk2077/new-tweak.yaml +121 -0
- package/scaffolds/cyberpunk2077/new-vehicle-livery.yaml +148 -0
- package/scaffolds/cyberpunk2077/new-workspot-entity.yaml +177 -0
- package/scaffolds/cyberpunk2077/sequences/iconic-weapon.yaml +72 -0
- package/scaffolds/cyberpunk2077/sequences/quickhack-takeover.yaml +213 -0
- package/scaffolds/stardewvalley/new-cp-patch.yaml +106 -0
- package/scaffolds/stardewvalley/new-csharp-mod.yaml +195 -0
- package/scaffolds/stardewvalley/new-project.yaml +184 -0
- package/scaffolds/stardewvalley/sequences/content-pack.yaml +31 -0
- package/scripts/mw-tool.mjs +184 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { firstExisting, pathExists, readTextIfExists } from "./fsutil.js";
|
|
4
|
+
export function steamRootCandidates(platform = process.platform, env = process.env, home = os.homedir()) {
|
|
5
|
+
if (platform === "win32") {
|
|
6
|
+
return [
|
|
7
|
+
"C:/Program Files (x86)/Steam",
|
|
8
|
+
"C:/Program Files/Steam",
|
|
9
|
+
...(env.LOCALAPPDATA ? [path.win32.join(env.LOCALAPPDATA, "..", "Steam")] : []),
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
if (!home)
|
|
13
|
+
return [];
|
|
14
|
+
if (platform === "darwin")
|
|
15
|
+
return [path.posix.join(home, "Library", "Application Support", "Steam")];
|
|
16
|
+
return [
|
|
17
|
+
path.posix.join(home, ".steam", "steam"),
|
|
18
|
+
path.posix.join(home, ".local", "share", "Steam"),
|
|
19
|
+
path.posix.join(home, ".var", "app", "com.valvesoftware.Steam", ".local", "share", "Steam"),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
export async function findSteamRoot() {
|
|
23
|
+
if (process.env.MODWRIGHT_STEAM_ROOT) {
|
|
24
|
+
const override = process.env.MODWRIGHT_STEAM_ROOT;
|
|
25
|
+
if (await pathExists(override))
|
|
26
|
+
return override;
|
|
27
|
+
}
|
|
28
|
+
return firstExisting(steamRootCandidates());
|
|
29
|
+
}
|
|
30
|
+
export async function steamLibraries() {
|
|
31
|
+
const root = await findSteamRoot();
|
|
32
|
+
if (!root)
|
|
33
|
+
return [];
|
|
34
|
+
const libraries = new Map();
|
|
35
|
+
const add = (dir) => {
|
|
36
|
+
const common = path.join(dir, "steamapps", "common");
|
|
37
|
+
const key = process.platform === "win32" ? path.resolve(common).toLowerCase() : path.resolve(common);
|
|
38
|
+
if (!libraries.has(key))
|
|
39
|
+
libraries.set(key, common);
|
|
40
|
+
};
|
|
41
|
+
add(root);
|
|
42
|
+
const vdf = await readTextIfExists(path.join(root, "steamapps", "libraryfolders.vdf"));
|
|
43
|
+
if (vdf) {
|
|
44
|
+
for (const match of vdf.matchAll(/"path"\s+"([^"]+)"/g)) {
|
|
45
|
+
const raw = match[1];
|
|
46
|
+
if (!raw)
|
|
47
|
+
continue;
|
|
48
|
+
add(raw.replace(/\\\\/g, "\\"));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return [...libraries.values()];
|
|
52
|
+
}
|
|
53
|
+
export async function findInSteam(folderNames) {
|
|
54
|
+
const found = [];
|
|
55
|
+
for (const library of await steamLibraries()) {
|
|
56
|
+
for (const name of folderNames) {
|
|
57
|
+
const candidate = path.join(library, name);
|
|
58
|
+
if (await pathExists(candidate))
|
|
59
|
+
found.push(candidate);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return found;
|
|
63
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { pathExists, resolveCandidate } from "./fsutil.js";
|
|
2
|
+
export function roots(specs) {
|
|
3
|
+
return specs.map((spec) => ({ ...spec, exists: false }));
|
|
4
|
+
}
|
|
5
|
+
export function logs(specs) {
|
|
6
|
+
return specs.map((spec) => {
|
|
7
|
+
const candidates = [...(spec.path ? [spec.path] : []), ...(spec.candidates ?? [])];
|
|
8
|
+
const { path: _path, candidates: _candidates, ...rest } = spec;
|
|
9
|
+
return { ...rest, path: candidates[0] ?? "", candidates, exists: false };
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export async function resolveRoots(surface, install) {
|
|
13
|
+
return Promise.all(surface.modRoots(install).map(async (root) => ({
|
|
14
|
+
...root,
|
|
15
|
+
exists: await pathExists(root.path),
|
|
16
|
+
})));
|
|
17
|
+
}
|
|
18
|
+
export async function resolveLogs(surface, install) {
|
|
19
|
+
const declared = surface.logs?.(install) ?? [];
|
|
20
|
+
return Promise.all(declared.map(async (log) => {
|
|
21
|
+
const hit = await resolveCandidate(log.candidates);
|
|
22
|
+
return hit
|
|
23
|
+
? { ...log, path: hit.path, resolvedFrom: hit.candidate, exists: true }
|
|
24
|
+
: { ...log, exists: false };
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
export function dedupeByRoot(installs) {
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
return installs.filter((install) => {
|
|
30
|
+
const key = install.root.toLowerCase();
|
|
31
|
+
if (seen.has(key))
|
|
32
|
+
return false;
|
|
33
|
+
seen.add(key);
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { lookupProbe } from "./registry.js";
|
|
2
|
+
export function checkRowProbes(row, game, lookup = lookupProbe) {
|
|
3
|
+
const toCheck = [...(row.setup ?? []), ...(row.probe?.probes ?? [])];
|
|
4
|
+
if (toCheck.length === 0)
|
|
5
|
+
return [];
|
|
6
|
+
const refusals = [];
|
|
7
|
+
for (const probe of toCheck) {
|
|
8
|
+
const entry = lookup(probe.kind);
|
|
9
|
+
if (!entry) {
|
|
10
|
+
refusals.push({
|
|
11
|
+
probeId: probe.id,
|
|
12
|
+
kind: probe.kind,
|
|
13
|
+
reason: `unknown probe kind "${probe.kind}" — not in ModWright's probe catalogue; the row is refused rather than run against something no bridge implements.`,
|
|
14
|
+
});
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (entry.game !== game) {
|
|
18
|
+
refusals.push({
|
|
19
|
+
probeId: probe.id,
|
|
20
|
+
kind: probe.kind,
|
|
21
|
+
reason: `probe "${probe.kind}" is a ${entry.game} probe, but this plan's game is ${game}.`,
|
|
22
|
+
});
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (entry.status === "contradicted") {
|
|
26
|
+
refusals.push({
|
|
27
|
+
probeId: probe.id,
|
|
28
|
+
kind: probe.kind,
|
|
29
|
+
reason: `probe "${probe.kind}" is marked contradicted in the catalogue and is refused rather than run.`,
|
|
30
|
+
});
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (probe.effect !== entry.effect) {
|
|
34
|
+
refusals.push({
|
|
35
|
+
probeId: probe.id,
|
|
36
|
+
kind: probe.kind,
|
|
37
|
+
reason: `probe "${probe.kind}" is an effect "${entry.effect}" kind in the catalogue but the row sends it as "${probe.effect}"; the effect is the catalogue's to choose, and a mutate kind sent as a read would bypass the bridge's allowMutate gate.`,
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const parsed = entry.paramsSchema.safeParse(probe.params);
|
|
42
|
+
if (!parsed.success) {
|
|
43
|
+
refusals.push({
|
|
44
|
+
probeId: probe.id,
|
|
45
|
+
kind: probe.kind,
|
|
46
|
+
reason: `params for "${probe.kind}" do not match its catalogue schema: ${parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return refusals;
|
|
51
|
+
}
|
|
52
|
+
export function worstAttestation(kinds, lookup = lookupProbe) {
|
|
53
|
+
let worst;
|
|
54
|
+
for (const kind of kinds) {
|
|
55
|
+
const entry = lookup(kind);
|
|
56
|
+
if (!entry)
|
|
57
|
+
continue;
|
|
58
|
+
if (entry.status === "unverified")
|
|
59
|
+
worst = "unverified";
|
|
60
|
+
else if (worst === undefined)
|
|
61
|
+
worst = entry.status;
|
|
62
|
+
}
|
|
63
|
+
return worst;
|
|
64
|
+
}
|
|
65
|
+
export function rowProbeKinds(row) {
|
|
66
|
+
return row.probe?.probes.map((p) => p.kind) ?? [];
|
|
67
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { PROBE_ATTESTATIONS, PROBE_CATALOG, lookupProbe, probesForGame } from "./registry.js";
|
|
3
|
+
export { checkRowProbes, rowProbeKinds, worstAttestation } from "./check.js";
|
|
4
|
+
export { DEFAULT_TEST_PLAN_GLOBS, loadTestPlanFile, loadTestPlans, resolveTestPlan, resolveTestPlanFiles, saveTestPlan, } from "./load.js";
|
|
5
|
+
export { runTestPlan } from "./run.js";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { globToRegExp } from "../validate/index.js";
|
|
5
|
+
import { pathExists } from "../fsutil.js";
|
|
6
|
+
import { executePlan, planFile } from "../safety/index.js";
|
|
7
|
+
import { stringifyPreserving } from "../yamledit.js";
|
|
8
|
+
import { parseTestPlan } from "./types.js";
|
|
9
|
+
export const DEFAULT_TEST_PLAN_GLOBS = ["tests/*.yaml"];
|
|
10
|
+
function toPosix(p) {
|
|
11
|
+
return p.split(path.sep).join("/");
|
|
12
|
+
}
|
|
13
|
+
async function expandGlob(root, pattern) {
|
|
14
|
+
const posix = toPosix(pattern);
|
|
15
|
+
const slash = posix.lastIndexOf("/");
|
|
16
|
+
const dirPart = slash === -1 ? "" : posix.slice(0, slash);
|
|
17
|
+
const filePart = slash === -1 ? posix : posix.slice(slash + 1);
|
|
18
|
+
const dirAbs = dirPart ? path.join(root, dirPart) : root;
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = await fs.readdir(dirAbs);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const re = globToRegExp(filePart);
|
|
27
|
+
return entries
|
|
28
|
+
.filter((name) => re.test(name))
|
|
29
|
+
.sort()
|
|
30
|
+
.map((name) => path.join(dirAbs, name));
|
|
31
|
+
}
|
|
32
|
+
export async function resolveTestPlanFiles(project) {
|
|
33
|
+
const globs = project.project.testPlans ?? DEFAULT_TEST_PLAN_GLOBS;
|
|
34
|
+
const found = [];
|
|
35
|
+
for (const glob of globs) {
|
|
36
|
+
found.push(...(await expandGlob(project.root, glob)));
|
|
37
|
+
}
|
|
38
|
+
return [...new Set(found)];
|
|
39
|
+
}
|
|
40
|
+
export async function loadTestPlanFile(absPath) {
|
|
41
|
+
let text;
|
|
42
|
+
try {
|
|
43
|
+
text = await fs.readFile(absPath, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return { path: absPath, problems: [`${absPath}: cannot read file: ${err.message}`] };
|
|
47
|
+
}
|
|
48
|
+
let raw;
|
|
49
|
+
try {
|
|
50
|
+
raw = parseYaml(text);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return { path: absPath, problems: [`${absPath}: YAML parse error: ${err.message}`] };
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return { path: absPath, plan: parseTestPlan(raw), problems: [] };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
return { path: absPath, problems: [`${absPath}: ${err.message}`] };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function loadTestPlans(project) {
|
|
63
|
+
const files = await resolveTestPlanFiles(project);
|
|
64
|
+
const plans = [];
|
|
65
|
+
const problems = [];
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
const result = await loadTestPlanFile(file);
|
|
68
|
+
if (result.plan)
|
|
69
|
+
plans.push({ path: file, plan: result.plan });
|
|
70
|
+
problems.push(...result.problems);
|
|
71
|
+
}
|
|
72
|
+
return { plans, problems };
|
|
73
|
+
}
|
|
74
|
+
export async function resolveTestPlan(project, planRef) {
|
|
75
|
+
const candidate = path.isAbsolute(planRef) ? planRef : path.join(project.root, planRef);
|
|
76
|
+
if (await pathExists(candidate)) {
|
|
77
|
+
const result = await loadTestPlanFile(candidate);
|
|
78
|
+
if (result.plan)
|
|
79
|
+
return { path: candidate, plan: result.plan };
|
|
80
|
+
throw new Error(result.problems.join("\n"));
|
|
81
|
+
}
|
|
82
|
+
const { plans, problems } = await loadTestPlans(project);
|
|
83
|
+
if (problems.length > 0 && plans.every((p) => p.plan.planId !== planRef)) {
|
|
84
|
+
throw new Error(`No test plan "${planRef}" loaded, and ${problems.length} plan file(s) had problems:\n${problems.join("\n")}`);
|
|
85
|
+
}
|
|
86
|
+
return plans.find((p) => p.plan.planId === planRef);
|
|
87
|
+
}
|
|
88
|
+
export async function saveTestPlan(filePath, plan, options = {}) {
|
|
89
|
+
const content = stringifyPreserving(await fs.readFile(filePath, "utf8").catch(() => undefined), plan, { lineWidth: 0 });
|
|
90
|
+
const writePlan = {
|
|
91
|
+
purpose: `write results back to ${path.basename(filePath)}`,
|
|
92
|
+
operations: [planFile(filePath, content)],
|
|
93
|
+
backupRoot: options.backupRoot,
|
|
94
|
+
};
|
|
95
|
+
return executePlan(writePlan, {
|
|
96
|
+
mode: options.mode ?? "dry-run",
|
|
97
|
+
now: options.now,
|
|
98
|
+
confidence: { level: "high", assumptions: [], verifiedBy: ["bridge result"] },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const PROBE_ATTESTATIONS = ["attested", "unverified", "contradicted"];
|
|
3
|
+
const str = () => z.string().min(1);
|
|
4
|
+
const record = () => z.record(z.unknown());
|
|
5
|
+
const BG3_PROBES = [
|
|
6
|
+
{
|
|
7
|
+
kind: "bg3.stat.exists",
|
|
8
|
+
game: "baldursgate3",
|
|
9
|
+
context: "server",
|
|
10
|
+
effect: "read",
|
|
11
|
+
status: "attested",
|
|
12
|
+
summary: "A stats entry loaded under this name.",
|
|
13
|
+
citation: "bg3.stat.exists",
|
|
14
|
+
paramsSchema: z.object({ name: str() }).strict(),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
kind: "bg3.stat.fields",
|
|
18
|
+
game: "baldursgate3",
|
|
19
|
+
context: "server",
|
|
20
|
+
effect: "read",
|
|
21
|
+
status: "attested",
|
|
22
|
+
summary: "Named fields match expected raw text.",
|
|
23
|
+
citation: "bg3.stat.fields",
|
|
24
|
+
paramsSchema: z.object({ name: str(), fields: z.array(str()).min(1) }).strict(),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
kind: "bg3.template.exists",
|
|
28
|
+
game: "baldursgate3",
|
|
29
|
+
context: "server",
|
|
30
|
+
effect: "read",
|
|
31
|
+
status: "attested",
|
|
32
|
+
summary: "A RootTemplate resolves in the engine (Ext.Template.GetTemplate), not just its stats entry.",
|
|
33
|
+
citation: "bg3.template.exists; attested(engine) 2026-09-10",
|
|
34
|
+
paramsSchema: z.object({ mapKey: str() }).strict(),
|
|
35
|
+
evidence: "Verified live at the console (2026-09-10): Ext.Template.GetTemplate(mapKey) — nil for a loose Public/<Mod>/RootTemplates/*.lsx, the full template object once the same node was converted to a sibling .lsf. The bridge handler itself was then exercised the same day (request req-mtvpgcjk-e514f0bc, extender runtime 32): the vanilla CONT_GEN_Chest_Heavy_A MapKey 0b90bf9d-66fd-4741-be1f-39181efca97a → pass with name, templateType item and fileName .../Public/Shared/RootTemplates/_merged.lsf; a bogus MapKey → fail with present:false; no errors. See also knowledge fact bg3.items.roottemplates.loose-lsx-in-roottemplates-is-not-loaded-a-separate-lsf-is.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
kind: "bg3.lua.eval",
|
|
39
|
+
game: "baldursgate3",
|
|
40
|
+
context: "server",
|
|
41
|
+
effect: "mutate",
|
|
42
|
+
status: "attested",
|
|
43
|
+
summary: "Evaluate one Lua expression or chunk in the server Lua state and return its value, the way a pasted _D(...) console line would. Always effect mutate: the bridge cannot know what a chunk does, so it never runs one the request did not opt into with allowMutate.",
|
|
44
|
+
citation: "bg3.lua.eval; added 2026-09-11 so console lines pasted by hand run through ModWright",
|
|
45
|
+
paramsSchema: z.object({ code: str(), maxBytes: z.number().int().positive().max(65536).optional() }).strict(),
|
|
46
|
+
evidence: "Verified live (2026-09-11, extender runtime 32): the first request errored with `bad argument #2 to 'loader' (table expected, got string)` — the extender's loader takes the code alone (its optional second argument is an environment table), so the chunk name was dropped; after a console `reset` the next request (req-mtx6wdoe-008d3beb) passed `1 + 1` → 2, `Ext.Mod.GetMod(uuid).Info.Directory` → the test mod's folder name, `Ext.Stats.Get(...).SpellType` → \"Projectile\", `Osi.DB_Players:Get(nil)` → one row, a two-value chunk → [1, \"two\"], `error(\"deliberate\")` → status error, and an `Ext.Entity.Get(host).Health` read → the coerced component; on a fresh session (req-mtx6ywlv-8535fcd9) `Osi.TemplateAddTo` ran (returnCount 0) and `maxBytes: 16` truncated the Health read to a 16-byte prefix with truncated: true. A read-labelled eval was skipped by the handler, as designed."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
kind: "bg3.mod.active",
|
|
50
|
+
game: "baldursgate3",
|
|
51
|
+
context: "server",
|
|
52
|
+
effect: "read",
|
|
53
|
+
status: "attested",
|
|
54
|
+
summary: "The module is loaded, and its version.",
|
|
55
|
+
citation: "bg3.mod.active",
|
|
56
|
+
paramsSchema: z.object({ uuid: str() }).strict(),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
kind: "bg3.entity.components",
|
|
60
|
+
game: "baldursgate3",
|
|
61
|
+
context: "server",
|
|
62
|
+
effect: "read",
|
|
63
|
+
status: "attested",
|
|
64
|
+
summary: "Which components an entity carries.",
|
|
65
|
+
citation: "bg3.entity.components",
|
|
66
|
+
paramsSchema: z.object({ guid: str() }).strict(),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
kind: "bg3.entity.status",
|
|
70
|
+
game: "baldursgate3",
|
|
71
|
+
context: "server",
|
|
72
|
+
effect: "read",
|
|
73
|
+
status: "attested",
|
|
74
|
+
summary: "Entity has an active status.",
|
|
75
|
+
citation: "bg3.entity.status",
|
|
76
|
+
paramsSchema: z.object({ guid: str(), status: str() }).strict(),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
kind: "bg3.entity.passive",
|
|
80
|
+
game: "baldursgate3",
|
|
81
|
+
context: "server",
|
|
82
|
+
effect: "read",
|
|
83
|
+
status: "attested",
|
|
84
|
+
summary: "Passive present in the container.",
|
|
85
|
+
citation: "bg3.entity.passive",
|
|
86
|
+
paramsSchema: z.object({ guid: str(), passive: str() }).strict(),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
kind: "bg3.entity.class",
|
|
90
|
+
game: "baldursgate3",
|
|
91
|
+
context: "server",
|
|
92
|
+
effect: "read",
|
|
93
|
+
status: "attested",
|
|
94
|
+
summary: "Class / subclass / level.",
|
|
95
|
+
citation: "bg3.entity.class",
|
|
96
|
+
paramsSchema: z.object({ guid: str() }).strict(),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
kind: "bg3.entity.resource",
|
|
100
|
+
game: "baldursgate3",
|
|
101
|
+
context: "server",
|
|
102
|
+
effect: "read",
|
|
103
|
+
status: "attested",
|
|
104
|
+
summary: "Action-resource amounts (spell slots). Shape observed in game: { Resources: { <ResourceUUID>: [{ Amount, Level, MaxAmount, ReplenishType, ResourceUUID }] } }.",
|
|
105
|
+
citation: "bg3.entity.resource",
|
|
106
|
+
paramsSchema: z.object({ guid: str(), resource: str() }).strict(),
|
|
107
|
+
evidence: "attested(engine) in game (2026-09-08) for the component shape above, captured whole via bridge arm/poll on $host. The SpellSlot ResourceUUID (d136c5d9-0ff0-43da-acce-a74a07f8d6bf) was identified only by corroboration (a Bard L1 host's single entry, a custom-class L5 host's three-entry table matching a caster's real slot table) — that UUID itself stays inferred, not attested, since ActionResourceDefinitions.lsx was not yet in the index at the time.",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
kind: "bg3.boost.freecast",
|
|
111
|
+
game: "baldursgate3",
|
|
112
|
+
context: "server",
|
|
113
|
+
effect: "observe",
|
|
114
|
+
status: "unverified",
|
|
115
|
+
summary: "Acceptance regression: is the Overclock slot-replacement boost live? Depends on bg3.entity.resource.",
|
|
116
|
+
citation: "bg3.boost.freecast",
|
|
117
|
+
paramsSchema: z.object({ guid: str(), resource: str(), status: str() }).strict(),
|
|
118
|
+
evidence: "Exercised in game (2026-09-08) with a free-cast status toggled on and one leveled cast — behaved correctly, no slot consumed, even under a deliberately broken BoostContext. Stays unverified rather than promoted: it still depends on the SpellSlot ResourceUUID under bg3.entity.resource, which is inferred by corroboration, not attested.",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
kind: "bg3.event.watch",
|
|
122
|
+
game: "baldursgate3",
|
|
123
|
+
context: "server",
|
|
124
|
+
effect: "observe",
|
|
125
|
+
status: "attested",
|
|
126
|
+
summary: "An Osiris event fired in the window.",
|
|
127
|
+
citation: "bg3.event.watch",
|
|
128
|
+
paramsSchema: z.object({ name: str(), arity: z.number().int().nonnegative(), match: record().default({}) }).strict(),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
kind: "bg3.event.damage",
|
|
132
|
+
game: "baldursgate3",
|
|
133
|
+
context: "server",
|
|
134
|
+
effect: "observe",
|
|
135
|
+
status: "unverified",
|
|
136
|
+
summary: "Damage amounts and types from an engine event. Field paths inside the event are not pinned.",
|
|
137
|
+
citation: "bg3.event.damage",
|
|
138
|
+
paramsSchema: z.object({ window: z.number().nonnegative().optional(), filter: record().default({}) }).strict(),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
kind: "bg3.osiris.health",
|
|
142
|
+
game: "baldursgate3",
|
|
143
|
+
context: "server",
|
|
144
|
+
effect: "read",
|
|
145
|
+
status: "attested",
|
|
146
|
+
summary: "Is Osiris degraded after a console reset?",
|
|
147
|
+
citation: "bg3.osiris.health",
|
|
148
|
+
paramsSchema: z.object({}).strict(),
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
kind: "bg3.precondition.report",
|
|
152
|
+
game: "baldursgate3",
|
|
153
|
+
context: "server",
|
|
154
|
+
effect: "read",
|
|
155
|
+
status: "attested",
|
|
156
|
+
summary: "Report, do not assume, the fixture state.",
|
|
157
|
+
citation: "bg3.precondition.report",
|
|
158
|
+
paramsSchema: z.object({}).strict(),
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
kind: "bg3.client.loaded",
|
|
162
|
+
game: "baldursgate3",
|
|
163
|
+
context: "client",
|
|
164
|
+
effect: "read",
|
|
165
|
+
status: "attested",
|
|
166
|
+
summary: "The mod enumerated at the main menu.",
|
|
167
|
+
citation: "bg3.client.loaded",
|
|
168
|
+
paramsSchema: z.object({}).strict(),
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
const CP2077_PROBES = [
|
|
172
|
+
{
|
|
173
|
+
kind: "cp2077.bridge.io",
|
|
174
|
+
game: "cyberpunk2077",
|
|
175
|
+
context: "cet",
|
|
176
|
+
effect: "read",
|
|
177
|
+
status: "attested",
|
|
178
|
+
summary: "Where can the bridge write? The mod sandbox provides io relative to the mod folder; the first candidate (the mod folder itself) is the write root.",
|
|
179
|
+
citation: "cp2077.bridge.io",
|
|
180
|
+
paramsSchema: z.object({}).strict(),
|
|
181
|
+
evidence: "Observed in game (2026-09-08, CET v1.37.1): the deployed bridge's heartbeat reported capabilities io:true, json:true, os:true, transport file, writeRoot modwright_hb.json, and a cp2077.mod.loaded arm/poll round trip completed in 11 ms through the file transport. " +
|
|
182
|
+
"An earlier console io.open failure was the console sandbox by design (CET changelog 1.10), and the bridge's earlier log-only fallback was its own _G-based probe (CET source: no _G in a mod environment), fixed in 921da79.",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
kind: "cp2077.mod.loaded",
|
|
186
|
+
game: "cyberpunk2077",
|
|
187
|
+
context: "cet",
|
|
188
|
+
effect: "read",
|
|
189
|
+
status: "attested",
|
|
190
|
+
summary: "The bridge mod is loaded and console-reachable.",
|
|
191
|
+
citation: "cp2077.mod.loaded",
|
|
192
|
+
paramsSchema: z.object({}).strict(),
|
|
193
|
+
evidence: "Reconfirmed at the CET console: `print(tostring(GetMod(\"ModWrightBridge\")))` returned `table: 0x01e978f3edf8`.",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
kind: "cp2077.player.present",
|
|
197
|
+
game: "cyberpunk2077",
|
|
198
|
+
context: "cet",
|
|
199
|
+
effect: "read",
|
|
200
|
+
status: "attested",
|
|
201
|
+
summary: "A player exists (i.e. in a save, not the menu).",
|
|
202
|
+
citation: "cp2077.player.present",
|
|
203
|
+
paramsSchema: z.object({}).strict(),
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
kind: "cp2077.item.equipped",
|
|
207
|
+
game: "cyberpunk2077",
|
|
208
|
+
context: "cet",
|
|
209
|
+
effect: "read",
|
|
210
|
+
status: "attested",
|
|
211
|
+
summary: "Which TweakDB item is in a slot.",
|
|
212
|
+
citation: "cp2077.item.equipped",
|
|
213
|
+
paramsSchema: z.object({ slot: str() }).strict(),
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
kind: "cp2077.inventory.add",
|
|
217
|
+
game: "cyberpunk2077",
|
|
218
|
+
context: "cet",
|
|
219
|
+
effect: "mutate",
|
|
220
|
+
status: "attested",
|
|
221
|
+
summary: "Give an item. Return value undocumented; success is never read from it.",
|
|
222
|
+
citation: "cp2077.inventory.add",
|
|
223
|
+
paramsSchema: z.object({ item: str(), qty: z.union([z.string(), z.number()]) }).strict(),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
kind: "cp2077.lua.eval",
|
|
227
|
+
game: "cyberpunk2077",
|
|
228
|
+
context: "cet",
|
|
229
|
+
effect: "mutate",
|
|
230
|
+
status: "attested",
|
|
231
|
+
summary: "Evaluate one Lua expression or chunk in the CET mod's Lua state and return its value, the way a pasted console line would (Game.AddToInventory(...), TweakDB:GetRecord(...)). Always effect mutate, gated by allowMutate like cp2077.inventory.add.",
|
|
232
|
+
citation: "cp2077.lua.eval; added 2026-09-11",
|
|
233
|
+
paramsSchema: z.object({ code: str(), maxBytes: z.number().int().positive().max(65536).optional() }).strict(),
|
|
234
|
+
evidence: "Verified live (Cyberpunk, 2026-09-11, CET file transport): request req-mtx7lhpv-b35c7106 passed `1 + 1` → 2, `TweakXL.Version()` → \"1.11.4\", a `TweakDB:GetFlat` LocKey read, a two-value chunk → [1, \"two\"], `error(\"deliberate\")` → status error, and `Game.GetPlayer():GetWorldPosition()` with maxBytes 16 → a 16-byte prefix with truncated: true; request req-mtx7m7q0-d4c566c0 ran `Game.AddToInventory(\"<a scratch test item>\", 1)` (returnCount 0) and the added items were counted in the inventory by hand. CET's loader accepted the code-only call at once. A `GetItemQuantity`/`HasItem` read by bare TweakDB id returned 0/false for the spawned weapon (weapons carry a per-instance seed in their ItemID), so that read is not a count."
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
kind: "cp2077.entity.tagged",
|
|
238
|
+
game: "cyberpunk2077",
|
|
239
|
+
context: "cet",
|
|
240
|
+
effect: "read",
|
|
241
|
+
status: "attested",
|
|
242
|
+
summary: "Find dynamic entities by tag.",
|
|
243
|
+
citation: "cp2077.entity.tagged",
|
|
244
|
+
paramsSchema: z.object({ tag: str() }).strict(),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
kind: "cp2077.entity.components",
|
|
248
|
+
game: "cyberpunk2077",
|
|
249
|
+
context: "cet",
|
|
250
|
+
effect: "read",
|
|
251
|
+
status: "attested",
|
|
252
|
+
summary: "Which named component an entity has. The call is attested; the names to ask for are per-case and unverified.",
|
|
253
|
+
citation: "cp2077.entity.components",
|
|
254
|
+
paramsSchema: z.object({ entityRef: str(), names: z.array(str()).min(1) }).strict(),
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
kind: "cp2077.entity.transform",
|
|
258
|
+
game: "cyberpunk2077",
|
|
259
|
+
context: "cet",
|
|
260
|
+
effect: "read",
|
|
261
|
+
status: "unverified",
|
|
262
|
+
summary: "Where an entity is. Attested for item objects; unverified for a sector-placed prop.",
|
|
263
|
+
citation: "cp2077.entity.transform",
|
|
264
|
+
paramsSchema: z.object({ entityRef: str() }).strict(),
|
|
265
|
+
evidence: "Observed in game: a test mod's own init.lua comment says its tag \"removes legacy dynamic cases from older experiments\" and the real case is a static sector prop, so GetTaggedIDs on it returned 0 twice at two locations — not a location miss. That tag marks legacy dev spawns only and is the wrong entry point for a sector-placed prop; a sector-placed prop needs a different probe mechanism, not just a re-run of this one. Stays unverified.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
kind: "cp2077.entity.appearance",
|
|
269
|
+
game: "cyberpunk2077",
|
|
270
|
+
context: "cet",
|
|
271
|
+
effect: "read",
|
|
272
|
+
status: "unverified",
|
|
273
|
+
summary: "Acceptance regression, differential: did the sector node's appearanceName resolve?",
|
|
274
|
+
citation: "cp2077.entity.appearance",
|
|
275
|
+
paramsSchema: z.object({ tag: str(), baselineId: str() }).strict(),
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
kind: "cp2077.tweakdb.record",
|
|
279
|
+
game: "cyberpunk2077",
|
|
280
|
+
context: "cet",
|
|
281
|
+
effect: "read",
|
|
282
|
+
status: "attested",
|
|
283
|
+
summary: "A TweakDB record exists.",
|
|
284
|
+
citation: "cp2077.tweakdb.record",
|
|
285
|
+
paramsSchema: z.object({ id: str() }).strict(),
|
|
286
|
+
evidence: 'Verified at the CET console: `print(tostring(TweakDB:GetRecord("Items.<a mod\'s weapon preset>")))` returned `userdata: 0x01e8a556bf60`.',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
kind: "cp2077.tweakdb.flat",
|
|
290
|
+
game: "cyberpunk2077",
|
|
291
|
+
context: "cet",
|
|
292
|
+
effect: "read",
|
|
293
|
+
status: "attested",
|
|
294
|
+
summary: "A flat's value.",
|
|
295
|
+
citation: "cp2077.tweakdb.flat",
|
|
296
|
+
paramsSchema: z.object({ id: str() }).strict(),
|
|
297
|
+
evidence: 'Verified at the CET console: `print(tostring(TweakDB:GetFlat("Items.<a mod\'s weapon preset>.displayName")))` returned `LocKey(15107614351950282319ull)`.',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
kind: "cp2077.interaction.present",
|
|
301
|
+
game: "cyberpunk2077",
|
|
302
|
+
context: "cet",
|
|
303
|
+
effect: "read",
|
|
304
|
+
status: "unverified",
|
|
305
|
+
summary: "An interaction component is on the entity. The component name is not attested anywhere on disk.",
|
|
306
|
+
citation: "cp2077.interaction.present",
|
|
307
|
+
paramsSchema: z.object({ entityRef: str() }).strict(),
|
|
308
|
+
},
|
|
309
|
+
];
|
|
310
|
+
export const PROBE_CATALOG = [...BG3_PROBES, ...CP2077_PROBES];
|
|
311
|
+
const BY_KIND = new Map(PROBE_CATALOG.map((entry) => [entry.kind, entry]));
|
|
312
|
+
export function lookupProbe(kind) {
|
|
313
|
+
return BY_KIND.get(kind);
|
|
314
|
+
}
|
|
315
|
+
export function probesForGame(game) {
|
|
316
|
+
return PROBE_CATALOG.filter((entry) => entry.game === game);
|
|
317
|
+
}
|