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,790 @@
1
+ import * as path from "node:path";
2
+ import { walk } from "../../../core/fsutil.js";
3
+ import { parseLsx, walkLsxNodes } from "../index/parsers/lsx.js";
4
+ function posix(p) {
5
+ return p.split(path.sep).join("/");
6
+ }
7
+ function lower(p) {
8
+ return posix(p).toLowerCase();
9
+ }
10
+ function stripBom(text) {
11
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
12
+ }
13
+ async function projectFiles(project, match) {
14
+ const out = [];
15
+ for (const src of project.sources) {
16
+ if (!src.exists)
17
+ continue;
18
+ const { files } = await walk(src.absPath, { limit: 20000 });
19
+ for (const f of files) {
20
+ const abs = path.join(src.absPath, f);
21
+ if (match(lower(abs)))
22
+ out.push(abs);
23
+ }
24
+ }
25
+ return out;
26
+ }
27
+ function fileTarget(match) {
28
+ return (target) => target.kind === "file" && match(lower(target.path));
29
+ }
30
+ function finding(validatorId, severity, file, message, evidence, extra) {
31
+ return { validatorId, severity, file, message, evidence, ...extra };
32
+ }
33
+ const isGoalFile = (p) => p.endsWith(".txt") && p.includes("/goals/");
34
+ const isJournalLsx = (p) => p.endsWith(".lsx") && p.includes("/journals/");
35
+ const isOriginsLsx = (p) => /^origins(\..+)?\.lsx$/.test(path.posix.basename(p));
36
+ const isTagsLsx = (p) => path.posix.basename(p) === "tags.lsx" || p.includes("/tags/");
37
+ const isDialogLsj = (p) => p.endsWith(".lsj") && p.includes("/story/dialog");
38
+ const isDialogBankLsx = (p) => p.endsWith(".lsx") && p.includes("dialogbank");
39
+ const SECTION_NAMES = ["INIT", "KB", "EXIT"];
40
+ const CALL_RE = /\b(DB_[A-Za-z0-9_]+|QRY_[A-Za-z0-9_]+|PROC_[A-Za-z0-9_]+|QuestUpdate|QuestAdd|QuestClose|GoalCompleted|SysClear)\s*\(([^)]*)\)/g;
41
+ function splitArgs(raw) {
42
+ return raw.split(",").map((s) => s.trim());
43
+ }
44
+ function parseGoalFile(text, file) {
45
+ const normalized = stripBom(text).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
46
+ const lines = normalized.split("\n");
47
+ const parentMatch = /^\s*\/\/\s*Parent:\s*(\S+)/im.exec(normalized);
48
+ const goal = {
49
+ file,
50
+ text: normalized,
51
+ lines,
52
+ sectionOrder: [],
53
+ parent: parentMatch?.[1],
54
+ definedDb: new Set(),
55
+ checkedDb: [],
56
+ definedQryProc: new Set(),
57
+ calledQryProc: [],
58
+ sysClearInKb: [],
59
+ questAddClose: [],
60
+ questUpdateCalls: [],
61
+ goalCompletedCount: 0,
62
+ };
63
+ const boundaries = [];
64
+ lines.forEach((raw, i) => {
65
+ const t = raw.trim();
66
+ if (t === "INIT" || t === "KB" || t === "EXIT") {
67
+ boundaries.push({ section: t, start: i });
68
+ goal.sectionOrder.push(t);
69
+ }
70
+ });
71
+ for (let b = 0; b < boundaries.length; b++) {
72
+ const section = boundaries[b].section;
73
+ const start = boundaries[b].start + 1;
74
+ const end = b + 1 < boundaries.length ? boundaries[b + 1].start : lines.length;
75
+ processSection(goal, section, start, end);
76
+ }
77
+ return goal;
78
+ }
79
+ function processSection(goal, section, start, end) {
80
+ let blockStart = start;
81
+ for (let i = start; i <= end; i++) {
82
+ const raw = goal.lines[i];
83
+ const isBlank = i === end || (raw ?? "").trim().length === 0;
84
+ if (isBlank) {
85
+ if (i > blockStart)
86
+ processBlock(goal, section, blockStart, i);
87
+ blockStart = i + 1;
88
+ }
89
+ }
90
+ }
91
+ function processBlock(goal, section, start, end) {
92
+ const blockLines = [];
93
+ for (let i = start; i < end; i++) {
94
+ const t = (goal.lines[i] ?? "").trim();
95
+ if (t.length === 0 || t.startsWith("//"))
96
+ continue;
97
+ blockLines.push({ i, text: t });
98
+ }
99
+ if (blockLines.length === 0)
100
+ return;
101
+ const thenIdx = blockLines.findIndex((l) => l.text === "THEN");
102
+ const ifIdx = blockLines.findIndex((l) => l.text === "IF");
103
+ const hasThen = thenIdx !== -1;
104
+ const hasIf = ifIdx !== -1;
105
+ const conditionLines = hasThen ? blockLines.slice(hasIf ? ifIdx + 1 : 0, thenIdx) : hasIf ? blockLines.slice(ifIdx + 1) : [];
106
+ const actionLines = hasThen ? blockLines.slice(thenIdx + 1) : hasIf ? [] : blockLines;
107
+ let firstCall = true;
108
+ const scan = (group, isCondition) => {
109
+ for (const { i, text } of group) {
110
+ CALL_RE.lastIndex = 0;
111
+ let m;
112
+ while ((m = CALL_RE.exec(text))) {
113
+ const name = m[1];
114
+ const line = i + 1;
115
+ if (name.startsWith("DB_")) {
116
+ if (isCondition)
117
+ goal.checkedDb.push({ name, line });
118
+ else
119
+ goal.definedDb.add(name);
120
+ }
121
+ else if (name.startsWith("QRY_") || name.startsWith("PROC_")) {
122
+ if (hasThen && isCondition && firstCall)
123
+ goal.definedQryProc.add(name);
124
+ else
125
+ goal.calledQryProc.push({ name, line });
126
+ }
127
+ else if (name === "QuestUpdate") {
128
+ goal.questUpdateCalls.push({ args: splitArgs(m[2] ?? ""), line });
129
+ }
130
+ else if (name === "QuestAdd" || name === "QuestClose") {
131
+ goal.questAddClose.push({ name, line });
132
+ }
133
+ else if (name === "GoalCompleted") {
134
+ goal.goalCompletedCount++;
135
+ }
136
+ else if (name === "SysClear") {
137
+ if (section === "KB")
138
+ goal.sysClearInKb.push({ name, line });
139
+ }
140
+ firstCall = false;
141
+ }
142
+ }
143
+ };
144
+ scan(conditionLines, true);
145
+ scan(actionLines, false);
146
+ }
147
+ const ENGINE_SET_DBS = new Set(["DB_QuestIsAccepted", "DB_QuestIsOpened", "DB_QuestIsClosed"]);
148
+ async function loadGoalFiles(project) {
149
+ const files = await projectFiles(project, isGoalFile);
150
+ const out = [];
151
+ for (const f of files) {
152
+ const { promises: fs } = await import("node:fs");
153
+ try {
154
+ out.push(parseGoalFile(await fs.readFile(f, "utf8"), f));
155
+ }
156
+ catch {
157
+ }
158
+ }
159
+ return out;
160
+ }
161
+ const osirisSymbolDefined = {
162
+ id: "bg3.story.osiris-symbol-defined",
163
+ game: "baldursgate3",
164
+ groups: ["story"],
165
+ modTypes: ["quest", "dialogue"],
166
+ summary: "Every DB_ symbol checked in KB is defined in INIT or a THEN block somewhere in the project; every QRY_/PROC_ called resolves to a definition.",
167
+ appliesTo: fileTarget(isGoalFile),
168
+ appliesToDescription: "every Osiris goal script (a .txt file under a Goals/ directory)",
169
+ needs: { project: true },
170
+ cost: "edit",
171
+ severity: "block",
172
+ evidence: [
173
+ "bg3.dialogue.osiris-goals.db-checked-but-never-defined-error",
174
+ "bg3.dialogue.osiris-goals.qry-proc-color-coding",
175
+ ],
176
+ async run(ctx) {
177
+ if (ctx.target.kind !== "file")
178
+ return [];
179
+ const allGoals = await loadGoalFiles(ctx.project);
180
+ const definedDb = new Set();
181
+ const definedQryProc = new Set();
182
+ for (const g of allGoals) {
183
+ for (const d of g.definedDb)
184
+ definedDb.add(d);
185
+ for (const d of g.definedQryProc)
186
+ definedQryProc.add(d);
187
+ }
188
+ const text = await ctx.readText(ctx.target.path);
189
+ const goal = parseGoalFile(text, ctx.target.path);
190
+ const findings = [];
191
+ for (const { name, line } of goal.checkedDb) {
192
+ if (ENGINE_SET_DBS.has(name))
193
+ continue;
194
+ if (definedDb.has(name))
195
+ continue;
196
+ findings.push(finding(osirisSymbolDefined.id, "block", ctx.target.path, `'${name}' is checked here but is never defined in INIT or a THEN block anywhere in this project. ` +
197
+ `Vanilla story symbols were not checked (ModWright does not extract the vanilla .osi story), so this ` +
198
+ `is reported only against the project's own goals; if '${name}' is a vanilla database this finding is wrong and should be suppressed.`, osirisSymbolDefined.evidence, { line, fix: `Define ${name} in this goal's INIT section or in a THEN block, or remove the check.` }));
199
+ }
200
+ for (const { name, line } of goal.calledQryProc) {
201
+ if (definedQryProc.has(name))
202
+ continue;
203
+ findings.push(finding(osirisSymbolDefined.id, "block", ctx.target.path, `'${name}' is called here but no goal in this project defines it (a rule whose head is '${name}'). ` +
204
+ `The Story Editor reports this as "Could not find any complete/correct definition of Osiris User Query/Procedure".`, osirisSymbolDefined.evidence, { line }));
205
+ }
206
+ return findings;
207
+ },
208
+ };
209
+ const osirisNamingConvention = {
210
+ id: "bg3.story.osiris-naming-convention",
211
+ game: "baldursgate3",
212
+ groups: ["story"],
213
+ modTypes: ["quest", "dialogue"],
214
+ summary: "User-defined facts/queries/procs carry the DB_/QRY_/PROC_ prefix; new goal files follow Act_Region_Situation.",
215
+ appliesTo: fileTarget(isGoalFile),
216
+ appliesToDescription: "every Osiris goal script (a .txt file under a Goals/ directory)",
217
+ needs: {},
218
+ cost: "edit",
219
+ severity: "info",
220
+ evidence: [
221
+ "bg3.dialogue.osiris-goals.act-region-situation-naming-convention",
222
+ "bg3.dialogue.osiris-goals.qry-proc-color-coding",
223
+ ],
224
+ async run(ctx) {
225
+ if (ctx.target.kind !== "file")
226
+ return [];
227
+ const findings = [];
228
+ const base = path.posix.basename(lower(ctx.target.path), ".txt");
229
+ if (!/^[a-z0-9]+(_[a-z0-9]+){2,}$/i.test(base)) {
230
+ findings.push(finding(osirisNamingConvention.id, "info", ctx.target.path, `Goal file name '${path.basename(ctx.target.path)}' does not obviously follow the Act_Region_Situation convention. ` +
231
+ `This is a naming convention, not a known build failure — no fact in knowledge/baldursgate3/ confirms a missing ` +
232
+ `prefix disables the story component (still an open question), so this stays informational.`, osirisNamingConvention.evidence));
233
+ }
234
+ const text = stripBom(await ctx.readText(ctx.target.path)).replace(/\r\n/g, "\n");
235
+ const badPrefixRe = /\b(?!DB_|QRY_|PROC_)([A-Za-z][A-Za-z0-9_]*)\s*\(/g;
236
+ let m;
237
+ const seen = new Set();
238
+ while ((m = badPrefixRe.exec(text))) {
239
+ const name = m[1];
240
+ if (seen.has(name))
241
+ continue;
242
+ if (!/_/.test(name))
243
+ continue;
244
+ if (["GoalCompleted", "SysClear", "QuestUpdate", "QuestAdd", "QuestClose"].includes(name))
245
+ continue;
246
+ seen.add(name);
247
+ findings.push(finding(osirisNamingConvention.id, "info", ctx.target.path, `'${name}' does not carry the DB_/QRY_/PROC_ prefix convention.`, osirisNamingConvention.evidence));
248
+ }
249
+ return findings;
250
+ },
251
+ };
252
+ const osirisSectionStructure = {
253
+ id: "bg3.story.osiris-section-structure",
254
+ game: "baldursgate3",
255
+ groups: ["story"],
256
+ modTypes: ["quest", "dialogue"],
257
+ summary: "Every goal file has INIT, KB and EXIT sections in that order; SysClear of a goal DB belongs in EXIT, not KB.",
258
+ appliesTo: fileTarget(isGoalFile),
259
+ appliesToDescription: "every Osiris goal script (a .txt file under a Goals/ directory)",
260
+ needs: {},
261
+ cost: "edit",
262
+ severity: "block",
263
+ evidence: ["bg3.dialogue.osiris-goals.init-kb-exit-sections", "bg3.dialogue.osiris-goals.exit-clears-databases"],
264
+ async run(ctx) {
265
+ if (ctx.target.kind !== "file")
266
+ return [];
267
+ const text = await ctx.readText(ctx.target.path);
268
+ const goal = parseGoalFile(text, ctx.target.path);
269
+ const findings = [];
270
+ const missing = SECTION_NAMES.filter((s) => !goal.sectionOrder.includes(s));
271
+ if (missing.length > 0) {
272
+ findings.push(finding(osirisSectionStructure.id, "block", ctx.target.path, `Missing section(s): ${missing.join(", ")}. Every goal script needs INIT, KB and EXIT in that order.`, osirisSectionStructure.evidence));
273
+ }
274
+ else {
275
+ const order = goal.sectionOrder.filter((s, i, arr) => arr.indexOf(s) === i);
276
+ if (order.join(",") !== "INIT,KB,EXIT") {
277
+ findings.push(finding(osirisSectionStructure.id, "block", ctx.target.path, `Sections appear out of order (${goal.sectionOrder.join(" -> ")}). Expected INIT, then KB, then EXIT.`, osirisSectionStructure.evidence));
278
+ }
279
+ }
280
+ for (const occ of goal.sysClearInKb) {
281
+ findings.push(finding(osirisSectionStructure.id, "info", ctx.target.path, `SysClear(...) in KB (line ${occ.line}) is conventionally placed in EXIT — KB stops being evaluated once EXIT runs, so a KB-section SysClear is unusual placement, not a build error.`, osirisSectionStructure.evidence, { line: occ.line }));
282
+ }
283
+ return findings;
284
+ },
285
+ };
286
+ const goalParentCompletes = {
287
+ id: "bg3.story.goal-parent-completes",
288
+ game: "baldursgate3",
289
+ groups: ["story"],
290
+ modTypes: ["quest", "dialogue"],
291
+ summary: "A goal nested under a project-authored parent must have some path where that parent calls GoalCompleted.",
292
+ appliesTo: fileTarget(isGoalFile),
293
+ appliesToDescription: "every Osiris goal script that declares a '// Parent: <Goal>' header authored in this project",
294
+ needs: { project: true },
295
+ cost: "edit",
296
+ severity: "warn",
297
+ evidence: [
298
+ "bg3.dialogue.osiris-goals.parent-goal-must-complete-before-children-activate",
299
+ "bg3.dialogue.osiris-goals.goals-execute-top-to-bottom",
300
+ ],
301
+ async run(ctx) {
302
+ if (ctx.target.kind !== "file")
303
+ return [];
304
+ const text = await ctx.readText(ctx.target.path);
305
+ const goal = parseGoalFile(text, ctx.target.path);
306
+ if (!goal.parent)
307
+ return [];
308
+ const allGoals = await loadGoalFiles(ctx.project);
309
+ const parentGoal = allGoals.find((g) => path.posix.basename(lower(g.file), ".txt") === goal.parent.toLowerCase());
310
+ if (!parentGoal)
311
+ return [];
312
+ if (parentGoal.goalCompletedCount === 0) {
313
+ return [
314
+ finding(goalParentCompletes.id, "warn", ctx.target.path, `This goal declares parent '${goal.parent}', but that parent goal (${path.basename(parentGoal.file)}) never calls GoalCompleted anywhere — a child goal under a parent that never completes never activates.`, goalParentCompletes.evidence, { related: [parentGoal.file] }),
315
+ ];
316
+ }
317
+ return [];
318
+ },
319
+ };
320
+ function collectJournal(doc) {
321
+ const topLevelQuestIds = new Set();
322
+ const subQuestIds = new Set();
323
+ const entryIds = new Set();
324
+ for (const region of doc.regions) {
325
+ walkLsxNodes(region.root, (node, breadcrumb) => {
326
+ if (node.id === "Category") {
327
+ const name = node.attributes.Name?.value;
328
+ if (name)
329
+ topLevelQuestIds.add(name);
330
+ }
331
+ else if (node.id === "SubQuest") {
332
+ const name = node.attributes.Name?.value;
333
+ if (name)
334
+ subQuestIds.add(name);
335
+ }
336
+ else if (node.id === "Entry") {
337
+ const idAttr = node.attributes.ID?.value;
338
+ if (idAttr)
339
+ entryIds.add(idAttr);
340
+ }
341
+ void breadcrumb;
342
+ });
343
+ }
344
+ return { topLevelQuestIds, subQuestIds, entryIds };
345
+ }
346
+ async function loadJournal(project, ctx) {
347
+ const files = await projectFiles(project, isJournalLsx);
348
+ if (files.length === 0)
349
+ return undefined;
350
+ const index = { topLevelQuestIds: new Set(), subQuestIds: new Set(), entryIds: new Set() };
351
+ for (const f of files) {
352
+ try {
353
+ const text = await ctx.readText(f);
354
+ const partial = collectJournal(parseLsx(text, f));
355
+ for (const v of partial.topLevelQuestIds)
356
+ index.topLevelQuestIds.add(v);
357
+ for (const v of partial.subQuestIds)
358
+ index.subQuestIds.add(v);
359
+ for (const v of partial.entryIds)
360
+ index.entryIds.add(v);
361
+ }
362
+ catch {
363
+ }
364
+ }
365
+ return index;
366
+ }
367
+ function literalArg(arg) {
368
+ const m = /^"([^"]*)"$/.exec(arg.trim());
369
+ return m?.[1];
370
+ }
371
+ const questUpdateParentId = {
372
+ id: "bg3.story.questupdate-parent-id",
373
+ game: "baldursgate3",
374
+ groups: ["story"],
375
+ modTypes: ["quest", "journal"],
376
+ summary: "QuestUpdate's _QuestID names a top-level quest, never a subquest id, even when updating a subquest entry.",
377
+ appliesTo: fileTarget(isGoalFile),
378
+ appliesToDescription: "every Osiris goal script that calls QuestUpdate with a literal _QuestID argument",
379
+ needs: { project: true },
380
+ cost: "edit",
381
+ severity: "block",
382
+ evidence: [
383
+ "bg3.journal.quests.questupdate-requires-parent-quest-id",
384
+ "bg3.journal.quests.questupdateunlocked-reports-top-level-quest-id",
385
+ ],
386
+ async run(ctx) {
387
+ if (ctx.target.kind !== "file")
388
+ return [];
389
+ const journal = await loadJournal(ctx.project, ctx);
390
+ if (!journal)
391
+ return [];
392
+ const text = await ctx.readText(ctx.target.path);
393
+ const goal = parseGoalFile(text, ctx.target.path);
394
+ const findings = [];
395
+ for (const call of goal.questUpdateCalls) {
396
+ const questIdArg = call.args.length >= 3 ? call.args[1] : call.args[0];
397
+ const questId = questIdArg ? literalArg(questIdArg) : undefined;
398
+ if (questId === undefined)
399
+ continue;
400
+ if (journal.subQuestIds.has(questId) && !journal.topLevelQuestIds.has(questId)) {
401
+ findings.push(finding(questUpdateParentId.id, "block", ctx.target.path, `QuestUpdate's _QuestID is '${questId}', which is a subquest id in the journal, not a top-level quest id. ` +
402
+ `QuestUpdate always requires the parent quest's own ID, even when updating a subquest entry.`, questUpdateParentId.evidence, { line: call.line }));
403
+ }
404
+ }
405
+ return findings;
406
+ },
407
+ };
408
+ const questEntryExists = {
409
+ id: "bg3.story.quest-entry-exists",
410
+ game: "baldursgate3",
411
+ groups: ["story"],
412
+ modTypes: ["quest", "journal"],
413
+ summary: "Every _EntryID / _Update / _Step / _NewStep resolves to a journal node in the mod's journal LSX or the vanilla index.",
414
+ appliesTo: fileTarget(isGoalFile),
415
+ appliesToDescription: "every Osiris goal script that references a journal entry id",
416
+ needs: { project: true, index: true },
417
+ cost: "edit",
418
+ severity: "block",
419
+ evidence: [
420
+ "bg3.journal.quests.db-questdef-state-reacts-to-flag",
421
+ "bg3.journal.quests.db-questdef-chainedstate-links-entries",
422
+ "bg3.journal.quests.db-questdef-conditionalstate-defined-multiple-times",
423
+ "bg3.journal.quests.questupdate-does-not-emit-a-journal-entry-by-itself",
424
+ ],
425
+ async run(ctx) {
426
+ if (ctx.target.kind !== "file")
427
+ return [];
428
+ const journal = await loadJournal(ctx.project, ctx);
429
+ if (!journal)
430
+ return [];
431
+ const targetPath = ctx.target.path;
432
+ const text = await ctx.readText(targetPath);
433
+ const goal = parseGoalFile(text, targetPath);
434
+ const findings = [];
435
+ const checkEntry = async (entryId, line, calledFrom) => {
436
+ if (journal.entryIds.has(entryId))
437
+ return;
438
+ const vanilla = await ctx.lookup("guid", { name: entryId });
439
+ if (vanilla.count > 0)
440
+ return;
441
+ findings.push(finding(questEntryExists.id, "block", targetPath, `${calledFrom}: entry id '${entryId}' does not resolve to a journal node in this project's journal LSX or the vanilla index.`, questEntryExists.evidence, { line }));
442
+ };
443
+ for (const call of goal.questUpdateCalls) {
444
+ const entryArg = call.args[call.args.length - 1];
445
+ const entryId = entryArg ? literalArg(entryArg) : undefined;
446
+ if (entryId !== undefined)
447
+ await checkEntry(entryId, call.line, "QuestUpdate");
448
+ }
449
+ const dbRe = /(DB_QuestDef_(?:State|ChainedState|ConditionalState))\s*\(([^)]*)\)/g;
450
+ let m;
451
+ while ((m = dbRe.exec(goal.text))) {
452
+ const args = splitArgs(m[2] ?? "");
453
+ const line = goal.text.slice(0, m.index).split("\n").length;
454
+ for (const raw of args.slice(1)) {
455
+ const lit = literalArg(raw);
456
+ if (lit !== undefined && !/^[0-9a-fA-F-]{8,}$/.test(lit)) {
457
+ await checkEntry(lit, line, m[1]);
458
+ }
459
+ }
460
+ }
461
+ return findings;
462
+ },
463
+ };
464
+ const questAddQuestCloseDiscouraged = {
465
+ id: "bg3.story.questadd-questclose-discouraged",
466
+ game: "baldursgate3",
467
+ groups: ["story"],
468
+ modTypes: ["quest", "journal"],
469
+ summary: "QuestAdd/QuestClose exist but the documentation recommends QuestUpdate instead.",
470
+ appliesTo: fileTarget(isGoalFile),
471
+ appliesToDescription: "every Osiris goal script that calls QuestAdd or QuestClose",
472
+ needs: {},
473
+ cost: "edit",
474
+ severity: "info",
475
+ evidence: ["bg3.journal.quests.questadd-questclose-should-be-avoided"],
476
+ async run(ctx) {
477
+ if (ctx.target.kind !== "file")
478
+ return [];
479
+ const targetPath = ctx.target.path;
480
+ const text = await ctx.readText(targetPath);
481
+ const goal = parseGoalFile(text, targetPath);
482
+ return goal.questAddClose.map((occ) => finding(questAddQuestCloseDiscouraged.id, "info", targetPath, `${occ.name}(...) is used here. The documentation recommends relying on QuestUpdate instead of manually adding/closing quests.`, questAddQuestCloseDiscouraged.evidence, { line: occ.line }));
483
+ },
484
+ };
485
+ const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
486
+ function collectOrigins(doc) {
487
+ const origins = [];
488
+ for (const region of doc.regions) {
489
+ walkLsxNodes(region.root, (node) => {
490
+ if (node.id !== "Origin")
491
+ return;
492
+ const tagUuids = [];
493
+ const reallyTags = node.children.find((c) => c.id === "ReallyTags");
494
+ for (const obj of reallyTags?.children ?? []) {
495
+ const v = obj.attributes.Object?.value;
496
+ if (v)
497
+ tagUuids.push(v);
498
+ }
499
+ origins.push({ name: node.attributes.Name?.value, tagUuids });
500
+ });
501
+ }
502
+ return origins;
503
+ }
504
+ function collectTagUuids(doc) {
505
+ const uuids = new Set();
506
+ for (const region of doc.regions) {
507
+ walkLsxNodes(region.root, (node) => {
508
+ if (node.id !== "Tag")
509
+ return;
510
+ const v = node.attributes.UUID?.value;
511
+ if (v)
512
+ uuids.add(v);
513
+ });
514
+ }
515
+ return uuids;
516
+ }
517
+ const originsTagsDefined = {
518
+ id: "bg3.story.origins-tags-defined",
519
+ game: "baldursgate3",
520
+ groups: ["story"],
521
+ modTypes: ["origin"],
522
+ summary: "Every ReallyTags UUID in Origins.lsx is defined in the mod's Tags.lsx or resolves in the vanilla index.",
523
+ appliesTo: fileTarget(isOriginsLsx),
524
+ appliesToDescription: "the project's Origins.lsx",
525
+ needs: { project: true, index: true },
526
+ cost: "edit",
527
+ severity: "block",
528
+ evidence: [
529
+ "bg3.crash.dangling-tag-guid",
530
+ "bg3.origins.companions.origins-lsx-tag-must-be-a-real-origin-tag",
531
+ ],
532
+ async run(ctx) {
533
+ if (ctx.target.kind !== "file")
534
+ return [];
535
+ const text = await ctx.readText(ctx.target.path);
536
+ const origins = collectOrigins(parseLsx(text, ctx.target.path));
537
+ const tagFiles = await projectFiles(ctx.project, isTagsLsx);
538
+ const projectTags = new Set();
539
+ for (const f of tagFiles) {
540
+ try {
541
+ for (const uuid of collectTagUuids(parseLsx(await ctx.readText(f), f)))
542
+ projectTags.add(uuid);
543
+ }
544
+ catch {
545
+ }
546
+ }
547
+ const findings = [];
548
+ for (const origin of origins) {
549
+ for (const uuid of origin.tagUuids) {
550
+ if (projectTags.has(uuid))
551
+ continue;
552
+ const vanilla = await ctx.lookup("guid", { uuid });
553
+ if (vanilla.count > 0)
554
+ continue;
555
+ findings.push(finding(originsTagsDefined.id, "block", ctx.target.path, `Origin '${origin.name ?? "?"}' names tag ${uuid} in ReallyTags, but no Tags.lsx in this project defines it and it does not resolve in the vanilla index. ` +
556
+ `This is believed (unverified) to null-deref tag lookup on character select; a first-hand crash report exists for the same shape of bug, so the severity stays at block even though the specific fact is unverified.`, originsTagsDefined.evidence, { fix: `Define ${uuid} in this project's Tags.lsx, or use a vanilla/existing origin's tag UUID instead.` }));
557
+ }
558
+ }
559
+ return findings;
560
+ },
561
+ };
562
+ const originRequiredFiles = {
563
+ id: "bg3.story.origin-required-files",
564
+ game: "baldursgate3",
565
+ groups: ["story"],
566
+ modTypes: ["origin"],
567
+ summary: "A project defining an origin ships the Origins.lsx entry, a CC preset template and a world-character file for it.",
568
+ appliesTo: fileTarget(isOriginsLsx),
569
+ appliesToDescription: "the project's Origins.lsx",
570
+ needs: { project: true },
571
+ cost: "edit",
572
+ severity: "block",
573
+ evidence: ["bg3.origins.companions.crash-without-worldcharacter-equivalent-file"],
574
+ async run(ctx) {
575
+ if (ctx.target.kind !== "file")
576
+ return [];
577
+ const text = await ctx.readText(ctx.target.path);
578
+ const origins = collectOrigins(parseLsx(text, ctx.target.path));
579
+ if (origins.length === 0)
580
+ return [];
581
+ const allFiles = await projectFiles(ctx.project, () => true);
582
+ const findings = [];
583
+ for (const origin of origins) {
584
+ if (!origin.name)
585
+ continue;
586
+ const ccPreset = allFiles.some((f) => lower(f).endsWith(`${origin.name.toLowerCase()}_ccpreset.lsx`));
587
+ const worldCharacter = allFiles.some((f) => lower(f).endsWith(`${origin.name.toLowerCase()}_worldcharacter.lsx`));
588
+ if (!ccPreset) {
589
+ findings.push(finding(originRequiredFiles.id, "block", ctx.target.path, `Origin '${origin.name}' has no matching CC preset template (<Name>_CCPreset.lsx) anywhere in this project.`, originRequiredFiles.evidence));
590
+ }
591
+ if (!worldCharacter) {
592
+ findings.push(finding(originRequiredFiles.id, "block", ctx.target.path, `Origin '${origin.name}' has no matching world-character file (<Name>_WorldCharacter.lsx) anywhere in this project. The custom-origin tutorial reports a crash when this file was omitted, even though its exact necessity was not fully understood by the tutorial's author.`, originRequiredFiles.evidence));
593
+ }
594
+ }
595
+ return findings;
596
+ },
597
+ };
598
+ const originTemplatePlaceholderLeft = {
599
+ id: "bg3.story.origin-template-placeholder-left",
600
+ game: "baldursgate3",
601
+ groups: ["story"],
602
+ modTypes: ["origin"],
603
+ summary: "A UUID still carrying the custom-origin template's 'Regenerate this!' marker comment is present.",
604
+ appliesTo: fileTarget((p) => p.endsWith(".lsx") && (isOriginsLsx(p) || p.includes("/origins/"))),
605
+ appliesToDescription: "Origins.lsx and other LSX files under an Origins/ directory",
606
+ needs: { project: true },
607
+ cost: "edit",
608
+ severity: "warn",
609
+ evidence: ["bg3.origins.companions.regenerate-uuid-find-replace-warning"],
610
+ async run(ctx) {
611
+ if (ctx.target.kind !== "file")
612
+ return [];
613
+ const text = await ctx.readText(ctx.target.path);
614
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
615
+ const findings = [];
616
+ for (let i = 0; i < lines.length; i++) {
617
+ if (!/Regenerate this!/i.test(lines[i] ?? ""))
618
+ continue;
619
+ const window = lines.slice(i, Math.min(lines.length, i + 3)).join("\n");
620
+ const uuid = /value="([0-9a-fA-F-]{36})"/.exec(window);
621
+ if (uuid) {
622
+ findings.push(finding(originTemplatePlaceholderLeft.id, "warn", ctx.target.path, `Placeholder UUID ${uuid[1]} still carries the custom-origin template's "Regenerate this!" marker. Replace every occurrence of this UUID across the whole template.`, originTemplatePlaceholderLeft.evidence, { line: i + 1 }));
623
+ }
624
+ }
625
+ return findings;
626
+ },
627
+ };
628
+ function parseDialogLsj(json) {
629
+ const obj = json;
630
+ const save = (obj?.save ?? {});
631
+ const v = save.version;
632
+ const version = v
633
+ ? {
634
+ major: Number(v.major ?? 0),
635
+ minor: Number(v.minor ?? 0),
636
+ revision: Number(v.revision ?? 0),
637
+ build: Number(v.build ?? 0),
638
+ }
639
+ : undefined;
640
+ const regions = (save.regions ?? {});
641
+ return { version, hasEditorData: Object.prototype.hasOwnProperty.call(regions, "editorData") };
642
+ }
643
+ const EDITOR_VERSION = { major: 4, minor: 5, revision: 0, build: 500 };
644
+ function sameVersion(a, b) {
645
+ return a.major === b.major && a.minor === b.minor && a.revision === b.revision && a.build === b.build;
646
+ }
647
+ const dialogueSourceVersion = {
648
+ id: "bg3.story.dialogue-source-version",
649
+ game: "baldursgate3",
650
+ groups: ["story"],
651
+ modTypes: ["dialogue"],
652
+ summary: "A shipped dialogue override is binary-derived (save version 4.6.0.1, no top-level editorData region), never the editor .lsj.",
653
+ appliesTo: fileTarget(isDialogLsj),
654
+ appliesToDescription: "a .lsj file under a Story/Dialog(s) path — the divine-converted intermediate of a DialogsBinary .lsf (no toolchain hook exists in ValidationContext, so this reads that intermediate directly rather than invoking divine)",
655
+ needs: { project: true, toolchain: ["divine"] },
656
+ cost: "build",
657
+ severity: "block",
658
+ evidence: ["bg3.dialogue.binary-source-not-editor", "bg3.dialogue.override-packing-rule-was-wrong"],
659
+ async run(ctx) {
660
+ if (ctx.target.kind !== "file")
661
+ return [];
662
+ let parsed;
663
+ try {
664
+ parsed = parseDialogLsj(JSON.parse(await ctx.readText(ctx.target.path)));
665
+ }
666
+ catch {
667
+ return [];
668
+ }
669
+ const findings = [];
670
+ if (parsed.hasEditorData) {
671
+ findings.push(finding(dialogueSourceVersion.id, "block", ctx.target.path, `This dialogue file carries a top-level 'editorData' region, which only the editor-exported .lsj has. ` +
672
+ `Never patch the shipped editor .lsj under Mods/<ns>/Story/Dialogs/ — extract the vanilla DialogsBinary .lsf, ` +
673
+ `convert lsf->lsj, patch that, then convert back.`, dialogueSourceVersion.evidence));
674
+ }
675
+ if (parsed.version && sameVersion(parsed.version, EDITOR_VERSION)) {
676
+ findings.push(finding(dialogueSourceVersion.id, "block", ctx.target.path, `This dialogue file's save version is ${EDITOR_VERSION.major}.${EDITOR_VERSION.minor}.${EDITOR_VERSION.revision}.${EDITOR_VERSION.build}, ` +
677
+ `the editor's version, not the binary's 4.6.0.1. Divine bakes this version into the converted .lsf, which is the root cause behind ` +
678
+ `the original (now-corrected) "dialog overrides cannot be packed" belief.`, dialogueSourceVersion.evidence));
679
+ }
680
+ return findings;
681
+ },
682
+ };
683
+ function collectBankResources(doc) {
684
+ const out = [];
685
+ for (const region of doc.regions) {
686
+ walkLsxNodes(region.root, (node) => {
687
+ if (node.id !== "Resource")
688
+ return;
689
+ out.push({
690
+ id: node.attributes.ID?.value,
691
+ name: node.attributes.Name?.value,
692
+ sourceFile: node.attributes.SourceFile?.value,
693
+ speakerSlots: node.attributes.SpeakerSlotsWithLines?.value,
694
+ flags: node.attributes.Flags?.value,
695
+ });
696
+ });
697
+ }
698
+ return out;
699
+ }
700
+ const dialogbankSourcefileRepoint = {
701
+ id: "bg3.story.dialogbank-sourcefile-repoint",
702
+ game: "baldursgate3",
703
+ groups: ["story"],
704
+ modTypes: ["dialogue"],
705
+ summary: "In the namespaced DialogBank pattern, only SourceFile is re-pointed; ID/Name/Flags/SpeakerSlotsWithLines keep the vanilla values.",
706
+ appliesTo: fileTarget(isDialogBankLsx),
707
+ appliesToDescription: "a DialogBank descriptor LSX (a divine-converted _merged.lsx twin) whose Resource nodes re-point SourceFile into this project's Story/Dialogs tree",
708
+ needs: { project: true, index: true, toolchain: ["divine"] },
709
+ cost: "build",
710
+ severity: "warn",
711
+ evidence: ["bg3.dialogue.dialogbank-namespaced-pattern"],
712
+ async run(ctx) {
713
+ if (ctx.target.kind !== "file")
714
+ return [];
715
+ const text = await ctx.readText(ctx.target.path);
716
+ let resources;
717
+ try {
718
+ resources = collectBankResources(parseLsx(text, ctx.target.path));
719
+ }
720
+ catch {
721
+ return [];
722
+ }
723
+ const findings = [];
724
+ for (const res of resources) {
725
+ if (!res.sourceFile || !res.id)
726
+ continue;
727
+ if (!lower(res.sourceFile).includes("/story/dialogs/"))
728
+ continue;
729
+ const vanilla = await ctx.lookup("guid", { uuid: res.id });
730
+ if (vanilla.count === 0)
731
+ continue;
732
+ const row = vanilla.rows[0];
733
+ const vanillaName = row.name ?? undefined;
734
+ if (vanillaName && res.name && vanillaName !== res.name) {
735
+ findings.push(finding(dialogbankSourcefileRepoint.id, "warn", ctx.target.path, `Resource ${res.id} has Name '${res.name}' but the vanilla entry is named '${vanillaName}'. Only SourceFile should be re-pointed; Name should be a byte-for-byte copy.`, dialogbankSourcefileRepoint.evidence));
736
+ }
737
+ }
738
+ return findings;
739
+ },
740
+ };
741
+ const dialogueFlagGuidResolves = {
742
+ id: "bg3.story.dialogue-flag-guid-resolves",
743
+ game: "baldursgate3",
744
+ groups: ["story"],
745
+ modTypes: ["dialogue"],
746
+ summary: "Flag GUIDs referenced from dialogue nodes resolve to a Public/*/Flags/<guid>.lsf definition in the mod or vanilla.",
747
+ appliesTo: fileTarget(isDialogLsj),
748
+ appliesToDescription: "a dialogue .lsj that references flags by 'Name_GUID' and this project ships its own Public/*/Flags/ tree",
749
+ needs: { project: true, index: true, toolchain: ["divine"] },
750
+ cost: "build",
751
+ severity: "warn",
752
+ evidence: ["bg3.dialogue.flags-guid-definitions"],
753
+ async run(ctx) {
754
+ if (ctx.target.kind !== "file")
755
+ return [];
756
+ const flagFiles = await projectFiles(ctx.project, (p) => p.includes("/flags/") && (p.endsWith(".lsf") || p.endsWith(".lsx")));
757
+ if (flagFiles.length === 0)
758
+ return [];
759
+ const definedGuids = new Set(flagFiles.map((f) => path.posix.basename(lower(f), path.extname(f)).toLowerCase()));
760
+ const text = await ctx.readText(ctx.target.path);
761
+ const nameGuidRe = /"Name"\s*:\s*"[^"]*_([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})"/g;
762
+ const findings = [];
763
+ let m;
764
+ while ((m = nameGuidRe.exec(text))) {
765
+ const guid = m[1].toLowerCase();
766
+ if (definedGuids.has(guid))
767
+ continue;
768
+ const vanilla = await ctx.lookup("guid", { uuid: guid });
769
+ if (vanilla.count > 0)
770
+ continue;
771
+ findings.push(finding(dialogueFlagGuidResolves.id, "warn", ctx.target.path, `Flag GUID ${guid} referenced here has no matching Public/*/Flags/${guid}.lsf in this project's own Flags/ tree.`, dialogueFlagGuidResolves.evidence));
772
+ }
773
+ return findings;
774
+ },
775
+ };
776
+ export const STORY_VALIDATORS = [
777
+ osirisSymbolDefined,
778
+ osirisNamingConvention,
779
+ osirisSectionStructure,
780
+ goalParentCompletes,
781
+ questUpdateParentId,
782
+ questEntryExists,
783
+ questAddQuestCloseDiscouraged,
784
+ originsTagsDefined,
785
+ originRequiredFiles,
786
+ originTemplatePlaceholderLeft,
787
+ dialogueSourceVersion,
788
+ dialogbankSourcefileRepoint,
789
+ dialogueFlagGuidResolves,
790
+ ];