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,171 @@
1
+ import { promises as fs } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { readPeFileVersion } from "../../core/compat/pe.js";
4
+ import { parseVersion } from "../../core/compat/version.js";
5
+ import { pathExists, readTextIfExists } from "../../core/fsutil.js";
6
+ async function exeKind(root) {
7
+ return (await pathExists(path.join(root, "valheim_server.exe"))) ? "server" : "client";
8
+ }
9
+ export function doorstopIniGeneration(iniText) {
10
+ const sections = new Set();
11
+ for (const raw of iniText.split(/\r?\n/)) {
12
+ const m = /^\s*\[(.+?)\]\s*$/.exec(raw);
13
+ if (m)
14
+ sections.add(m[1].trim().toLowerCase());
15
+ }
16
+ if (sections.has("unitydoorstop"))
17
+ return "legacy";
18
+ if (sections.has("general"))
19
+ return "modern";
20
+ return "unknown";
21
+ }
22
+ async function peVersionOf(file) {
23
+ if (!(await pathExists(file)))
24
+ return undefined;
25
+ try {
26
+ return await readPeFileVersion(file);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ async function findJotunn(pluginsDir) {
33
+ const direct = path.join(pluginsDir, "Jotunn.dll");
34
+ if (await pathExists(direct))
35
+ return direct;
36
+ let entries;
37
+ try {
38
+ entries = await fs.readdir(pluginsDir);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ for (const entry of entries.sort()) {
44
+ const candidate = path.join(pluginsDir, entry, "Jotunn.dll");
45
+ if (await pathExists(candidate))
46
+ return candidate;
47
+ }
48
+ return undefined;
49
+ }
50
+ export async function detectComponents(install) {
51
+ const root = install.root;
52
+ const kind = await exeKind(root);
53
+ const exe = path.join(root, kind === "server" ? "valheim_server.exe" : "valheim.exe");
54
+ const exeExists = await pathExists(exe);
55
+ const gameVersion = install.version ?? (await peVersionOf(exe));
56
+ const components = [];
57
+ components.push({
58
+ id: "game",
59
+ label: kind === "server" ? "Valheim dedicated server" : "Valheim",
60
+ kind: "game",
61
+ installed: exeExists,
62
+ ...(gameVersion !== undefined ? { version: gameVersion } : {}),
63
+ source: install.version ? "install record" : `${path.basename(exe)} PE file version (the Unity player version, not Iron Gate's patch number)`,
64
+ path: exe,
65
+ });
66
+ const bepinexDir = path.join(root, "BepInEx");
67
+ const bepinexDll = path.join(bepinexDir, "core", "BepInEx.dll");
68
+ const bepinexVersion = await peVersionOf(bepinexDll);
69
+ const bepinexInstalled = await pathExists(bepinexDll);
70
+ components.push({ id: "bepinex", label: "BepInEx 5", kind: "loader", installed: bepinexInstalled, ...(bepinexVersion !== undefined ? { version: bepinexVersion } : {}), source: "BepInEx/core/BepInEx.dll PE file version", path: bepinexDll });
71
+ const packMarkers = ["start_game_bepinex.sh", "start_server_bepinex.sh", "doorstop_libs"].map((m) => path.join(root, m));
72
+ let packMarker;
73
+ for (const m of packMarkers)
74
+ if (await pathExists(m)) {
75
+ packMarker = m;
76
+ break;
77
+ }
78
+ components.push({
79
+ id: "bepinexpack",
80
+ label: "BepInExPack_Valheim",
81
+ kind: "loader",
82
+ installed: packMarker !== undefined,
83
+ source: packMarker ? `${path.basename(packMarker)} beside the executable (the pack's own file; its version is not on disk)` : "the pack's run scripts or doorstop_libs folder, not found",
84
+ ...(packMarker ? { path: packMarker } : {}),
85
+ });
86
+ components.push({ id: "bepinexpack-missing", label: "BepInEx without the Valheim pack", kind: "loader", installed: bepinexInstalled && packMarker === undefined, source: "BepInEx.dll present, no pack marker" });
87
+ const doorstopVersionFile = path.join(root, ".doorstop_version");
88
+ const doorstopText = await readTextIfExists(doorstopVersionFile);
89
+ const doorstopVersion = doorstopText?.trim().split(/\s+/)[0];
90
+ const ini = await readTextIfExists(path.join(root, "doorstop_config.ini"));
91
+ const doorstopInstalled = (await pathExists(path.join(root, "winhttp.dll"))) || doorstopText !== undefined || ini !== undefined;
92
+ components.push({
93
+ id: "doorstop",
94
+ label: "UnityDoorstop",
95
+ kind: "loader",
96
+ installed: doorstopInstalled,
97
+ ...(doorstopVersion && parseVersion(doorstopVersion) ? { version: doorstopVersion } : {}),
98
+ source: ".doorstop_version beside the executable",
99
+ path: doorstopVersionFile,
100
+ });
101
+ const generation = ini === undefined ? "unknown" : doorstopIniGeneration(ini);
102
+ components.push({ id: "doorstop-ini-legacy", label: "doorstop_config.ini in the [UnityDoorstop] shape (BepInEx <= 5.4.22)", kind: "loader", installed: generation === "legacy", source: "doorstop_config.ini section names" });
103
+ components.push({ id: "doorstop-ini-modern", label: "doorstop_config.ini in the [General]/[UnityMono] shape (BepInEx >= 5.4.23)", kind: "loader", installed: generation === "modern", source: "doorstop_config.ini section names" });
104
+ const harmonyDll = path.join(bepinexDir, "core", "0Harmony.dll");
105
+ const harmonyVersion = await peVersionOf(harmonyDll);
106
+ components.push({ id: "harmony", label: "HarmonyX", kind: "framework", installed: await pathExists(harmonyDll), ...(harmonyVersion !== undefined ? { version: harmonyVersion } : {}), source: "BepInEx/core/0Harmony.dll PE file version", path: harmonyDll });
107
+ const jotunnDll = await findJotunn(path.join(bepinexDir, "plugins"));
108
+ const jotunnVersion = jotunnDll ? await peVersionOf(jotunnDll) : undefined;
109
+ components.push({ id: "jotunn", label: "Jötunn", kind: "framework", installed: jotunnDll !== undefined, ...(jotunnVersion !== undefined ? { version: jotunnVersion } : {}), source: "BepInEx/plugins/**/Jotunn.dll PE file version", ...(jotunnDll ? { path: jotunnDll } : {}) });
110
+ return components;
111
+ }
112
+ const BEPINEX_5_MAX = "5.99.99.99";
113
+ const DOORSTOP_4 = "4.0.0";
114
+ export const RULES = [
115
+ {
116
+ id: "vh-bepinex-5-only",
117
+ game: "valheim",
118
+ component: "bepinex",
119
+ requires: [{ component: "bepinex", max: BEPINEX_5_MAX }],
120
+ undeterminedSeverity: "info",
121
+ note: "Valheim plugins are BepInEx 5 assemblies; BepInEx 6's own release notes say BepInEx 5 plugins will not work with it and tell BepInEx 5 users to stay on 5.",
122
+ source: "https://github.com/BepInEx/BepInEx/releases/tag/v6.0.0-pre.2",
123
+ },
124
+ {
125
+ id: "vh-bepinex-without-pack",
126
+ game: "valheim",
127
+ component: "bepinexpack-missing",
128
+ requires: [],
129
+ advisory: true,
130
+ severity: "warn",
131
+ note: "BepInEx is installed without the Valheim pack's files: Jötunn and most Valheim mods depend on denikson's BepInExPack_Valheim, which differs from stock (SemVer plugin versions accepted, console on, isModded forced true). Stock or outdated BepInEx is the wiki's first client failure mode.",
132
+ source: "https://thunderstore.io/c/valheim/p/denikson/BepInExPack_Valheim/; https://github.com/Valheim-Modding/Wiki/wiki/Client-Troubleshooting",
133
+ },
134
+ {
135
+ id: "vh-jotunn-needs-pack",
136
+ game: "valheim",
137
+ component: "jotunn",
138
+ requires: [{ component: "bepinexpack-missing" }],
139
+ advisory: true,
140
+ severity: "block",
141
+ note: "Jötunn is installed on a BepInEx that is not the Valheim pack; Jötunn's manifest depends on denikson-BepInExPack_Valheim and its guide says a GitHub BepInEx release may not work properly.",
142
+ source: "https://valheim-modding.github.io/Jotunn/guides/guide.html; Jötunn's Thunderstore manifest",
143
+ },
144
+ {
145
+ id: "vh-doorstop-ini-legacy-under-doorstop-4",
146
+ game: "valheim",
147
+ component: "doorstop-ini-legacy",
148
+ requires: [{ component: "doorstop", max: "3.99.99" }],
149
+ note: "doorstop_config.ini is in the [UnityDoorstop] shape while the installed Doorstop is 4.x (BepInEx 5.4.23+ renamed every key); the loader reads nothing and the game starts unmodded.",
150
+ source: "https://github.com/BepInEx/BepInEx/releases/tag/v5.4.23.2",
151
+ },
152
+ {
153
+ id: "vh-doorstop-ini-modern-under-doorstop-3",
154
+ game: "valheim",
155
+ component: "doorstop-ini-modern",
156
+ requires: [{ component: "doorstop", min: DOORSTOP_4 }],
157
+ note: "doorstop_config.ini is in the [General]/[UnityMono] shape while the installed Doorstop is older than 4; the pre-5.4.23 loader reads [UnityDoorstop] and loads nothing.",
158
+ source: "https://github.com/BepInEx/BepInEx/releases/tag/v5.4.23.2",
159
+ },
160
+ {
161
+ id: "vh-game-update-invalidates-plugins",
162
+ game: "valheim",
163
+ component: "game",
164
+ requires: [],
165
+ advisory: true,
166
+ severity: "info",
167
+ note: "A Valheim update invalidates compiled plugins (Valheim 1.0 broke most of them); the MissingMethodException a stale plugin throws is logged at Debug or to Player.log, so LogOutput.log can look clean while the game is broken.",
168
+ source: "BepInEx v5-lts TypeLoader.cs and Chainloader.cs",
169
+ },
170
+ ];
171
+ export const valheimCompat = { detectComponents, rules: RULES };
@@ -0,0 +1,301 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { defaultCacheDir, indexFile, IndexStore } from "../../../core/knowledge/store.js";
3
+ import { resolveDataDir } from "../../../core/userconfig.js";
4
+ import { cellName, findJotunnDataDir, listJotunnDataFiles, parseJotunnMarkdown, plainText } from "./tables.js";
5
+ export const JOTUNN_DATA_ENV = "MODWRIGHT_VALHEIM_JOTUNN_DATA";
6
+ export const JOTUNN_DATA_ID = "valheim.jotunn";
7
+ const GAME = "valheim";
8
+ export function createSchema(store) {
9
+ store.exec(`
10
+ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
11
+ CREATE TABLE IF NOT EXISTS source (
12
+ id INTEGER PRIMARY KEY,
13
+ kind TEXT NOT NULL,
14
+ path TEXT NOT NULL,
15
+ mtime TEXT
16
+ );
17
+ CREATE TABLE IF NOT EXISTS jotunn_table (
18
+ id INTEGER PRIMARY KEY,
19
+ kind TEXT NOT NULL,
20
+ file TEXT NOT NULL,
21
+ game_version TEXT,
22
+ section TEXT,
23
+ columns_json TEXT NOT NULL,
24
+ row_count INTEGER NOT NULL
25
+ );
26
+ CREATE TABLE IF NOT EXISTS entry (
27
+ id INTEGER PRIMARY KEY,
28
+ kind TEXT NOT NULL,
29
+ name TEXT NOT NULL COLLATE NOCASE,
30
+ asset_id TEXT COLLATE NOCASE,
31
+ token TEXT COLLATE NOCASE,
32
+ english_name TEXT COLLATE NOCASE,
33
+ section TEXT,
34
+ table_id INTEGER NOT NULL REFERENCES jotunn_table(id),
35
+ data_json TEXT NOT NULL
36
+ );
37
+ CREATE INDEX IF NOT EXISTS idx_entry_kind_name ON entry(kind, name);
38
+ CREATE INDEX IF NOT EXISTS idx_entry_asset ON entry(asset_id);
39
+ CREATE INDEX IF NOT EXISTS idx_entry_token ON entry(token);
40
+ CREATE INDEX IF NOT EXISTS idx_entry_english ON entry(english_name);
41
+ `);
42
+ }
43
+ export function isBuilt(store) {
44
+ const row = store.get("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'entry'");
45
+ return row !== undefined && store.get("SELECT COUNT(*) AS n FROM entry")?.n > 0;
46
+ }
47
+ function columnIndex(columns, ...names) {
48
+ const lower = columns.map((c) => c.toLowerCase());
49
+ for (const n of names) {
50
+ const i = lower.indexOf(n.toLowerCase());
51
+ if (i >= 0)
52
+ return i;
53
+ }
54
+ return -1;
55
+ }
56
+ export async function importJotunnFile(store, dataFile) {
57
+ const text = await fs.readFile(dataFile.file, "utf8");
58
+ const parsed = parseJotunnMarkdown(text);
59
+ const stat = await fs.stat(dataFile.file);
60
+ store.run("INSERT INTO source (kind, path, mtime) VALUES (?, ?, ?)", ["jotunn", dataFile.relative, stat.mtime.toISOString()]);
61
+ let rows = 0;
62
+ for (const table of parsed.tables) {
63
+ store.run("INSERT INTO jotunn_table (kind, file, game_version, section, columns_json, row_count) VALUES (?, ?, ?, ?, ?, ?)", [
64
+ dataFile.kind,
65
+ dataFile.relative,
66
+ parsed.gameVersion ?? null,
67
+ table.section ?? null,
68
+ JSON.stringify(table.columns),
69
+ table.rows.length,
70
+ ]);
71
+ const tableId = store.get("SELECT last_insert_rowid() AS id")?.id;
72
+ const assetCol = columnIndex(table.columns, "AssetID", "Asset ID");
73
+ const tokenCol = columnIndex(table.columns, "Token");
74
+ const englishCol = columnIndex(table.columns, "English Name");
75
+ const cell = (r, i) => (i >= 0 ? plainText(r[i] ?? "") || null : null);
76
+ const batch = [];
77
+ for (const r of table.rows) {
78
+ const name = cellName(r[0] ?? "");
79
+ if (name.length === 0)
80
+ continue;
81
+ const data = {};
82
+ table.columns.forEach((c, i) => {
83
+ data[c] = plainText(r[i] ?? "");
84
+ });
85
+ batch.push([dataFile.kind, name, cell(r, assetCol), cell(r, tokenCol), cell(r, englishCol), table.section ?? null, tableId, JSON.stringify(data)]);
86
+ }
87
+ if (batch.length > 0)
88
+ store.insertMany("entry", ["kind", "name", "asset_id", "token", "english_name", "section", "table_id", "data_json"], batch);
89
+ rows += batch.length;
90
+ }
91
+ return { file: dataFile.relative, kind: dataFile.kind, ...(parsed.gameVersion !== undefined ? { gameVersion: parsed.gameVersion } : {}), tables: parsed.tables.length, rows };
92
+ }
93
+ function summarize(store, sources) {
94
+ const file = indexFile(defaultCacheDir(), GAME);
95
+ const builtAt = store.get("SELECT value FROM meta WHERE key = 'builtAt'")?.value;
96
+ const gameVersion = store.get("SELECT value FROM meta WHERE key = 'gameVersion'")?.value;
97
+ return {
98
+ game: GAME,
99
+ file,
100
+ exists: true,
101
+ ...(builtAt !== undefined ? { builtAt: String(builtAt) } : {}),
102
+ ...(gameVersion !== undefined ? { gameVersion: String(gameVersion) } : {}),
103
+ tables: store.tableCounts(),
104
+ sources,
105
+ };
106
+ }
107
+ export async function buildIndex(_install, store, options = {}) {
108
+ const sources = [];
109
+ createSchema(store);
110
+ if (isBuilt(store) && !options.force) {
111
+ sources.push("(Jötunn index already built — pass { force: true } to rebuild)");
112
+ return summarize(store, sources);
113
+ }
114
+ store.exec("DELETE FROM entry; DELETE FROM jotunn_table; DELETE FROM source; DELETE FROM meta;");
115
+ const requested = options.dataDir ?? (await resolveDataDir(JOTUNN_DATA_ID, JOTUNN_DATA_ENV))?.path;
116
+ if (!requested) {
117
+ sources.push(`no Jötunn data: set ${JOTUNN_DATA_ENV}, or remember the folder once with check_toolchain action=remember data=${JOTUNN_DATA_ID} dataPath=<folder>, pointing at a Valheim-Modding/Jotunn checkout (or its JotunnLib/Documentation/data folder); nothing was indexed.`);
118
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('builtAt', ?)", [new Date().toISOString()]);
119
+ return summarize(store, sources);
120
+ }
121
+ const dataDir = await findJotunnDataDir(requested);
122
+ if (!dataDir) {
123
+ sources.push(`${requested} is not a Jötunn checkout or data directory (no prefabs/ or objects/ folder found under it or JotunnLib/Documentation/data); nothing was indexed.`);
124
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('builtAt', ?)", [new Date().toISOString()]);
125
+ return summarize(store, sources);
126
+ }
127
+ let files = await listJotunnDataFiles(dataDir);
128
+ if (options.only && options.only.length > 0) {
129
+ const wanted = new Set(options.only.map((k) => k.toLowerCase().replace(/-/g, "_")));
130
+ files = files.filter((f) => wanted.has(f.kind));
131
+ }
132
+ const versions = new Map();
133
+ const errors = [];
134
+ let index = 0;
135
+ for (const f of files) {
136
+ options.progress?.({ step: `jotunn:${f.kind}`, done: index++, total: files.length, note: f.relative });
137
+ try {
138
+ const summary = await importJotunnFile(store, f);
139
+ sources.push(`${f.relative} (${summary.kind}, Valheim ${summary.gameVersion ?? "version not stated"}, ${summary.rows} rows in ${summary.tables} table${summary.tables === 1 ? "" : "s"})`);
140
+ if (summary.gameVersion)
141
+ versions.set(f.kind, summary.gameVersion);
142
+ }
143
+ catch (err) {
144
+ errors.push(`${f.relative}: ${err.message}`);
145
+ }
146
+ }
147
+ const distinct = new Set(versions.values());
148
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('builtAt', ?)", [new Date().toISOString()]);
149
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('dataDir', ?)", [dataDir]);
150
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('gameVersions', ?)", [JSON.stringify(Object.fromEntries(versions))]);
151
+ if (distinct.size === 1) {
152
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('gameVersion', ?)", [[...distinct][0]]);
153
+ }
154
+ else if (distinct.size > 1) {
155
+ sources.push(`the dumps state ${distinct.size} different game versions (${[...distinct].sort().join(", ")}); no single index version is recorded, each table carries its own.`);
156
+ }
157
+ if (errors.length > 0) {
158
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('buildErrors', ?)", [JSON.stringify(errors.slice(0, 200))]);
159
+ sources.push(`${errors.length} file(s) failed to parse — see meta.buildErrors.`);
160
+ }
161
+ if (files.length === 0)
162
+ sources.push(`${dataDir}: no *-list.md files found.`);
163
+ return summarize(store, sources);
164
+ }
165
+ export async function info(_install) {
166
+ const file = indexFile(defaultCacheDir(), GAME);
167
+ const result = { game: GAME, file, exists: false };
168
+ let stat;
169
+ try {
170
+ stat = await fs.stat(file);
171
+ }
172
+ catch {
173
+ return result;
174
+ }
175
+ result.exists = true;
176
+ result.sizeBytes = stat.size;
177
+ let store;
178
+ try {
179
+ store = await IndexStore.open(file);
180
+ const builtAt = store.get("SELECT value FROM meta WHERE key = 'builtAt'")?.value;
181
+ const gameVersion = store.get("SELECT value FROM meta WHERE key = 'gameVersion'")?.value;
182
+ if (builtAt !== undefined)
183
+ result.builtAt = String(builtAt);
184
+ if (gameVersion !== undefined)
185
+ result.gameVersion = String(gameVersion);
186
+ result.tables = store.tableCounts();
187
+ const rows = store.all("SELECT kind, file, game_version, row_count FROM jotunn_table ORDER BY id");
188
+ if (rows.length > 0)
189
+ result.sources = rows.map((r) => `${r.file} (${r.kind}, Valheim ${r.game_version ?? "version not stated"}, ${r.row_count} rows)`);
190
+ }
191
+ catch {
192
+ }
193
+ finally {
194
+ store?.close();
195
+ }
196
+ return result;
197
+ }
198
+ const DEFAULT_LIMIT = 50;
199
+ function str(v) {
200
+ if (v === undefined)
201
+ return undefined;
202
+ const s = String(v).trim();
203
+ return s.length > 0 ? s : undefined;
204
+ }
205
+ function limitOf(params) {
206
+ const n = Number(params.limit);
207
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_LIMIT;
208
+ }
209
+ export function queryKinds() {
210
+ return [
211
+ { kind: "kinds", description: "The JotunnDoc tables in the index, each with its stated Valheim version and row count.", params: [] },
212
+ { kind: "entry", description: "Objects of one kind (prefab, item, piece, recipe, status_effect, location, vegetation, rpc, character, material, mesh, shader, sprite, piece_table, ...) by exact name, or by `contains` substring; every column of the dump comes back in `data`.", params: ["kind", "name", "contains", "limit"] },
213
+ { kind: "prefab", description: "A prefab by name (exact, case-insensitive) or `contains`: its AssetID, localization token, English name and components.", params: ["name", "contains", "limit"] },
214
+ { kind: "item", description: "An item by name or `contains`: AssetID, token, English name, item type, description.", params: ["name", "contains", "limit"] },
215
+ { kind: "piece", description: "A buildable piece by name or `contains`, with the piece table it belongs to.", params: ["name", "contains", "limit"] },
216
+ { kind: "recipe", description: "A recipe by name or `contains`: the item it makes, amount, resources.", params: ["name", "contains", "limit"] },
217
+ { kind: "asset", description: "Whatever carries a given AssetID, across every kind (Iron Gate's stable key across renames); the mesh list's `Asset ID` column counts.", params: ["id", "limit"] },
218
+ { kind: "token", description: "Whatever carries a given localization token ($item_..., $piece_...), across every kind, or the English name it resolves to.", params: ["token", "english", "limit"] },
219
+ ];
220
+ }
221
+ function rowsOut(rows) {
222
+ return rows.map((r) => {
223
+ const { data_json, ...rest } = r;
224
+ return { ...rest, data: typeof data_json === "string" ? JSON.parse(data_json) : undefined };
225
+ });
226
+ }
227
+ const ENTRY_COLUMNS = "e.kind, e.name, e.asset_id AS assetId, e.token, e.english_name AS englishName, e.section, t.game_version AS gameVersion, t.file, e.data_json";
228
+ function likeEscape(text) {
229
+ return text.replace(/[\\%_]/g, (c) => `\\${c}`);
230
+ }
231
+ function byName(store, kind, params) {
232
+ const limit = limitOf(params);
233
+ const name = str(params.name);
234
+ const contains = str(params.contains);
235
+ const where = [];
236
+ const args = [];
237
+ if (kind) {
238
+ where.push("e.kind = ?");
239
+ args.push(kind);
240
+ }
241
+ if (name) {
242
+ where.push("e.name = ? COLLATE NOCASE");
243
+ args.push(name);
244
+ }
245
+ else if (contains) {
246
+ where.push("e.name LIKE ? ESCAPE '\\'");
247
+ args.push(`%${likeEscape(contains)}%`);
248
+ }
249
+ const sql = `SELECT ${ENTRY_COLUMNS} FROM entry e JOIN jotunn_table t ON t.id = e.table_id${where.length ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY e.name LIMIT ?`;
250
+ const rows = store.all(sql, [...args, limit + 1]);
251
+ const truncated = rows.length > limit;
252
+ const out = rowsOut(rows.slice(0, limit));
253
+ const what = kind ?? "any kind";
254
+ const note = name ? `${what} named "${name}" (exact, case-insensitive)` : contains ? `${what} whose name contains "${contains}"` : `every ${what} entry, first ${limit}`;
255
+ return { kind: kind ?? "entry", params, count: out.length, ...(truncated ? { truncated: true } : {}), rows: out, note: `${note}; each row's gameVersion is the version its JotunnDoc dump states.` };
256
+ }
257
+ export async function query(store, kind, params) {
258
+ if (!isBuilt(store)) {
259
+ return { kind, params, count: 0, rows: [], note: `No Valheim index has been built: run build_index once a Jötunn checkout is set (${JOTUNN_DATA_ENV}) or remembered (check_toolchain action=remember data=${JOTUNN_DATA_ID}).` };
260
+ }
261
+ switch (kind) {
262
+ case "kinds": {
263
+ const rows = store.all("SELECT kind, file, game_version AS gameVersion, section, row_count AS rows FROM jotunn_table ORDER BY kind, id");
264
+ return { kind, params, count: rows.length, rows, note: "One row per table imported from the JotunnDoc dumps, with the Valheim version each file states." };
265
+ }
266
+ case "entry": {
267
+ const k = str(params.kind);
268
+ return byName(store, k, params);
269
+ }
270
+ case "prefab":
271
+ case "item":
272
+ case "piece":
273
+ case "recipe":
274
+ return byName(store, kind, params);
275
+ case "asset": {
276
+ const id = str(params.id);
277
+ if (!id)
278
+ return { kind, params, count: 0, rows: [], note: "asset needs `id`, a 32-hex AssetID." };
279
+ const limit = limitOf(params);
280
+ const rows = store.all(`SELECT ${ENTRY_COLUMNS} FROM entry e JOIN jotunn_table t ON t.id = e.table_id WHERE e.asset_id = ? COLLATE NOCASE ORDER BY e.kind, e.name LIMIT ?`, [id, limit + 1]);
281
+ const truncated = rows.length > limit;
282
+ return { kind, params, count: Math.min(rows.length, limit), ...(truncated ? { truncated: true } : {}), rows: rowsOut(rows.slice(0, limit)), note: `entries of every kind carrying AssetID ${id}.` };
283
+ }
284
+ case "token": {
285
+ const token = str(params.token);
286
+ const english = str(params.english);
287
+ const limit = limitOf(params);
288
+ if (!token && !english)
289
+ return { kind, params, count: 0, rows: [], note: "token needs `token` (e.g. $item_sword_bronze) or `english` (the displayed name)." };
290
+ const rows = token
291
+ ? store.all(`SELECT ${ENTRY_COLUMNS} FROM entry e JOIN jotunn_table t ON t.id = e.table_id WHERE e.token = ? COLLATE NOCASE ORDER BY e.kind, e.name LIMIT ?`, [token, limit + 1])
292
+ : store.all(`SELECT ${ENTRY_COLUMNS} FROM entry e JOIN jotunn_table t ON t.id = e.table_id WHERE e.english_name = ? COLLATE NOCASE ORDER BY e.kind, e.name LIMIT ?`, [english, limit + 1]);
293
+ const truncated = rows.length > limit;
294
+ return { kind, params, count: Math.min(rows.length, limit), ...(truncated ? { truncated: true } : {}), rows: rowsOut(rows.slice(0, limit)), note: token ? `entries whose localization token is ${token}.` : `entries whose English name is "${english}".` };
295
+ }
296
+ default:
297
+ return { kind, params, count: 0, rows: [], note: `Unknown kind "${kind}"; call kind "kinds" or see queryKinds().` };
298
+ }
299
+ }
300
+ export const valheimIndex = { info, build: buildIndex, queryKinds, query };
301
+ export { parseJotunnMarkdown, plainText, cellName, kindOfFile, findJotunnDataDir, listJotunnDataFiles } from "./tables.js";
@@ -0,0 +1,103 @@
1
+ import { promises as fs } from "node:fs";
2
+ import * as path from "node:path";
3
+ const VERSION_RE = /generated from Valheim (\S+?) using/i;
4
+ function splitRow(line) {
5
+ let body = line.trim();
6
+ if (body.startsWith("|"))
7
+ body = body.slice(1);
8
+ if (body.endsWith("|"))
9
+ body = body.slice(0, -1);
10
+ return body.split("|").map((c) => c.trim());
11
+ }
12
+ const isSeparator = (cells) => cells.length > 0 && cells.every((c) => /^:?-{3,}:?$/.test(c));
13
+ export function parseJotunnMarkdown(text) {
14
+ const lines = text.split(/\r?\n/);
15
+ const out = { tables: [] };
16
+ let section;
17
+ let i = 0;
18
+ while (i < lines.length) {
19
+ const line = lines[i];
20
+ if (out.gameVersion === undefined) {
21
+ const m = VERSION_RE.exec(line);
22
+ if (m)
23
+ out.gameVersion = m[1];
24
+ }
25
+ if (/^#\s+/.test(line) && out.title === undefined)
26
+ out.title = line.replace(/^#\s+/, "").trim();
27
+ const heading = /^##\s+(.+)$/.exec(line);
28
+ if (heading) {
29
+ section = heading[1].trim();
30
+ i++;
31
+ continue;
32
+ }
33
+ if (line.trim().startsWith("|") && i + 1 < lines.length && isSeparator(splitRow(lines[i + 1]))) {
34
+ const columns = splitRow(line);
35
+ const table = { ...(section !== undefined ? { section } : {}), columns, rows: [], line: i + 1 };
36
+ i += 2;
37
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
38
+ const cells = splitRow(lines[i]);
39
+ if (cells.length > columns.length) {
40
+ const head = cells.slice(0, columns.length - 1);
41
+ head.push(cells.slice(columns.length - 1).join("|"));
42
+ table.rows.push(head);
43
+ }
44
+ else {
45
+ while (cells.length < columns.length)
46
+ cells.push("");
47
+ table.rows.push(cells);
48
+ }
49
+ i++;
50
+ }
51
+ out.tables.push(table);
52
+ continue;
53
+ }
54
+ i++;
55
+ }
56
+ return out;
57
+ }
58
+ export function plainText(cell) {
59
+ return cell
60
+ .replace(/<\/(?:li|dd|dt)>\s*<(?:li|dd|dt)\b[^>]*>/g, "; ")
61
+ .replace(/<br\s*\/?>/gi, " ")
62
+ .replace(/<[^>]*>/g, "")
63
+ .replace(/\s+/g, " ")
64
+ .trim();
65
+ }
66
+ export function cellName(cell) {
67
+ const tag = cell.indexOf("<");
68
+ const head = tag > 0 ? plainText(cell.slice(0, tag)) : "";
69
+ return head.length > 0 ? head : plainText(cell);
70
+ }
71
+ export function kindOfFile(fileName) {
72
+ const m = /^([a-z0-9-]+)-list\.md$/i.exec(fileName);
73
+ return m ? m[1].toLowerCase().replace(/-/g, "_") : undefined;
74
+ }
75
+ export async function findJotunnDataDir(dir) {
76
+ for (const candidate of [dir, path.join(dir, "JotunnLib", "Documentation", "data"), path.join(dir, "Documentation", "data"), path.join(dir, "data")]) {
77
+ try {
78
+ const entries = await fs.readdir(candidate, { withFileTypes: true });
79
+ if (entries.some((e) => e.isDirectory() && (e.name === "prefabs" || e.name === "objects")))
80
+ return candidate;
81
+ }
82
+ catch {
83
+ }
84
+ }
85
+ return undefined;
86
+ }
87
+ export async function listJotunnDataFiles(dataDir) {
88
+ const out = [];
89
+ const visit = async (dir, rel) => {
90
+ const entries = await fs.readdir(dir, { withFileTypes: true });
91
+ for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
92
+ if (e.isDirectory()) {
93
+ await visit(path.join(dir, e.name), [...rel, e.name]);
94
+ continue;
95
+ }
96
+ const kind = kindOfFile(e.name);
97
+ if (kind)
98
+ out.push({ kind, file: path.join(dir, e.name), relative: [...rel, e.name].join("/") });
99
+ }
100
+ };
101
+ await visit(dataDir, []);
102
+ return out;
103
+ }
@@ -0,0 +1,11 @@
1
+ export { valheim, installKind, diskLoggingDisabled } from "./surface.js";
2
+ export { valheimCompat, doorstopIniGeneration } from "./compat.js";
3
+ export { valheimIndex, buildIndex as buildValheimIndex, JOTUNN_DATA_ENV, JOTUNN_DATA_ID } from "./index/index.js";
4
+ export { parseJotunnMarkdown, kindOfFile, findJotunnDataDir, listJotunnDataFiles } from "./index/tables.js";
5
+ export { parseThunderstoreManifest, parseDependencyString, readPngDimensions, routeOf, checkThunderstoreToml, PACKAGE_ROUTES } from "./packaging.js";
6
+ export { scanPluginSource, scanCfg, packageNameMatchesGuid, BEPINPLUGIN_GUID_RE } from "./plugins.js";
7
+ export { PACKAGING_VALIDATORS, GAME_ASSEMBLY_RE } from "./validators/packaging.js";
8
+ export { PLUGIN_VALIDATORS } from "./validators/plugins.js";
9
+ export { REFERENCE_VALIDATORS } from "./validators/references.js";
10
+ export { scanReferences, translationKeys } from "./references.js";
11
+ export { SIGNATURES } from "./logs.js";