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.
Files changed (455) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +212 -0
  3. package/assets/claude/agents/mw-explore.md +46 -0
  4. package/assets/claude/agents/mw-fixture.md +33 -0
  5. package/assets/claude/agents/mw-rule-author.md +51 -0
  6. package/assets/claude/agents/mw-scaffold-author.md +48 -0
  7. package/assets/claude/skills/mw-author.md +59 -0
  8. package/assets/claude/skills/mw-new-item.md +51 -0
  9. package/assets/claude/skills/mw-new-spell.md +50 -0
  10. package/assets/claude/skills/mw-scaffold.md +59 -0
  11. package/assets/claude/skills/mw-ship.md +57 -0
  12. package/assets/claude/skills/mw-validate.md +51 -0
  13. package/assets/claude/skills/mw-verify.md +59 -0
  14. package/assets/nivalisnights/extract_resources.py +177 -0
  15. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Config.json +5 -0
  16. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapClient.lua +55 -0
  17. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapServer.lua +81 -0
  18. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Log.lua +96 -0
  19. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Probes.lua +1038 -0
  20. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/ProbesClient.lua +74 -0
  21. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Protocol.lua +985 -0
  22. package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Watch.lua +175 -0
  23. package/bridges/bg3-se/Mods/ModWrightBridge/meta.lsx +45 -0
  24. package/bridges/bg3-se/README.md +99 -0
  25. package/bridges/bg3-se/bridge.json +68 -0
  26. package/bridges/cp2077-cet/ModWrightBridge/init.lua +375 -0
  27. package/bridges/cp2077-cet/ModWrightBridge/probes.lua +1104 -0
  28. package/bridges/cp2077-cet/ModWrightBridge/protocol.lua +1306 -0
  29. package/bridges/cp2077-cet/README.md +165 -0
  30. package/bridges/cp2077-cet/bridge.json +53 -0
  31. package/dist/core/bridge/arm.js +175 -0
  32. package/dist/core/bridge/cet-console.js +26 -0
  33. package/dist/core/bridge/cet-log.js +362 -0
  34. package/dist/core/bridge/index.js +11 -0
  35. package/dist/core/bridge/locate.js +35 -0
  36. package/dist/core/bridge/paths.js +34 -0
  37. package/dist/core/bridge/pending.js +89 -0
  38. package/dist/core/bridge/poll.js +155 -0
  39. package/dist/core/bridge/stage.js +100 -0
  40. package/dist/core/bridge/status.js +97 -0
  41. package/dist/core/bridge/types.js +145 -0
  42. package/dist/core/bridge/validate.js +107 -0
  43. package/dist/core/build/index.js +6 -0
  44. package/dist/core/build/manifest.js +47 -0
  45. package/dist/core/build/paths.js +32 -0
  46. package/dist/core/build/run.js +342 -0
  47. package/dist/core/build/stage.js +265 -0
  48. package/dist/core/build/steps/bg3.js +129 -0
  49. package/dist/core/build/steps/core.js +195 -0
  50. package/dist/core/build/steps/cyberpunk.js +95 -0
  51. package/dist/core/build/steps/listing.js +35 -0
  52. package/dist/core/build/types.js +13 -0
  53. package/dist/core/build/zip.js +148 -0
  54. package/dist/core/compat/check.js +113 -0
  55. package/dist/core/compat/pe.js +220 -0
  56. package/dist/core/compat/types.js +1 -0
  57. package/dist/core/compat/version.js +70 -0
  58. package/dist/core/deploy/guards.js +292 -0
  59. package/dist/core/deploy/index.js +5 -0
  60. package/dist/core/deploy/plan.js +696 -0
  61. package/dist/core/deploy/reload-apply.js +361 -0
  62. package/dist/core/deploy/reload.js +358 -0
  63. package/dist/core/deploy/sources.js +169 -0
  64. package/dist/core/deploy/types.js +1 -0
  65. package/dist/core/epic.js +54 -0
  66. package/dist/core/fsutil.js +191 -0
  67. package/dist/core/home.js +70 -0
  68. package/dist/core/knowledge/facts.js +229 -0
  69. package/dist/core/knowledge/index.js +4 -0
  70. package/dist/core/knowledge/search.js +112 -0
  71. package/dist/core/knowledge/store.js +123 -0
  72. package/dist/core/knowledge/types.js +1 -0
  73. package/dist/core/ledger/check.js +42 -0
  74. package/dist/core/ledger/hooks.js +80 -0
  75. package/dist/core/ledger/index.js +7 -0
  76. package/dist/core/ledger/query.js +17 -0
  77. package/dist/core/ledger/record.js +84 -0
  78. package/dist/core/ledger/status.js +53 -0
  79. package/dist/core/ledger/store.js +98 -0
  80. package/dist/core/ledger/types.js +108 -0
  81. package/dist/core/logs/index.js +4 -0
  82. package/dist/core/logs/registry.js +50 -0
  83. package/dist/core/logs/scan.js +195 -0
  84. package/dist/core/logs/triage.js +160 -0
  85. package/dist/core/logs/types.js +1 -0
  86. package/dist/core/lsx.js +10 -0
  87. package/dist/core/lz4.js +66 -0
  88. package/dist/core/mods.js +229 -0
  89. package/dist/core/process.js +49 -0
  90. package/dist/core/project/build-steps.js +83 -0
  91. package/dist/core/project/describe.js +74 -0
  92. package/dist/core/project/index.js +5 -0
  93. package/dist/core/project/load.js +146 -0
  94. package/dist/core/project/schema.js +264 -0
  95. package/dist/core/project/types.js +3 -0
  96. package/dist/core/prompts.js +72 -0
  97. package/dist/core/registry.js +35 -0
  98. package/dist/core/safety/backup.js +206 -0
  99. package/dist/core/safety/execute.js +188 -0
  100. package/dist/core/safety/index.js +5 -0
  101. package/dist/core/safety/plan.js +122 -0
  102. package/dist/core/safety/restore.js +172 -0
  103. package/dist/core/safety/types.js +1 -0
  104. package/dist/core/scaffold/bootstrap.js +7 -0
  105. package/dist/core/scaffold/catalog.js +28 -0
  106. package/dist/core/scaffold/claude-assets.js +271 -0
  107. package/dist/core/scaffold/collisions.js +64 -0
  108. package/dist/core/scaffold/emit.js +356 -0
  109. package/dist/core/scaffold/engine.js +680 -0
  110. package/dist/core/scaffold/ids.js +78 -0
  111. package/dist/core/scaffold/index.js +10 -0
  112. package/dist/core/scaffold/jsonpath.js +121 -0
  113. package/dist/core/scaffold/load.js +81 -0
  114. package/dist/core/scaffold/render.js +85 -0
  115. package/dist/core/scaffold/sequence.js +396 -0
  116. package/dist/core/scaffold/types.js +309 -0
  117. package/dist/core/steam.js +63 -0
  118. package/dist/core/surfaceutil.js +36 -0
  119. package/dist/core/testplan/check.js +67 -0
  120. package/dist/core/testplan/index.js +5 -0
  121. package/dist/core/testplan/load.js +100 -0
  122. package/dist/core/testplan/registry.js +317 -0
  123. package/dist/core/testplan/run.js +389 -0
  124. package/dist/core/testplan/types.js +82 -0
  125. package/dist/core/text/converters/bg3.js +50 -0
  126. package/dist/core/text/converters/cyberpunk.js +36 -0
  127. package/dist/core/text/converters/index.js +261 -0
  128. package/dist/core/text/diff/cr2w.js +180 -0
  129. package/dist/core/text/diff/index.js +16 -0
  130. package/dist/core/text/diff/loca.js +43 -0
  131. package/dist/core/text/diff/lsx.js +126 -0
  132. package/dist/core/text/handles.js +86 -0
  133. package/dist/core/text/index.js +6 -0
  134. package/dist/core/text/json.js +46 -0
  135. package/dist/core/text/templates/cache.js +71 -0
  136. package/dist/core/text/templates/index.js +3 -0
  137. package/dist/core/text/templates/library.js +245 -0
  138. package/dist/core/text/templates/types.js +51 -0
  139. package/dist/core/text/toolchain.js +16 -0
  140. package/dist/core/text/types.js +1 -0
  141. package/dist/core/text/validate.js +149 -0
  142. package/dist/core/text/xml.js +58 -0
  143. package/dist/core/toolchain/datafetch.js +151 -0
  144. package/dist/core/toolchain/env.js +63 -0
  145. package/dist/core/toolchain/features.js +170 -0
  146. package/dist/core/toolchain/index.js +8 -0
  147. package/dist/core/toolchain/install.js +191 -0
  148. package/dist/core/toolchain/locate.js +275 -0
  149. package/dist/core/toolchain/remember.js +84 -0
  150. package/dist/core/toolchain/run.js +318 -0
  151. package/dist/core/toolchain/specs.js +351 -0
  152. package/dist/core/toolchain/types.js +1 -0
  153. package/dist/core/types.js +1 -0
  154. package/dist/core/userconfig.js +66 -0
  155. package/dist/core/validate/context.js +139 -0
  156. package/dist/core/validate/index.js +5 -0
  157. package/dist/core/validate/registry.js +58 -0
  158. package/dist/core/validate/run.js +131 -0
  159. package/dist/core/validate/suppressions.js +91 -0
  160. package/dist/core/validate/types.js +2 -0
  161. package/dist/core/yamledit.js +51 -0
  162. package/dist/index.js +389 -0
  163. package/dist/server.js +1914 -0
  164. package/dist/surfaces/baldursgate3/compat.js +164 -0
  165. package/dist/surfaces/baldursgate3/identity.js +110 -0
  166. package/dist/surfaces/baldursgate3/index/build.js +331 -0
  167. package/dist/surfaces/baldursgate3/index/index.js +17 -0
  168. package/dist/surfaces/baldursgate3/index/info.js +40 -0
  169. package/dist/surfaces/baldursgate3/index/parsers/loca.js +41 -0
  170. package/dist/surfaces/baldursgate3/index/parsers/lsx.js +137 -0
  171. package/dist/surfaces/baldursgate3/index/parsers/stats.js +45 -0
  172. package/dist/surfaces/baldursgate3/index/parsers/treasuretable.js +57 -0
  173. package/dist/surfaces/baldursgate3/index/parsers/xmlutil.js +142 -0
  174. package/dist/surfaces/baldursgate3/index/queries.js +327 -0
  175. package/dist/surfaces/baldursgate3/index/schema.js +62 -0
  176. package/dist/surfaces/baldursgate3/index.js +3 -0
  177. package/dist/surfaces/baldursgate3/logs.js +278 -0
  178. package/dist/surfaces/baldursgate3/modsettings.js +115 -0
  179. package/dist/surfaces/baldursgate3/pak.js +153 -0
  180. package/dist/surfaces/baldursgate3/settings.js +49 -0
  181. package/dist/surfaces/baldursgate3/surface.js +232 -0
  182. package/dist/surfaces/baldursgate3/validators/bridge.js +7 -0
  183. package/dist/surfaces/baldursgate3/validators/files.js +476 -0
  184. package/dist/surfaces/baldursgate3/validators/items.js +1001 -0
  185. package/dist/surfaces/baldursgate3/validators/progression.js +734 -0
  186. package/dist/surfaces/baldursgate3/validators/stats.js +1076 -0
  187. package/dist/surfaces/baldursgate3/validators/story.js +790 -0
  188. package/dist/surfaces/baldursgate3/validators/templates.js +7 -0
  189. package/dist/surfaces/cyberpunk2077/compat.js +223 -0
  190. package/dist/surfaces/cyberpunk2077/index/build.js +231 -0
  191. package/dist/surfaces/cyberpunk2077/index/index.js +17 -0
  192. package/dist/surfaces/cyberpunk2077/index/info.js +51 -0
  193. package/dist/surfaces/cyberpunk2077/index/model.js +52 -0
  194. package/dist/surfaces/cyberpunk2077/index/parsers/tweak.js +483 -0
  195. package/dist/surfaces/cyberpunk2077/index/parsers/tweakxl-yaml.js +183 -0
  196. package/dist/surfaces/cyberpunk2077/index/queries.js +504 -0
  197. package/dist/surfaces/cyberpunk2077/index/resource.js +135 -0
  198. package/dist/surfaces/cyberpunk2077/index/schema.js +90 -0
  199. package/dist/surfaces/cyberpunk2077/index.js +3 -0
  200. package/dist/surfaces/cyberpunk2077/logs.js +742 -0
  201. package/dist/surfaces/cyberpunk2077/paths.js +12 -0
  202. package/dist/surfaces/cyberpunk2077/surface.js +218 -0
  203. package/dist/surfaces/cyberpunk2077/validators/bridge.js +7 -0
  204. package/dist/surfaces/cyberpunk2077/validators/cr2w.js +349 -0
  205. package/dist/surfaces/cyberpunk2077/validators/garments.js +1181 -0
  206. package/dist/surfaces/cyberpunk2077/validators/mesh.js +338 -0
  207. package/dist/surfaces/cyberpunk2077/validators/modsettings.js +120 -0
  208. package/dist/surfaces/cyberpunk2077/validators/packaging.js +463 -0
  209. package/dist/surfaces/cyberpunk2077/validators/red4ext.js +318 -0
  210. package/dist/surfaces/cyberpunk2077/validators/redscript.js +193 -0
  211. package/dist/surfaces/cyberpunk2077/validators/templates.js +7 -0
  212. package/dist/surfaces/cyberpunk2077/validators/tweaks.js +768 -0
  213. package/dist/surfaces/cyberpunk2077/validators/vehicles.js +531 -0
  214. package/dist/surfaces/cyberpunk2077/validators/xl.js +308 -0
  215. package/dist/surfaces/eldenring/binders.js +111 -0
  216. package/dist/surfaces/eldenring/compat.js +166 -0
  217. package/dist/surfaces/eldenring/dcx.js +49 -0
  218. package/dist/surfaces/eldenring/index.js +13 -0
  219. package/dist/surfaces/eldenring/loaders.js +132 -0
  220. package/dist/surfaces/eldenring/logs.js +67 -0
  221. package/dist/surfaces/eldenring/paramdef.js +256 -0
  222. package/dist/surfaces/eldenring/params.js +129 -0
  223. package/dist/surfaces/eldenring/surface.js +121 -0
  224. package/dist/surfaces/eldenring/tools.js +52 -0
  225. package/dist/surfaces/eldenring/validators/binders.js +193 -0
  226. package/dist/surfaces/eldenring/validators/packages.js +159 -0
  227. package/dist/surfaces/eldenring/validators/params.js +334 -0
  228. package/dist/surfaces/eldenring/validators/profiles.js +287 -0
  229. package/dist/surfaces/index.js +34 -0
  230. package/dist/surfaces/nivalisnights/compat.js +229 -0
  231. package/dist/surfaces/nivalisnights/index/build.js +310 -0
  232. package/dist/surfaces/nivalisnights/index/index.js +5 -0
  233. package/dist/surfaces/nivalisnights/index/queries.js +231 -0
  234. package/dist/surfaces/nivalisnights/index.js +4 -0
  235. package/dist/surfaces/nivalisnights/logs.js +473 -0
  236. package/dist/surfaces/nivalisnights/surface.js +195 -0
  237. package/dist/surfaces/skyrimse/archives.js +229 -0
  238. package/dist/surfaces/skyrimse/compat.js +163 -0
  239. package/dist/surfaces/skyrimse/index.js +12 -0
  240. package/dist/surfaces/skyrimse/loadorder.js +174 -0
  241. package/dist/surfaces/skyrimse/logs.js +241 -0
  242. package/dist/surfaces/skyrimse/plugins.js +149 -0
  243. package/dist/surfaces/skyrimse/records.js +94 -0
  244. package/dist/surfaces/skyrimse/surface.js +240 -0
  245. package/dist/surfaces/skyrimse/validators/archive-records.js +165 -0
  246. package/dist/surfaces/skyrimse/validators/archives.js +110 -0
  247. package/dist/surfaces/skyrimse/validators/plugins.js +374 -0
  248. package/dist/surfaces/skyrimse/validators/records.js +117 -0
  249. package/dist/surfaces/stardewvalley/compat.js +190 -0
  250. package/dist/surfaces/stardewvalley/config.js +42 -0
  251. package/dist/surfaces/stardewvalley/contentpatcher.js +203 -0
  252. package/dist/surfaces/stardewvalley/index/index.js +229 -0
  253. package/dist/surfaces/stardewvalley/index/wiki.js +190 -0
  254. package/dist/surfaces/stardewvalley/index.js +12 -0
  255. package/dist/surfaces/stardewvalley/logs.js +307 -0
  256. package/dist/surfaces/stardewvalley/mods.js +182 -0
  257. package/dist/surfaces/stardewvalley/surface.js +244 -0
  258. package/dist/surfaces/stardewvalley/validators/content.js +321 -0
  259. package/dist/surfaces/stardewvalley/validators/fields.js +147 -0
  260. package/dist/surfaces/stardewvalley/validators/loadorder.js +69 -0
  261. package/dist/surfaces/stardewvalley/validators/manifest.js +221 -0
  262. package/dist/surfaces/subnautica2/compat.js +138 -0
  263. package/dist/surfaces/subnautica2/index.js +5 -0
  264. package/dist/surfaces/subnautica2/logs.js +129 -0
  265. package/dist/surfaces/subnautica2/paks.js +173 -0
  266. package/dist/surfaces/subnautica2/surface.js +151 -0
  267. package/dist/surfaces/subnautica2/validators/packaging.js +298 -0
  268. package/dist/surfaces/valheim/compat.js +171 -0
  269. package/dist/surfaces/valheim/index/index.js +301 -0
  270. package/dist/surfaces/valheim/index/tables.js +103 -0
  271. package/dist/surfaces/valheim/index.js +11 -0
  272. package/dist/surfaces/valheim/logs.js +197 -0
  273. package/dist/surfaces/valheim/packaging.js +131 -0
  274. package/dist/surfaces/valheim/plugins.js +270 -0
  275. package/dist/surfaces/valheim/references.js +121 -0
  276. package/dist/surfaces/valheim/surface.js +223 -0
  277. package/dist/surfaces/valheim/validators/packaging.js +218 -0
  278. package/dist/surfaces/valheim/validators/plugins.js +287 -0
  279. package/dist/surfaces/valheim/validators/references.js +169 -0
  280. package/knowledge/baldursgate3/animations.gr2.yaml +166 -0
  281. package/knowledge/baldursgate3/audio.voice.yaml +182 -0
  282. package/knowledge/baldursgate3/audio.wwise.yaml +104 -0
  283. package/knowledge/baldursgate3/charactercreation.heads-hair.yaml +193 -0
  284. package/knowledge/baldursgate3/communitylibrary.shared-content.yaml +129 -0
  285. package/knowledge/baldursgate3/compatibilityframework.api.yaml +176 -0
  286. package/knowledge/baldursgate3/crash.causes.yaml +90 -0
  287. package/knowledge/baldursgate3/dialogue.dialogbank.yaml +120 -0
  288. package/knowledge/baldursgate3/dialogue.files.yaml +236 -0
  289. package/knowledge/baldursgate3/dialogue.osiris-goals.yaml +386 -0
  290. package/knowledge/baldursgate3/dialogue.yaml +122 -0
  291. package/knowledge/baldursgate3/feats.classes.yaml +164 -0
  292. package/knowledge/baldursgate3/items.cloth-physics.yaml +167 -0
  293. package/knowledge/baldursgate3/items.roottemplates.yaml +320 -0
  294. package/knowledge/baldursgate3/items.visualbank.yaml +628 -0
  295. package/knowledge/baldursgate3/journal.quests.yaml +214 -0
  296. package/knowledge/baldursgate3/loadorder.modsettings.yaml +128 -0
  297. package/knowledge/baldursgate3/localization.handles.yaml +92 -0
  298. package/knowledge/baldursgate3/misc.encoding-and-practice.yaml +111 -0
  299. package/knowledge/baldursgate3/origins.companions.yaml +144 -0
  300. package/knowledge/baldursgate3/osiris.signatures.yaml +211 -0
  301. package/knowledge/baldursgate3/packaging.meta.yaml +155 -0
  302. package/knowledge/baldursgate3/packaging.paks.yaml +79 -0
  303. package/knowledge/baldursgate3/process.launch.yaml +34 -0
  304. package/knowledge/baldursgate3/progressions.yaml +92 -0
  305. package/knowledge/baldursgate3/publishing.modio-nexus.yaml +170 -0
  306. package/knowledge/baldursgate3/publishing.toolkit-modio.yaml +135 -0
  307. package/knowledge/baldursgate3/races.progression.yaml +228 -0
  308. package/knowledge/baldursgate3/scriptextender.console.yaml +257 -0
  309. package/knowledge/baldursgate3/scriptextender.yaml +252 -0
  310. package/knowledge/baldursgate3/stats.boosts.yaml +293 -0
  311. package/knowledge/baldursgate3/stats.equipment-kits.yaml +69 -0
  312. package/knowledge/baldursgate3/stats.interrupts.yaml +45 -0
  313. package/knowledge/baldursgate3/stats.itemcombos.yaml +65 -0
  314. package/knowledge/baldursgate3/stats.spells.yaml +293 -0
  315. package/knowledge/baldursgate3/stats.weapons.yaml +210 -0
  316. package/knowledge/baldursgate3/toolchain.divine.yaml +179 -0
  317. package/knowledge/baldursgate3/toolchain.lslib-formats.yaml +242 -0
  318. package/knowledge/baldursgate3/toolkit.levels.yaml +177 -0
  319. package/knowledge/baldursgate3/ui.icons-atlas.yaml +78 -0
  320. package/knowledge/baldursgate3/ui.icons.yaml +412 -0
  321. package/knowledge/baldursgate3/ui.mcm.yaml +161 -0
  322. package/knowledge/baldursgate3/vfx.materials.yaml +174 -0
  323. package/knowledge/baldursgate3/visuals.characterfix.yaml +73 -0
  324. package/knowledge/cyberpunk2077/animations.anims.yaml +118 -0
  325. package/knowledge/cyberpunk2077/anims.archivexl.yaml +163 -0
  326. package/knowledge/cyberpunk2077/anims.authoring.yaml +579 -0
  327. package/knowledge/cyberpunk2077/anims.encoder.yaml +156 -0
  328. package/knowledge/cyberpunk2077/anims.graph.yaml +629 -0
  329. package/knowledge/cyberpunk2077/anims.method.yaml +360 -0
  330. package/knowledge/cyberpunk2077/anims.pipeline.yaml +352 -0
  331. package/knowledge/cyberpunk2077/anims.roundtrip.yaml +222 -0
  332. package/knowledge/cyberpunk2077/anims.sets.yaml +176 -0
  333. package/knowledge/cyberpunk2077/archivexl.bodytypes.yaml +149 -0
  334. package/knowledge/cyberpunk2077/archivexl.dynamic-appearances.yaml +218 -0
  335. package/knowledge/cyberpunk2077/archivexl.manifest.yaml +214 -0
  336. package/knowledge/cyberpunk2077/audio.events.yaml +168 -0
  337. package/knowledge/cyberpunk2077/audio.sounds.yaml +164 -0
  338. package/knowledge/cyberpunk2077/audio.voicesets.yaml +146 -0
  339. package/knowledge/cyberpunk2077/cet.sandbox.yaml +219 -0
  340. package/knowledge/cyberpunk2077/clothing.garments.yaml +283 -0
  341. package/knowledge/cyberpunk2077/clothing.refits.yaml +215 -0
  342. package/knowledge/cyberpunk2077/codeware.overview.yaml +111 -0
  343. package/knowledge/cyberpunk2077/codeware.systems.yaml +192 -0
  344. package/knowledge/cyberpunk2077/drones.combat.yaml +416 -0
  345. package/knowledge/cyberpunk2077/drones.control.yaml +552 -0
  346. package/knowledge/cyberpunk2077/entities.appearance.yaml +166 -0
  347. package/knowledge/cyberpunk2077/equipmentex.slots.yaml +151 -0
  348. package/knowledge/cyberpunk2077/fx.effects.yaml +246 -0
  349. package/knowledge/cyberpunk2077/fx.lights.yaml +171 -0
  350. package/knowledge/cyberpunk2077/hair.modding.yaml +145 -0
  351. package/knowledge/cyberpunk2077/items.wiring.yaml +246 -0
  352. package/knowledge/cyberpunk2077/loadorder.archives.yaml +71 -0
  353. package/knowledge/cyberpunk2077/mesh.blender.yaml +209 -0
  354. package/knowledge/cyberpunk2077/nativedb.dump.yaml +98 -0
  355. package/knowledge/cyberpunk2077/npc.appearance.yaml +275 -0
  356. package/knowledge/cyberpunk2077/npc.behaviour.yaml +128 -0
  357. package/knowledge/cyberpunk2077/npc.locomotion.yaml +490 -0
  358. package/knowledge/cyberpunk2077/packaging.deploy.yaml +175 -0
  359. package/knowledge/cyberpunk2077/packaging.variants.yaml +176 -0
  360. package/knowledge/cyberpunk2077/player.control.yaml +918 -0
  361. package/knowledge/cyberpunk2077/process.assetbuild.yaml +154 -0
  362. package/knowledge/cyberpunk2077/process.lessons.yaml +276 -0
  363. package/knowledge/cyberpunk2077/quests.minor.yaml +46 -0
  364. package/knowledge/cyberpunk2077/quests.scenes.yaml +282 -0
  365. package/knowledge/cyberpunk2077/quickhacks.tiers.yaml +41 -0
  366. package/knowledge/cyberpunk2077/red4ext.api.yaml +645 -0
  367. package/knowledge/cyberpunk2077/red4ext.plugins.yaml +124 -0
  368. package/knowledge/cyberpunk2077/redhottools.hotreload.yaml +245 -0
  369. package/knowledge/cyberpunk2077/redscript.compile-log.yaml +76 -0
  370. package/knowledge/cyberpunk2077/redscript.modules.yaml +118 -0
  371. package/knowledge/cyberpunk2077/toolchain.wolvenkit.yaml +163 -0
  372. package/knowledge/cyberpunk2077/tweakdb.loot-recipes.yaml +184 -0
  373. package/knowledge/cyberpunk2077/tweakdb.melee.yaml +352 -0
  374. package/knowledge/cyberpunk2077/tweakdb.weapons.yaml +325 -0
  375. package/knowledge/cyberpunk2077/tweakxl.hotreload.yaml +151 -0
  376. package/knowledge/cyberpunk2077/tweakxl.yaml-syntax.yaml +208 -0
  377. package/knowledge/cyberpunk2077/ui.icons.yaml +259 -0
  378. package/knowledge/cyberpunk2077/ui.inkatlas.yaml +81 -0
  379. package/knowledge/cyberpunk2077/ui.modsettings.yaml +204 -0
  380. package/knowledge/cyberpunk2077/vehicles.control.yaml +475 -0
  381. package/knowledge/cyberpunk2077/vehicles.dashboard-ui.yaml +203 -0
  382. package/knowledge/cyberpunk2077/vehicles.entities.yaml +195 -0
  383. package/knowledge/cyberpunk2077/vehicles.wiring.yaml +137 -0
  384. package/knowledge/cyberpunk2077/wolvenkit.cli.yaml +167 -0
  385. package/knowledge/cyberpunk2077/workspot.system.yaml +55 -0
  386. package/knowledge/cyberpunk2077/world.interactions.yaml +41 -0
  387. package/knowledge/cyberpunk2077/world.loot-container.yaml +167 -0
  388. package/knowledge/cyberpunk2077/world.sectors.yaml +233 -0
  389. package/knowledge/eldenring/formats.binders.yaml +237 -0
  390. package/knowledge/eldenring/launcher.me3.yaml +230 -0
  391. package/knowledge/eldenring/launcher.modengine2.yaml +119 -0
  392. package/knowledge/eldenring/params.paramdef.yaml +149 -0
  393. package/knowledge/eldenring/params.regulation.yaml +142 -0
  394. package/knowledge/eldenring/saves.online.yaml +118 -0
  395. package/knowledge/eldenring/toolchain.cli.yaml +256 -0
  396. package/knowledge/nivalisnights/modding.runtime.yaml +157 -0
  397. package/knowledge/skyrimse/archives.bsa.yaml +212 -0
  398. package/knowledge/skyrimse/install.detection.yaml +41 -0
  399. package/knowledge/skyrimse/loadorder.files.yaml +242 -0
  400. package/knowledge/skyrimse/plugins.esl.yaml +189 -0
  401. package/knowledge/skyrimse/plugins.format.yaml +224 -0
  402. package/knowledge/skyrimse/skse.runtime.yaml +283 -0
  403. package/knowledge/skyrimse/toolchain.xedit-spriggit.yaml +336 -0
  404. package/knowledge/stardewvalley/contentpatcher.format.yaml +338 -0
  405. package/knowledge/stardewvalley/distribution.channels.yaml +236 -0
  406. package/knowledge/stardewvalley/game.versions.yaml +202 -0
  407. package/knowledge/stardewvalley/install.layout.yaml +277 -0
  408. package/knowledge/stardewvalley/smapi.loader.yaml +284 -0
  409. package/knowledge/stardewvalley/smapi.logs.yaml +329 -0
  410. package/knowledge/stardewvalley/smapi.manifest.yaml +293 -0
  411. package/knowledge/stardewvalley/toolchain.build.yaml +261 -0
  412. package/knowledge/stardewvalley/wiki.dataformat.yaml +130 -0
  413. package/knowledge/subnautica2/game.build.yaml +331 -0
  414. package/knowledge/subnautica2/modding.state.yaml +210 -0
  415. package/knowledge/subnautica2/ue5.containers.yaml +72 -0
  416. package/knowledge/subnautica2/ue5.paks.yaml +203 -0
  417. package/knowledge/subnautica2/ue5.tooling.yaml +175 -0
  418. package/knowledge/subnautica2/ue5.ue4ss.yaml +203 -0
  419. package/knowledge/valheim/bepinex.loader.yaml +247 -0
  420. package/knowledge/valheim/bepinex.plugins.yaml +251 -0
  421. package/knowledge/valheim/game.versions.yaml +321 -0
  422. package/knowledge/valheim/install.layout.yaml +216 -0
  423. package/knowledge/valheim/jotunn.library.yaml +334 -0
  424. package/knowledge/valheim/thunderstore.packaging.yaml +217 -0
  425. package/knowledge/valheim/toolchain.build.yaml +111 -0
  426. package/package.json +29 -0
  427. package/scaffolds/baldursgate3/new-class-injection.yaml +148 -0
  428. package/scaffolds/baldursgate3/new-item.yaml +264 -0
  429. package/scaffolds/baldursgate3/new-mod.yaml +174 -0
  430. package/scaffolds/baldursgate3/new-passive.yaml +100 -0
  431. package/scaffolds/baldursgate3/new-project.yaml +118 -0
  432. package/scaffolds/baldursgate3/new-quest-stub.yaml +159 -0
  433. package/scaffolds/baldursgate3/new-spell.yaml +102 -0
  434. package/scaffolds/baldursgate3/new-status.yaml +119 -0
  435. package/scaffolds/baldursgate3/sequences/subclass-kit.yaml +50 -0
  436. package/scaffolds/cyberpunk2077/edit-tweak.yaml +70 -0
  437. package/scaffolds/cyberpunk2077/mesh-export-preset.yaml +170 -0
  438. package/scaffolds/cyberpunk2077/new-entity-patch.yaml +255 -0
  439. package/scaffolds/cyberpunk2077/new-garment-refit.yaml +279 -0
  440. package/scaffolds/cyberpunk2077/new-item-chain.yaml +548 -0
  441. package/scaffolds/cyberpunk2077/new-localization.yaml +130 -0
  442. package/scaffolds/cyberpunk2077/new-mod-settings.yaml +133 -0
  443. package/scaffolds/cyberpunk2077/new-player-replacer.yaml +1003 -0
  444. package/scaffolds/cyberpunk2077/new-project.yaml +157 -0
  445. package/scaffolds/cyberpunk2077/new-sector-node.yaml +156 -0
  446. package/scaffolds/cyberpunk2077/new-tweak.yaml +121 -0
  447. package/scaffolds/cyberpunk2077/new-vehicle-livery.yaml +148 -0
  448. package/scaffolds/cyberpunk2077/new-workspot-entity.yaml +177 -0
  449. package/scaffolds/cyberpunk2077/sequences/iconic-weapon.yaml +72 -0
  450. package/scaffolds/cyberpunk2077/sequences/quickhack-takeover.yaml +213 -0
  451. package/scaffolds/stardewvalley/new-cp-patch.yaml +106 -0
  452. package/scaffolds/stardewvalley/new-csharp-mod.yaml +195 -0
  453. package/scaffolds/stardewvalley/new-project.yaml +184 -0
  454. package/scaffolds/stardewvalley/sequences/content-pack.yaml +31 -0
  455. package/scripts/mw-tool.mjs +184 -0
@@ -0,0 +1,12 @@
1
+ import * as path from "node:path";
2
+ export function red4extLogCandidates(install) {
3
+ return [path.join(install.root, "red4ext", "logs", "red4ext-*.log")];
4
+ }
5
+ export function pluginLogCandidates(install, plugin) {
6
+ const dir = path.join(install.root, "red4ext", "plugins", plugin);
7
+ return [path.join(dir, `${plugin}.log`), path.join(dir, `${plugin}-*.log`)];
8
+ }
9
+ export function redscriptLogCandidates(install) {
10
+ const dir = path.join(install.root, "r6", "logs");
11
+ return [path.join(dir, "redscript_r*-*.log"), path.join(dir, "redscript_rCURRENT.log")];
12
+ }
@@ -0,0 +1,218 @@
1
+ import * as path from "node:path";
2
+ import { cyberpunk2077Compat } from "./compat.js";
3
+ import { pathExists, readTextIfExists } from "../../core/fsutil.js";
4
+ import { findInSteam } from "../../core/steam.js";
5
+ import { dedupeByRoot, logs, roots } from "../../core/surfaceutil.js";
6
+ import { cp2077Index } from "./index/index.js";
7
+ import { SIGNATURES } from "./logs.js";
8
+ import { BRIDGE_VALIDATORS } from "./validators/bridge.js";
9
+ import { GARMENT_VALIDATORS } from "./validators/garments.js";
10
+ import { MESH_VALIDATORS } from "./validators/mesh.js";
11
+ import { MODSETTINGS_VALIDATORS } from "./validators/modsettings.js";
12
+ import { PACKAGING_VALIDATORS } from "./validators/packaging.js";
13
+ import { RED4EXT_VALIDATORS } from "./validators/red4ext.js";
14
+ import { REDSCRIPT_VALIDATORS } from "./validators/redscript.js";
15
+ import { TEMPLATE_VALIDATORS } from "./validators/templates.js";
16
+ import { TWEAK_VALIDATORS } from "./validators/tweaks.js";
17
+ import { VEHICLE_VALIDATORS } from "./validators/vehicles.js";
18
+ import { XL_VALIDATORS } from "./validators/xl.js";
19
+ import { pluginLogCandidates, red4extLogCandidates, redscriptLogCandidates } from "./paths.js";
20
+ const NON_STEAM_CANDIDATES = [
21
+ "C:/GOG Games/Cyberpunk 2077",
22
+ "C:/Program Files (x86)/GOG Galaxy/Games/Cyberpunk 2077",
23
+ "C:/Program Files/Epic Games/Cyberpunk 2077",
24
+ ];
25
+ export const cyberpunk2077 = {
26
+ id: "cyberpunk2077",
27
+ name: "Cyberpunk 2077",
28
+ aliases: ["cp2077", "cyberpunk", "2077"],
29
+ summary: "REDengine 4. Mods split across archive/pc/mod (.archive), mods/ (REDmod), " +
30
+ "r6/scripts (redscript), r6/tweaks (TweakXL), red4ext/plugins and CET lua mods.",
31
+ async detect() {
32
+ const installs = [];
33
+ const override = process.env.MODWRIGHT_CP2077_ROOT;
34
+ if (override && (await pathExists(override))) {
35
+ installs.push({ gameId: "cyberpunk2077", root: override, source: "env" });
36
+ }
37
+ for (const root of await findInSteam(["Cyberpunk 2077"])) {
38
+ installs.push({ gameId: "cyberpunk2077", root, source: "steam" });
39
+ }
40
+ for (const candidate of NON_STEAM_CANDIDATES) {
41
+ if (await pathExists(candidate)) {
42
+ installs.push({
43
+ gameId: "cyberpunk2077",
44
+ root: candidate,
45
+ source: candidate.includes("Epic") ? "epic" : "gog",
46
+ });
47
+ }
48
+ }
49
+ return dedupeByRoot(installs);
50
+ },
51
+ modRoots(install) {
52
+ const at = (...segments) => path.join(install.root, ...segments);
53
+ return roots([
54
+ {
55
+ id: "archive",
56
+ label: "Legacy archives",
57
+ path: at("archive", "pc", "mod"),
58
+ layout: "file",
59
+ extensions: [".archive", ".xl"],
60
+ ordered: true,
61
+ conflictScope: "opaque",
62
+ notes: "Loaded alphabetically; later names win on identical resource paths. " +
63
+ "A .xl sidecar is ArchiveXL and must keep the same base name as its .archive.",
64
+ },
65
+ {
66
+ id: "redmod",
67
+ label: "REDmod",
68
+ path: at("mods"),
69
+ layout: "directory",
70
+ ordered: true,
71
+ conflictScope: "isolated",
72
+ notes: "Each subfolder needs info.json. REDmods only take effect after " +
73
+ "`tools/redmod/bin/redMod.exe deploy` runs, or with -modded on the launcher.",
74
+ },
75
+ {
76
+ id: "redscript",
77
+ label: "redscript",
78
+ path: at("r6", "scripts"),
79
+ layout: "directory",
80
+ conflictScope: "isolated",
81
+ notes: "Compiled at launch. One broken .reds aborts the whole compile and the " +
82
+ "game starts with no script mods at all — check r6/logs/redscript_rCURRENT.log.",
83
+ },
84
+ {
85
+ id: "tweaks",
86
+ label: "TweakXL",
87
+ path: at("r6", "tweaks"),
88
+ layout: "flat",
89
+ conflictScope: "merged",
90
+ notes: "YAML tweak records applied by TweakXL. Requires the TweakXL RED4ext plugin.",
91
+ },
92
+ {
93
+ id: "input",
94
+ label: "Input Loader",
95
+ path: at("r6", "input"),
96
+ layout: "flat",
97
+ conflictScope: "merged",
98
+ notes: "Input Loader XML: <bindings> that add actions to input contexts (append=\"true\") " +
99
+ "and their key mappings. Read at launch; requires the input_loader RED4ext plugin.",
100
+ },
101
+ {
102
+ id: "red4ext",
103
+ label: "RED4ext plugins",
104
+ path: at("red4ext", "plugins"),
105
+ layout: "directory",
106
+ conflictScope: "isolated",
107
+ notes: "Native .dll plugins. Version-locked to the game build — the usual cause " +
108
+ "of a silent failure to launch after a patch.",
109
+ },
110
+ {
111
+ id: "cet",
112
+ label: "Cyber Engine Tweaks mods",
113
+ path: at("bin", "x64", "plugins", "cyber_engine_tweaks", "mods"),
114
+ layout: "directory",
115
+ conflictScope: "isolated",
116
+ notes: "Lua mods, each with init.lua. Reloadable in-game from the CET console.",
117
+ },
118
+ ]);
119
+ },
120
+ async readManifest(modPath, root) {
121
+ if (root.id !== "redmod")
122
+ return undefined;
123
+ const text = await readTextIfExists(path.join(modPath, "info.json"));
124
+ if (!text)
125
+ return undefined;
126
+ const raw = JSON.parse(text);
127
+ return {
128
+ format: "info.json",
129
+ name: typeof raw.name === "string" ? raw.name : undefined,
130
+ description: typeof raw.description === "string" ? raw.description : undefined,
131
+ version: typeof raw.version === "string" ? raw.version : undefined,
132
+ raw,
133
+ };
134
+ },
135
+ logs(install) {
136
+ const at = (...segments) => path.join(install.root, ...segments);
137
+ return logs([
138
+ {
139
+ id: "redscript",
140
+ label: "redscript compiler",
141
+ candidates: redscriptLogCandidates(install),
142
+ rootId: "redscript",
143
+ notes: "First place to look when script mods silently stop working. Ends with " +
144
+ "'Output successfully saved' on a good compile.",
145
+ },
146
+ {
147
+ id: "red4ext",
148
+ label: "RED4ext loader",
149
+ candidates: red4extLogCandidates(install),
150
+ rootId: "red4ext",
151
+ notes: "One '[info] <Plugin> (version: x) has been loaded' line per plugin and a final " +
152
+ "'N plugin(s) loaded'; version mismatches after a game patch are reported here. " +
153
+ "A zero-byte log means RED4ext never initialized.",
154
+ },
155
+ {
156
+ id: "cet",
157
+ label: "Cyber Engine Tweaks",
158
+ path: at("bin", "x64", "plugins", "cyber_engine_tweaks", "cyber_engine_tweaks.log"),
159
+ rootId: "cet",
160
+ notes: "CET's own startup and hook log, appended across launches (not re-created). Per-mod " +
161
+ "Lua output goes to mods/<mod>/<mod>.log beside each mod, not here. The previous " +
162
+ "run's last line is often cut mid-write, so a run's first line can start mid-line.",
163
+ },
164
+ {
165
+ id: "archivexl",
166
+ label: "ArchiveXL",
167
+ candidates: pluginLogCandidates(install, "ArchiveXL"),
168
+ rootId: "archive",
169
+ notes: "One 'Loading \"<file>.xl\"...' per manifest, then per-section [ResourcePatch] / " +
170
+ "[Localization] / [Journal] / [WorldStreaming] lines; resolution failures are " +
171
+ "[error] lines tagged with the section.",
172
+ },
173
+ {
174
+ id: "tweakxl",
175
+ label: "TweakXL",
176
+ candidates: pluginLogCandidates(install, "TweakXL"),
177
+ rootId: "tweaks",
178
+ notes: "One 'Reading \"<file>\"...' per tweak file, then 'Import completed.' and " +
179
+ "'Execution completed.'; bad references are [warning] lines naming the record.flat.",
180
+ },
181
+ {
182
+ id: "codeware",
183
+ label: "Codeware",
184
+ candidates: pluginLogCandidates(install, "Codeware"),
185
+ rootId: "red4ext",
186
+ notes: "Codeware's own log: 'Codeware <version> is initializing...' then 'Codeware is " +
187
+ "initialized.' A missing 'is initialized' line means every redscript mod that " +
188
+ "depends on Codeware is running without it.",
189
+ },
190
+ ]);
191
+ },
192
+ logSignatures: SIGNATURES,
193
+ index: cp2077Index,
194
+ validators: [
195
+ ...TWEAK_VALIDATORS,
196
+ ...GARMENT_VALIDATORS,
197
+ ...VEHICLE_VALIDATORS,
198
+ ...MESH_VALIDATORS,
199
+ ...XL_VALIDATORS,
200
+ ...PACKAGING_VALIDATORS,
201
+ ...BRIDGE_VALIDATORS,
202
+ ...TEMPLATE_VALIDATORS,
203
+ ...MODSETTINGS_VALIDATORS,
204
+ ...REDSCRIPT_VALIDATORS,
205
+ ...RED4EXT_VALIDATORS,
206
+ ],
207
+ toolNeeds: [
208
+ { feature: "index", tools: ["wolvenkit-cli"], optional: true, note: "TweakDB records come from the REDmod tweak sources with no tool; WolvenKit CLI adds the archive resource listing (without it, that part is skipped)." },
209
+ { feature: "build", tools: ["wolvenkit-cli"], note: "wolvenkit-import and wolvenkit-pack steps, and verifying an .archive's listing." },
210
+ { feature: "build", tools: ["blender"], optional: true, note: "blender-export steps only." },
211
+ ],
212
+ processNames: ["Cyberpunk2077.exe"],
213
+ compat: cyberpunk2077Compat,
214
+ guidance: "Order of operations matters: RED4ext loads native plugins, which host ArchiveXL and " +
215
+ "TweakXL, which in turn make r6/tweaks and .xl files meaningful. A missing RED4ext " +
216
+ "explains an entire category of mods appearing to do nothing. REDmod (mods/) and legacy " +
217
+ "archives (archive/pc/mod) are separate systems and can both be present at once.",
218
+ };
@@ -0,0 +1,7 @@
1
+ import { bridgeNeverShipValidator } from "../../../core/bridge/index.js";
2
+ export const bridgeNeverShipsValidator = bridgeNeverShipValidator({
3
+ id: "cp2077.history.bridge-never-ships",
4
+ game: "cyberpunk2077",
5
+ evidence: ["ModWright research"],
6
+ });
7
+ export const BRIDGE_VALIDATORS = [bridgeNeverShipsValidator];
@@ -0,0 +1,349 @@
1
+ export function isJsonObject(v) {
2
+ return v !== null && typeof v === "object" && !Array.isArray(v);
3
+ }
4
+ export function tryParseJson(text) {
5
+ try {
6
+ return JSON.parse(text);
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ }
12
+ export function walkObjects(root, visit, jsonPath = "$") {
13
+ if (Array.isArray(root)) {
14
+ root.forEach((item, i) => walkObjects(item, visit, `${jsonPath}[${i}]`));
15
+ return;
16
+ }
17
+ if (isJsonObject(root)) {
18
+ visit(root, jsonPath);
19
+ for (const [key, value] of Object.entries(root)) {
20
+ walkObjects(value, visit, `${jsonPath}.${key}`);
21
+ }
22
+ }
23
+ }
24
+ export function collectKey(root, key) {
25
+ const hits = [];
26
+ walkObjects(root, (obj, jsonPath) => {
27
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
28
+ hits.push({ value: obj[key], jsonPath: `${jsonPath}.${key}`, container: obj });
29
+ }
30
+ });
31
+ return hits;
32
+ }
33
+ export function cnameValue(v) {
34
+ if (typeof v === "string")
35
+ return v;
36
+ if (isJsonObject(v) && typeof v.$value === "string")
37
+ return v.$value;
38
+ return undefined;
39
+ }
40
+ export function resourcePathValue(v) {
41
+ if (!isJsonObject(v))
42
+ return undefined;
43
+ if (v.$type === "ResourcePath" && typeof v.$value === "string")
44
+ return v.$value;
45
+ if ("DepotPath" in v)
46
+ return resourcePathValue(v.DepotPath);
47
+ return undefined;
48
+ }
49
+ function isSentinelPath(p) {
50
+ return p.length === 0 || /^\d+$/.test(p);
51
+ }
52
+ export function collectResourcePaths(root) {
53
+ const out = [];
54
+ walkObjects(root, (obj, jsonPath) => {
55
+ if (obj.$type === "ResourcePath" && typeof obj.$value === "string" && !isSentinelPath(obj.$value)) {
56
+ out.push({ path: obj.$value, jsonPath: `${jsonPath}.$value` });
57
+ }
58
+ });
59
+ return out;
60
+ }
61
+ export function collectDollarValueStrings(root) {
62
+ const out = [];
63
+ walkObjects(root, (obj, jsonPath) => {
64
+ if (typeof obj.$value === "string")
65
+ out.push({ value: obj.$value, jsonPath: `${jsonPath}.$value` });
66
+ });
67
+ return out;
68
+ }
69
+ export function lineOfOccurrence(text, literal, occurrence = 0) {
70
+ let index = -1;
71
+ for (let i = 0; i <= occurrence; i++) {
72
+ index = text.indexOf(literal, index + 1);
73
+ if (index === -1)
74
+ return undefined;
75
+ }
76
+ return text.slice(0, index).split("\n").length;
77
+ }
78
+ export async function readCr2wJson(ctx, absPath) {
79
+ let text;
80
+ try {
81
+ text = await ctx.readText(absPath);
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ const root = tryParseJson(text);
87
+ if (root === undefined)
88
+ return undefined;
89
+ return { root, text };
90
+ }
91
+ export async function readStagedCr2wJson(ctx, absBase) {
92
+ for (const candidate of [absBase, `${absBase}.json`]) {
93
+ if (await ctx.exists(candidate)) {
94
+ const parsed = await readCr2wJson(ctx, candidate);
95
+ if (parsed)
96
+ return { ...parsed, absPath: candidate };
97
+ }
98
+ }
99
+ return undefined;
100
+ }
101
+ export async function existsStaged(ctx, absBase) {
102
+ if (await ctx.exists(absBase))
103
+ return true;
104
+ return ctx.exists(`${absBase}.json`);
105
+ }
106
+ export function normalizeDepotPath(depotPath) {
107
+ const stripped = depotPath.startsWith("*") ? depotPath.slice(1) : depotPath;
108
+ return stripped.replace(/\//g, "\\");
109
+ }
110
+ export async function existsInVanillaIndex(ctx, depotPath) {
111
+ if (!ctx.store)
112
+ return false;
113
+ const res = await ctx.lookup("resource", { path: normalizeDepotPath(depotPath) });
114
+ return res.count > 0;
115
+ }
116
+ const CR2W_RESOURCE_EXTENSIONS = new Set([
117
+ "ent",
118
+ "app",
119
+ "mesh",
120
+ "mi",
121
+ "mlsetup",
122
+ "phys",
123
+ "streamingsector",
124
+ "streamingblock",
125
+ "inkatlas",
126
+ "inkwidget",
127
+ "csv",
128
+ "json",
129
+ "effect",
130
+ "es",
131
+ "anims",
132
+ "rig",
133
+ "xbm",
134
+ ]);
135
+ function hasKnownCr2wResourceExtension(filePath) {
136
+ const base = filePath.toLowerCase().split(/[/\\]/).pop() ?? "";
137
+ const m = /\.([a-z0-9]+)\.json$/.exec(base);
138
+ return m !== null && CR2W_RESOURCE_EXTENSIONS.has(m[1]);
139
+ }
140
+ function hasCr2wHeaderShape(root) {
141
+ if (!isJsonObject(root))
142
+ return false;
143
+ const header = root.Header;
144
+ if (!isJsonObject(header))
145
+ return false;
146
+ return header.DataType === "CR2W" || header.WKitJsonVersion !== undefined;
147
+ }
148
+ function isJsonUnderConsideration(filePath) {
149
+ const lower = filePath.toLowerCase();
150
+ if (!lower.endsWith(".json"))
151
+ return false;
152
+ if (lower.endsWith("modwright.json"))
153
+ return false;
154
+ if (lower.endsWith("suppressions.json"))
155
+ return false;
156
+ return true;
157
+ }
158
+ export function isCr2wCandidate(filePath, root) {
159
+ if (!isJsonUnderConsideration(filePath))
160
+ return false;
161
+ if (hasKnownCr2wResourceExtension(filePath))
162
+ return true;
163
+ return hasCr2wHeaderShape(root);
164
+ }
165
+ function fileTarget(t) {
166
+ return t.kind === "file" ? t.path : undefined;
167
+ }
168
+ const UNIQUE_IDS_EVIDENCE = [
169
+ "cp2077.sectors.sector-json-file-uniqueness",
170
+ "cp2077.toolchain.cli-convert-deserialize",
171
+ ];
172
+ async function checkUniqueIds(text, root, file) {
173
+ const findings = [];
174
+ for (const key of ["HandleId", "BufferId"]) {
175
+ const hits = collectKey(root, key);
176
+ const byValue = new Map();
177
+ for (const hit of hits) {
178
+ if (typeof hit.value !== "string")
179
+ continue;
180
+ const list = byValue.get(hit.value) ?? [];
181
+ list.push(hit);
182
+ byValue.set(hit.value, list);
183
+ }
184
+ for (const [value, list] of byValue) {
185
+ if (list.length < 2)
186
+ continue;
187
+ const lines = list.map((_, i) => lineOfOccurrence(text, `"${key}": "${value}"`, i)).filter((l) => l !== undefined);
188
+ findings.push({
189
+ validatorId: "cp2077.vehicles.cr2w-unique-ids",
190
+ severity: "block",
191
+ file,
192
+ line: lines[0],
193
+ message: `${key} "${value}" appears ${list.length} times in this CR2W json file (at ${list.map((h) => h.jsonPath).join(", ")}); ` +
194
+ `${key}s must be unique across the whole file, not per node. WolvenKit's convert deserialize fails on the duplicate with ` +
195
+ `only "a useless generic JsonException at the buffer's closing brace" — assign a fresh, unused ${key} to every occurrence but one.`,
196
+ fix: `Change all but one of these ${key} values to a number not used anywhere else in the file.`,
197
+ evidence: UNIQUE_IDS_EVIDENCE,
198
+ });
199
+ }
200
+ }
201
+ return findings;
202
+ }
203
+ const PLAIN_ENUM_FIELDS = ["category", "lootQuality", "containerType", "cookingPlatform"];
204
+ const ENUM_EVIDENCE = ["cp2077.toolchain.cr2w-enums-are-strings"];
205
+ function checkEnumsAreStrings(text, root, file) {
206
+ const findings = [];
207
+ walkObjects(root, (obj, jsonPath) => {
208
+ for (const key of PLAIN_ENUM_FIELDS) {
209
+ const v = obj[key];
210
+ if (typeof v === "number") {
211
+ findings.push(enumFinding(text, file, key, v, `${jsonPath}.${key}`));
212
+ }
213
+ }
214
+ if ("Flags" in obj && "DepotPath" in obj && typeof obj.Flags === "number") {
215
+ findings.push(enumFinding(text, file, "Flags", obj.Flags, `${jsonPath}.Flags`));
216
+ }
217
+ });
218
+ return findings;
219
+ }
220
+ function enumFinding(text, file, key, value, jsonPath) {
221
+ const line = lineOfOccurrence(text, `"${key}": ${value}`);
222
+ return {
223
+ validatorId: "cp2077.vehicles.cr2w-enums-are-strings",
224
+ severity: "block",
225
+ file,
226
+ line,
227
+ message: `"${key}": ${value} at ${jsonPath} is a bare number; CR2W json enums are quoted strings (e.g. "Always"), ` +
228
+ "never bare numbers or unquoted names — a bare enum value is invalid json for this format.",
229
+ fix: `Replace the numeric ${value} with the enum's string name (check a known-good CR2W json for the same $type to find it).`,
230
+ evidence: ENUM_EVIDENCE,
231
+ };
232
+ }
233
+ const RESOURCE_PATH_EVIDENCE = ["cp2077.toolchain.resource-path-shape", "cp2077.archivexl.factory-csv-appearance-mapping"];
234
+ function checkResourcePathShape(root, file) {
235
+ const findings = [];
236
+ for (const { path: p, jsonPath } of collectResourcePaths(root)) {
237
+ const problems = [];
238
+ if (p.includes("/"))
239
+ problems.push("uses a forward slash instead of a backslash");
240
+ if (p.startsWith("\\") || p.startsWith("/"))
241
+ problems.push("has a leading slash");
242
+ if (p !== p.toLowerCase())
243
+ problems.push("has uppercase characters");
244
+ if (problems.length === 0)
245
+ continue;
246
+ findings.push({
247
+ validatorId: "cp2077.vehicles.cr2w-resource-path-shape",
248
+ severity: "warn",
249
+ file,
250
+ message: `Resource path "${p}" at ${jsonPath} ${problems.join(", ")}. Shipping CR2W depot paths are backslash-separated, ` +
251
+ `no leading slash, all lowercase (e.g. "mod\\\\mymod\\\\cache_case.ent").`,
252
+ fix: "Rewrite the path with backslashes, no leading slash, and lowercase.",
253
+ evidence: RESOURCE_PATH_EVIDENCE,
254
+ });
255
+ }
256
+ return findings;
257
+ }
258
+ const GAMEVERSION_EVIDENCE = [
259
+ "cp2077.toolchain.gameversion-header",
260
+ "cp2077.packaging.game-version-compatibility",
261
+ "observed: a WolvenKit 8.20.0 en-us.source.json header carries GameVersion 2310",
262
+ ];
263
+ function expectedGameVersion(marketingVersion) {
264
+ const m = /^(\d+)\.(\d{1,2})$/.exec(marketingVersion.trim());
265
+ if (!m)
266
+ return undefined;
267
+ const major = m[1];
268
+ const minor = m[2].padStart(2, "0");
269
+ return Number(`${major}${minor}0`);
270
+ }
271
+ function checkGameVersionHeader(root, file, installVersion) {
272
+ if (!isJsonObject(root))
273
+ return [];
274
+ const header = isJsonObject(root.Header) ? root.Header : undefined;
275
+ const gameVersion = header?.GameVersion;
276
+ if (gameVersion === undefined) {
277
+ return [
278
+ {
279
+ validatorId: "cp2077.vehicles.cr2w-gameversion-header",
280
+ severity: "warn",
281
+ file,
282
+ message: "This CR2W json has no Header.GameVersion. WolvenKit writes one on every serialize/extract " +
283
+ '(e.g. a mod\'s en-us.source.json: WolvenKitVersion "8.20.0", GameVersion 2310); ' +
284
+ "a hand-edited or hand-assembled json missing it cannot be confirmed to target the intended engine build.",
285
+ fix: 'Add a Header with GameVersion set to the value WolvenKit writes for the game version you are building against (e.g. 2310 for game 2.31).',
286
+ evidence: GAMEVERSION_EVIDENCE,
287
+ },
288
+ ];
289
+ }
290
+ if (typeof gameVersion !== "number" || !installVersion)
291
+ return [];
292
+ const expected = expectedGameVersion(installVersion);
293
+ if (expected === undefined)
294
+ return [];
295
+ if (expected !== gameVersion) {
296
+ return [
297
+ {
298
+ validatorId: "cp2077.vehicles.cr2w-gameversion-header",
299
+ severity: "warn",
300
+ file,
301
+ message: `Header.GameVersion is ${gameVersion}, but the located install reports game version ${installVersion} ` +
302
+ `(expected GameVersion ${expected} on that build family). A mod's CR2W assets built for one game version ` +
303
+ "may not load correctly on another.",
304
+ fix: "Re-serialize this asset with a WolvenKit CLI matching the target game version, or confirm the install version.",
305
+ evidence: GAMEVERSION_EVIDENCE,
306
+ },
307
+ ];
308
+ }
309
+ return [];
310
+ }
311
+ function makeCr2wValidator(id, summary, severity, evidence, needsInstall, check) {
312
+ return {
313
+ id,
314
+ game: "cyberpunk2077",
315
+ groups: ["vehicles"],
316
+ modTypes: ["vehicle", "garment", "world-sector", "ui"],
317
+ summary,
318
+ appliesTo: (target) => {
319
+ const file = fileTarget(target);
320
+ return file !== undefined && isJsonUnderConsideration(file);
321
+ },
322
+ appliesToDescription: "every .json file in the project except modwright.json and .modwright/suppressions.json, narrowed at run time to genuine CR2W resource json (a known resource extension, or a Header with DataType \"CR2W\"/WKitJsonVersion)",
323
+ needs: { project: true, ...(needsInstall ? { install: true } : {}) },
324
+ cost: "edit",
325
+ severity,
326
+ evidence,
327
+ async run(ctx) {
328
+ const file = fileTarget(ctx.target);
329
+ if (!file)
330
+ return [];
331
+ const parsed = await readCr2wJson(ctx, file);
332
+ if (!parsed)
333
+ return [];
334
+ if (!isCr2wCandidate(file, parsed.root))
335
+ return [];
336
+ return check({ text: parsed.text, root: parsed.root, file, project: ctx.project, install: ctx.install });
337
+ },
338
+ };
339
+ }
340
+ export const CR2W_UNIQUE_IDS = makeCr2wValidator("cp2077.vehicles.cr2w-unique-ids", "Every HandleId and every BufferId is unique across the whole CR2W json file.", "block", UNIQUE_IDS_EVIDENCE, false, ({ text, root, file }) => checkUniqueIds(text, root, file));
341
+ export const CR2W_ENUMS_ARE_STRINGS = makeCr2wValidator("cp2077.vehicles.cr2w-enums-are-strings", "Enum-valued fields (category, lootQuality, containerType, cookingPlatform, and resource-reference Flags) are quoted strings, never bare numbers.", "block", ENUM_EVIDENCE, false, ({ text, root, file }) => checkEnumsAreStrings(text, root, file));
342
+ export const CR2W_RESOURCE_PATH_SHAPE = makeCr2wValidator("cp2077.vehicles.cr2w-resource-path-shape", "DepotPath/resource-path values use backslashes, no leading slash, and lowercase.", "warn", RESOURCE_PATH_EVIDENCE, false, ({ root, file }) => checkResourcePathShape(root, file));
343
+ export const CR2W_GAMEVERSION_HEADER = makeCr2wValidator("cp2077.vehicles.cr2w-gameversion-header", "The CR2W json Header carries GameVersion, compared against the installed game's version when known.", "warn", GAMEVERSION_EVIDENCE, true, ({ root, file, install }) => checkGameVersionHeader(root, file, install?.version));
344
+ export const CR2W_VALIDATORS = [
345
+ CR2W_UNIQUE_IDS,
346
+ CR2W_ENUMS_ARE_STRINGS,
347
+ CR2W_RESOURCE_PATH_SHAPE,
348
+ CR2W_GAMEVERSION_HEADER,
349
+ ];