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,7 @@
1
+ import { vanillaExtractNeverShipValidator } from "../../../core/text/validate.js";
2
+ export const vanillaExtractNeverShipsValidator = vanillaExtractNeverShipValidator({
3
+ id: "bg3.history.vanilla-extract-never-ships",
4
+ game: "baldursgate3",
5
+ evidence: ["ModWright research"],
6
+ });
7
+ export const TEMPLATE_VALIDATORS = [vanillaExtractNeverShipsValidator];
@@ -0,0 +1,223 @@
1
+ import * as path from "node:path";
2
+ import { readPeFileVersion } from "../../core/compat/pe.js";
3
+ import { head, pathExists, readTextIfExists, resolveCandidate } from "../../core/fsutil.js";
4
+ import { red4extLogCandidates } from "./paths.js";
5
+ const RED4EXT_INIT_RE = /RED4ext\s*\(v([\d.]+)\)\s+is initializing/i;
6
+ const RED4EXT_PRODUCT_VERSION_RE = /Product version:\s*([\d.]+)/i;
7
+ function pluginLoadedPattern(name) {
8
+ return new RegExp(`${name}\\s*\\(version:\\s*([\\d.]+).*\\)\\s*has been loaded`, "i");
9
+ }
10
+ const RED4EXT_PLUGINS = [
11
+ { id: "archivexl", label: "ArchiveXL", dir: "ArchiveXL" },
12
+ { id: "tweakxl", label: "TweakXL", dir: "TweakXL" },
13
+ { id: "codeware", label: "Codeware", dir: "Codeware" },
14
+ ];
15
+ async function detectComponents(install) {
16
+ const at = (...segments) => path.join(install.root, ...segments);
17
+ const components = [];
18
+ const red4extLogHit = await resolveCandidate(red4extLogCandidates(install));
19
+ const red4extLog = red4extLogHit ? await readTextIfExists(red4extLogHit.path) : undefined;
20
+ const gameExe = at("bin", "x64", "Cyberpunk2077.exe");
21
+ const gameInstalled = await pathExists(gameExe);
22
+ let gameVersion = await readPeFileVersion(gameExe);
23
+ let gameSource = gameVersion ? "bin/x64/Cyberpunk2077.exe (PE FileVersion)" : undefined;
24
+ const productVersionMatch = red4extLog?.match(RED4EXT_PRODUCT_VERSION_RE);
25
+ const marketingVersion = productVersionMatch?.[1];
26
+ if (!gameVersion && marketingVersion) {
27
+ gameVersion = marketingVersion;
28
+ gameSource = 'red4ext/logs/red4ext.log ("Product version:" header line)';
29
+ }
30
+ components.push({
31
+ id: "game",
32
+ label: "Cyberpunk 2077",
33
+ kind: "game",
34
+ installed: gameInstalled || gameVersion !== undefined,
35
+ version: gameVersion,
36
+ marketingVersion,
37
+ source: gameSource,
38
+ path: gameExe,
39
+ });
40
+ const red4extDll = at("red4ext", "RED4ext.dll");
41
+ const red4extInstalled = await pathExists(red4extDll);
42
+ let red4extVersion = await readPeFileVersion(red4extDll);
43
+ let red4extSource = red4extVersion ? "red4ext/RED4ext.dll (PE FileVersion)" : undefined;
44
+ if (!red4extVersion) {
45
+ const match = red4extLog?.match(RED4EXT_INIT_RE);
46
+ if (match?.[1]) {
47
+ red4extVersion = match[1];
48
+ red4extSource = 'red4ext/logs/red4ext.log ("is initializing" header line)';
49
+ }
50
+ }
51
+ components.push({
52
+ id: "red4ext",
53
+ label: "RED4ext",
54
+ kind: "loader",
55
+ installed: red4extInstalled || red4extVersion !== undefined,
56
+ version: red4extVersion,
57
+ source: red4extSource,
58
+ path: red4extDll,
59
+ });
60
+ for (const plugin of RED4EXT_PLUGINS) {
61
+ const dllPath = at("red4ext", "plugins", plugin.dir, `${plugin.dir}.dll`);
62
+ const dllExists = await pathExists(dllPath);
63
+ let version = await readPeFileVersion(dllPath);
64
+ let source = version
65
+ ? `red4ext/plugins/${plugin.dir}/${plugin.dir}.dll (PE FileVersion)`
66
+ : undefined;
67
+ if (!version) {
68
+ const match = red4extLog?.match(pluginLoadedPattern(plugin.dir));
69
+ if (match?.[1]) {
70
+ version = match[1];
71
+ source = 'red4ext/logs/red4ext.log ("has been loaded" line)';
72
+ }
73
+ }
74
+ components.push({
75
+ id: plugin.id,
76
+ label: plugin.label,
77
+ kind: "framework",
78
+ installed: dllExists || version !== undefined,
79
+ version,
80
+ source,
81
+ path: dllPath,
82
+ });
83
+ }
84
+ const sccExe = at("engine", "tools", "scc.exe");
85
+ const sccLib = at("engine", "tools", "scc_lib.dll");
86
+ const sccExeExists = await pathExists(sccExe);
87
+ const sccLibExists = await pathExists(sccLib);
88
+ let redscriptVersion = sccExeExists ? await readPeFileVersion(sccExe) : undefined;
89
+ let redscriptSource = redscriptVersion
90
+ ? "engine/tools/scc.exe (PE FileVersion, candidate)"
91
+ : undefined;
92
+ if (!redscriptVersion && sccLibExists) {
93
+ redscriptVersion = await readPeFileVersion(sccLib);
94
+ if (redscriptVersion)
95
+ redscriptSource = "engine/tools/scc_lib.dll (PE FileVersion, candidate)";
96
+ }
97
+ const redscriptLog = at("r6", "logs", "redscript_rCURRENT.log");
98
+ const redscriptLogExists = await pathExists(redscriptLog);
99
+ if (!redscriptVersion && redscriptLogExists) {
100
+ const banner = /Running REDscript (\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.]+)?)/.exec(await head(redscriptLog));
101
+ if (banner) {
102
+ redscriptVersion = banner[1];
103
+ redscriptSource = 'r6/logs/redscript_rCURRENT.log ("Running REDscript" banner)';
104
+ }
105
+ }
106
+ components.push({
107
+ id: "redscript",
108
+ label: "redscript",
109
+ kind: "tool",
110
+ installed: sccExeExists || sccLibExists || redscriptLogExists,
111
+ version: redscriptVersion,
112
+ source: redscriptSource,
113
+ path: sccExeExists ? sccExe : sccLibExists ? sccLib : redscriptLogExists ? redscriptLog : sccExe,
114
+ });
115
+ const cetAsi = at("bin", "x64", "plugins", "cyber_engine_tweaks.asi");
116
+ const cetLogPath = at("bin", "x64", "plugins", "cyber_engine_tweaks", "cyber_engine_tweaks.log");
117
+ const cetInstalled = await pathExists(cetAsi);
118
+ let cetVersion = await readPeFileVersion(cetAsi);
119
+ let cetSource = cetVersion
120
+ ? "bin/x64/plugins/cyber_engine_tweaks.asi (PE FileVersion)"
121
+ : undefined;
122
+ if (!cetVersion) {
123
+ const cetLog = await readTextIfExists(cetLogPath);
124
+ const firstLine = cetLog?.split(/\r?\n/, 1)[0];
125
+ const match = firstLine?.match(/(\d+\.\d+(?:\.\d+)*)/);
126
+ if (match?.[1]) {
127
+ cetVersion = match[1];
128
+ cetSource = "bin/x64/plugins/cyber_engine_tweaks/cyber_engine_tweaks.log (first line, heuristic — unverified format)";
129
+ }
130
+ }
131
+ components.push({
132
+ id: "cet",
133
+ label: "Cyber Engine Tweaks",
134
+ kind: "framework",
135
+ installed: cetInstalled,
136
+ version: cetVersion,
137
+ source: cetSource,
138
+ path: cetAsi,
139
+ });
140
+ const redHotToolsDir = at("red4ext", "plugins", "RedHotTools");
141
+ const redHotToolsDll = path.join(redHotToolsDir, "RedHotTools.dll");
142
+ const redHotToolsVersion = await readPeFileVersion(redHotToolsDll);
143
+ components.push({
144
+ id: "redhottools",
145
+ label: "Red Hot Tools",
146
+ kind: "framework",
147
+ installed: await pathExists(redHotToolsDir),
148
+ version: redHotToolsVersion,
149
+ source: redHotToolsVersion ? "red4ext/plugins/RedHotTools/RedHotTools.dll (PE FileVersion)" : undefined,
150
+ path: redHotToolsDir,
151
+ });
152
+ return components;
153
+ }
154
+ const RULES = [
155
+ {
156
+ id: "cp2077-redhottools-floor",
157
+ game: "cyberpunk2077",
158
+ component: "redhottools",
159
+ requires: [
160
+ { component: "game", min: "2.21" },
161
+ { component: "archivexl", min: "1.21.0" },
162
+ { component: "cet", min: "1.35.0" },
163
+ { component: "redscript", min: "0.5.25" },
164
+ { component: "red4ext", min: "1.27.0" },
165
+ ],
166
+ note: "Red Hot Tools needs at least these versions to hot reload archives, scripts and tweaks.",
167
+ source: "https://github.com/psiberx/cp2077-red-hot-tools (README, Compatibility)",
168
+ },
169
+ {
170
+ id: "cp2077-archivexl-floor",
171
+ game: "cyberpunk2077",
172
+ component: "archivexl",
173
+ requires: [
174
+ { component: "game", min: "2.31" },
175
+ { component: "redscript", min: "0.5.31" },
176
+ { component: "red4ext", min: "1.29.0" },
177
+ ],
178
+ note: "ArchiveXL states these as its minimum compatible versions.",
179
+ source: "cp2077-archive-xl README.md, Getting Started > Compatibility (mirrors psiberx/cp2077-archive-xl)",
180
+ },
181
+ {
182
+ id: "cp2077-tweakxl-floor",
183
+ game: "cyberpunk2077",
184
+ component: "tweakxl",
185
+ requires: [
186
+ { component: "game", min: "2.3" },
187
+ { component: "redscript", min: "0.5.27" },
188
+ { component: "red4ext", min: "1.28.0" },
189
+ ],
190
+ note: "TweakXL states these as its minimum compatible versions.",
191
+ source: "https://github.com/psiberx/cp2077-tweak-xl (README, Compatibility)",
192
+ },
193
+ {
194
+ id: "cp2077-codeware-floor",
195
+ game: "cyberpunk2077",
196
+ component: "codeware",
197
+ requires: [
198
+ { component: "game", min: "2.31" },
199
+ { component: "redscript", min: "0.5.31" },
200
+ { component: "cet", min: "1.37.0" },
201
+ { component: "red4ext", min: "1.29.0" },
202
+ ],
203
+ note: "Codeware states these as its minimum compatible versions.",
204
+ source: "https://github.com/psiberx/cp2077-codeware (README, Compatibility and Installation)",
205
+ },
206
+ {
207
+ id: "cp2077-red4ext-locked-to-build",
208
+ game: "cyberpunk2077",
209
+ component: "red4ext",
210
+ requires: [],
211
+ advisory: true,
212
+ severity: "info",
213
+ note: "RED4ext plugins are locked to the game build. After a game patch, wait for RED4ext and its " +
214
+ "plugins to publish updated releases before assuming a mod is broken.",
215
+ source: "https://wiki.redmodding.org/cyberpunk-2077-modding/for-mod-users/user-guide-troubleshooting " +
216
+ "(\"prone to breaking with every update\"); RED4ext itself refuses to run against a game patch " +
217
+ "it does not recognize (WopsS/RED4ext#33).",
218
+ },
219
+ ];
220
+ export const cyberpunk2077Compat = {
221
+ detectComponents,
222
+ rules: RULES,
223
+ };
@@ -0,0 +1,231 @@
1
+ import { promises as fs } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { pathExists, walk } from "../../../core/fsutil.js";
4
+ import { parseTweakFile } from "./parsers/tweak.js";
5
+ import { parseTweakXLYaml } from "./parsers/tweakxl-yaml.js";
6
+ import { buildResourceListing } from "./resource.js";
7
+ import { createSchema, isBuilt, TWEAKDB_PARSE_VERSION, tweakdbParseCurrent } from "./schema.js";
8
+ const TWEAK_EXT = [".tweak"];
9
+ const YAML_EXT = [".yaml", ".yml"];
10
+ function derivePackage(name) {
11
+ const dot = name.indexOf(".");
12
+ return dot === -1 ? null : name.slice(0, dot);
13
+ }
14
+ function loadIdState(store) {
15
+ const recordId = Number(store.get("SELECT COALESCE(MAX(id), 0) AS m FROM record")?.m ?? 0);
16
+ const flatId = Number(store.get("SELECT COALESCE(MAX(id), 0) AS m FROM flat")?.m ?? 0);
17
+ const sourceId = Number(store.get("SELECT COALESCE(MAX(id), 0) AS m FROM source")?.m ?? 0);
18
+ return { recordId, flatId, sourceId };
19
+ }
20
+ async function readDirectoryFiles(dir, extensions, errors) {
21
+ const walked = await walk(dir, { limit: 20_000 });
22
+ const files = walked.files.filter((f) => extensions.some((ext) => f.toLowerCase().endsWith(ext)));
23
+ const out = [];
24
+ for (const rel of files) {
25
+ const abs = path.join(dir, rel);
26
+ try {
27
+ const text = await fs.readFile(abs, "utf8");
28
+ let mtime;
29
+ try {
30
+ mtime = (await fs.stat(abs)).mtime.toISOString();
31
+ }
32
+ catch {
33
+ mtime = undefined;
34
+ }
35
+ out.push({ abs, rel, text, mtime });
36
+ }
37
+ catch (err) {
38
+ errors.push(`${abs}: ${err instanceof Error ? err.message : String(err)}`);
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+ function parseFile(file) {
44
+ const isYaml = YAML_EXT.some((ext) => file.rel.toLowerCase().endsWith(ext));
45
+ return isYaml ? parseTweakXLYaml(file.text, file.abs) : parseTweakFile(file.text, file.abs);
46
+ }
47
+ function insertRecords(store, records, sourceId, ids) {
48
+ let recordRows = 0;
49
+ let flatRows = 0;
50
+ for (const rec of records) {
51
+ ids.recordId += 1;
52
+ const recordId = ids.recordId;
53
+ store.run("INSERT INTO record (id, name, package, base, type, source_id, file, line) VALUES (?,?,?,?,?,?,?,?)", [
54
+ recordId,
55
+ rec.name,
56
+ derivePackage(rec.name),
57
+ rec.base ?? null,
58
+ rec.type ?? null,
59
+ sourceId,
60
+ rec.file,
61
+ rec.line,
62
+ ]);
63
+ recordRows++;
64
+ const ftsParts = [];
65
+ if (rec.base)
66
+ ftsParts.push(rec.base);
67
+ if (rec.type)
68
+ ftsParts.push(rec.type);
69
+ for (const flat of rec.flats) {
70
+ ids.flatId += 1;
71
+ const valueJson = JSON.stringify(flat.value ?? null);
72
+ store.run("INSERT INTO flat (id, record_id, name, kind, value_json, op) VALUES (?,?,?,?,?,?)", [
73
+ ids.flatId,
74
+ recordId,
75
+ flat.name,
76
+ flat.kind,
77
+ valueJson,
78
+ flat.op,
79
+ ]);
80
+ flatRows++;
81
+ ftsParts.push(flat.name, valueJson);
82
+ }
83
+ store.run("INSERT INTO record_fts (docid, name, flats_text) VALUES (?,?,?)", [recordId, rec.name, ftsParts.join(" ")]);
84
+ }
85
+ return { records: recordRows, flats: flatRows };
86
+ }
87
+ function ingest(store, files, kind, ids, progress, step, errors) {
88
+ let recordTotal = 0;
89
+ let flatTotal = 0;
90
+ let done = 0;
91
+ for (const file of files) {
92
+ let records;
93
+ try {
94
+ records = parseFile(file);
95
+ }
96
+ catch (err) {
97
+ errors.push(err instanceof Error ? err.message : String(err));
98
+ done++;
99
+ continue;
100
+ }
101
+ ids.sourceId += 1;
102
+ const sourceId = ids.sourceId;
103
+ store.run("INSERT INTO source (id, kind, path, mtime) VALUES (?,?,?,?)", [sourceId, kind, file.abs, file.mtime ?? null]);
104
+ const { records: rCount, flats: fCount } = insertRecords(store, records, sourceId, ids);
105
+ recordTotal += rCount;
106
+ flatTotal += fCount;
107
+ done++;
108
+ if (done === files.length || done % 100 === 0) {
109
+ progress?.({ step, done, total: files.length });
110
+ }
111
+ }
112
+ return { records: recordTotal, flats: flatTotal };
113
+ }
114
+ function summarize(store, install, sources) {
115
+ const tables = store.tableCounts();
116
+ const builtAtRow = store.get("SELECT value FROM meta WHERE key = 'builtAt'");
117
+ return {
118
+ game: "cyberpunk2077",
119
+ file: store.file ?? "",
120
+ exists: true,
121
+ builtAt: builtAtRow ? String(builtAtRow.value) : undefined,
122
+ gameVersion: install?.version,
123
+ tables,
124
+ sources,
125
+ };
126
+ }
127
+ export async function buildIndex(install, store, options = {}) {
128
+ createSchema(store);
129
+ const sources = [];
130
+ const only = options.only;
131
+ const includesTweakDB = !only || only.includes("vanilla") || only.includes("mods");
132
+ const staleParse = includesTweakDB && isBuilt(store) && !tweakdbParseCurrent(store);
133
+ if (!includesTweakDB) {
134
+ sources.push(`(TweakDB tables kept — only: ${only?.join(", ")})`);
135
+ }
136
+ else if (!options.force && !staleParse && isBuilt(store)) {
137
+ sources.push("(TweakDB index already built — pass { force: true } to rebuild)");
138
+ }
139
+ else {
140
+ if (staleParse && !options.force) {
141
+ sources.push(`(TweakDB index built by an older parse — rebuilt at parse version ${TWEAKDB_PARSE_VERSION})`);
142
+ }
143
+ if (options.force) {
144
+ store.exec("DROP TABLE IF EXISTS flat; DROP TABLE IF EXISTS record_fts; DROP TABLE IF EXISTS record; DROP TABLE IF EXISTS source; DROP TABLE IF EXISTS meta;");
145
+ createSchema(store);
146
+ }
147
+ else if (staleParse) {
148
+ store.exec("DROP TABLE IF EXISTS flat; DROP TABLE IF EXISTS record_fts; DROP TABLE IF EXISTS record; DROP TABLE IF EXISTS source; " +
149
+ "DELETE FROM meta WHERE key NOT LIKE 'resource%';");
150
+ createSchema(store);
151
+ }
152
+ const includeVanilla = !only || only.includes("vanilla");
153
+ const includeMods = !!only && only.includes("mods");
154
+ const errors = [];
155
+ const ids = loadIdState(store);
156
+ if (includeVanilla) {
157
+ const vanillaDir = path.join(install.root, "tools", "redmod", "tweaks");
158
+ if (await pathExists(vanillaDir)) {
159
+ const files = await readDirectoryFiles(vanillaDir, TWEAK_EXT, errors);
160
+ options.progress?.({ step: "scan:vanilla", total: files.length, note: `${files.length} .tweak files under ${vanillaDir}` });
161
+ const { records, flats } = store.transaction(() => ingest(store, files, "vanilla", ids, options.progress, "parse:vanilla", errors));
162
+ sources.push(`${vanillaDir} (${files.length} files, ${records} records, ${flats} flats)`);
163
+ }
164
+ else {
165
+ sources.push(`${vanillaDir}: not found — REDmod tools are an optional install component ` +
166
+ "(installed via the game's REDmod feature or Steam/GOG verify); vanilla TweakDB records cannot be indexed without it.");
167
+ options.progress?.({ step: "scan:vanilla", note: sources[sources.length - 1] });
168
+ }
169
+ }
170
+ if (includeMods) {
171
+ const modsDir = path.join(install.root, "r6", "tweaks");
172
+ if (await pathExists(modsDir)) {
173
+ const files = await readDirectoryFiles(modsDir, YAML_EXT, errors);
174
+ options.progress?.({ step: "scan:mods", total: files.length, note: `${files.length} .yaml files under ${modsDir}` });
175
+ const { records, flats } = store.transaction(() => ingest(store, files, "mod", ids, options.progress, "parse:mods", errors));
176
+ sources.push(`${modsDir} (${files.length} files, ${records} records, ${flats} flats)`);
177
+ }
178
+ else {
179
+ sources.push(`${modsDir}: not found — no TweakXL mods installed.`);
180
+ }
181
+ }
182
+ const reclassified = store.transaction(() => {
183
+ const before = Number(store.get("SELECT COUNT(*) AS n FROM record WHERE base IS NOT NULL")?.n ?? 0);
184
+ store.run(`UPDATE record SET base = package || '.' || type, type = NULL
185
+ WHERE type IS NOT NULL AND package IS NOT NULL
186
+ AND EXISTS (SELECT 1 FROM record b WHERE b.name = record.package || '.' || record.type)`);
187
+ const after = Number(store.get("SELECT COUNT(*) AS n FROM record WHERE base IS NOT NULL")?.n ?? 0);
188
+ return after - before;
189
+ });
190
+ if (reclassified > 0)
191
+ sources.push(`${reclassified} bare ': Target' declarations resolved to same-package base records across files.`);
192
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('builtAt', ?)", [new Date().toISOString()]);
193
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('tweakdbParseVersion', ?)", [TWEAKDB_PARSE_VERSION]);
194
+ if (install.version)
195
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('gameVersion', ?)", [install.version]);
196
+ if (errors.length > 0) {
197
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('buildErrors', ?)", [JSON.stringify(errors.slice(0, 200))]);
198
+ sources.push(`${errors.length} file(s) failed to parse — see meta.buildErrors.`);
199
+ }
200
+ }
201
+ const includeResource = !only || only.includes("resource");
202
+ if (includeResource) {
203
+ const resourceOptions = {
204
+ force: options.force,
205
+ progress: options.progress,
206
+ toolOverrides: options.toolOverrides,
207
+ };
208
+ const cpOptions = options;
209
+ if (cpOptions.resourceToolLocator)
210
+ resourceOptions.resourceToolLocator = cpOptions.resourceToolLocator;
211
+ const resourceSummary = await buildResourceListing(install, store, resourceOptions);
212
+ sources.push(...resourceSummary.notes);
213
+ }
214
+ return summarize(store, install, sources);
215
+ }
216
+ export async function indexFromDirectory(dir, store, progress, kind = "vanilla") {
217
+ createSchema(store);
218
+ const errors = [];
219
+ const files = await readDirectoryFiles(dir, [...TWEAK_EXT, ...YAML_EXT], errors);
220
+ progress?.({ step: "scan", total: files.length, note: `${files.length} files under ${dir}` });
221
+ const ids = loadIdState(store);
222
+ const { records, flats } = store.transaction(() => ingest(store, files, kind, ids, progress, "parse", errors));
223
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('builtAt', ?)", [new Date().toISOString()]);
224
+ if (errors.length > 0) {
225
+ store.run("INSERT OR REPLACE INTO meta (key, value) VALUES ('buildErrors', ?)", [JSON.stringify(errors.slice(0, 200))]);
226
+ }
227
+ return summarize(store, undefined, [
228
+ `${dir} (${files.length} files, ${records} records, ${flats} flats)`,
229
+ ...(errors.length > 0 ? [`${errors.length} file(s) failed to parse — see meta.buildErrors.`] : []),
230
+ ]);
231
+ }
@@ -0,0 +1,17 @@
1
+ import { buildIndex } from "./build.js";
2
+ import { info } from "./info.js";
3
+ import { query, queryKinds } from "./queries.js";
4
+ export const cp2077Index = {
5
+ info,
6
+ build: buildIndex,
7
+ queryKinds,
8
+ query,
9
+ };
10
+ export { buildIndex, indexFromDirectory } from "./build.js";
11
+ export { info } from "./info.js";
12
+ export { query, queryKinds } from "./queries.js";
13
+ export { createSchema, isBuilt, ensureResourceSchema, RESOURCE_SCHEMA_VERSION } from "./schema.js";
14
+ export { parseTweakFile } from "./parsers/tweak.js";
15
+ export { parseTweakXLYaml } from "./parsers/tweakxl-yaml.js";
16
+ export { overlayFlats, TweakParseError } from "./model.js";
17
+ export { buildResourceListing } from "./resource.js";
@@ -0,0 +1,51 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { defaultCacheDir, indexFile, IndexStore } from "../../../core/knowledge/store.js";
3
+ import { TWEAKDB_PARSE_VERSION, tweakdbParseCurrent } from "./schema.js";
4
+ export async function info(_install) {
5
+ const file = indexFile(defaultCacheDir(), "cyberpunk2077");
6
+ const result = { game: "cyberpunk2077", file, exists: false };
7
+ let stat;
8
+ try {
9
+ stat = await fs.stat(file);
10
+ }
11
+ catch {
12
+ return result;
13
+ }
14
+ result.exists = true;
15
+ result.sizeBytes = stat.size;
16
+ let store;
17
+ try {
18
+ store = await IndexStore.open(file);
19
+ const builtAt = store.get(`SELECT value FROM meta WHERE key = 'builtAt'`)?.value;
20
+ const gameVersion = store.get(`SELECT value FROM meta WHERE key = 'gameVersion'`)?.value;
21
+ if (builtAt !== undefined)
22
+ result.builtAt = String(builtAt);
23
+ if (gameVersion !== undefined)
24
+ result.gameVersion = String(gameVersion);
25
+ if (builtAt !== undefined && !tweakdbParseCurrent(store)) {
26
+ result.stale = `TweakDB tables built by an older parse (before version ${TWEAKDB_PARSE_VERSION}: inline records named differently from the game); build_index rebuilds them without force`;
27
+ }
28
+ result.tables = store.tableCounts();
29
+ const sourceRows = store.all(`SELECT kind, path FROM source ORDER BY id`);
30
+ const sources = sourceRows.map((r) => `${r.kind}: ${r.path}`);
31
+ const foldersScannedRaw = store.get(`SELECT value FROM meta WHERE key = 'resourceFoldersScanned'`)?.value;
32
+ if (foldersScannedRaw !== undefined) {
33
+ try {
34
+ const folders = JSON.parse(String(foldersScannedRaw));
35
+ for (const f of folders) {
36
+ sources.push(f.exists ? `resource folder: ${f.dir} (${f.archives} archive(s))` : `resource folder: ${f.dir} (not found)`);
37
+ }
38
+ }
39
+ catch {
40
+ }
41
+ }
42
+ if (sources.length > 0)
43
+ result.sources = sources;
44
+ }
45
+ catch {
46
+ }
47
+ finally {
48
+ store?.close();
49
+ }
50
+ return result;
51
+ }
@@ -0,0 +1,52 @@
1
+ export class TweakParseError extends Error {
2
+ file;
3
+ line;
4
+ constructor(message, file, line) {
5
+ super(`${file}:${line}: ${message}`);
6
+ this.file = file;
7
+ this.line = line;
8
+ this.name = "TweakParseError";
9
+ }
10
+ }
11
+ export function overlayFlats(occurrences) {
12
+ const effective = new Map();
13
+ for (const occ of occurrences) {
14
+ for (const flat of occ.flats) {
15
+ const prior = effective.get(flat.name);
16
+ if (flat.op === "set") {
17
+ effective.set(flat.name, {
18
+ name: flat.name,
19
+ value: flat.value,
20
+ kind: flat.kind,
21
+ from: occ.recordLabel,
22
+ contributors: prior ? [...prior.contributors, occ.recordLabel] : [occ.recordLabel],
23
+ });
24
+ }
25
+ else if (flat.op === "append") {
26
+ const base = Array.isArray(prior?.value) ? prior.value : [];
27
+ const added = Array.isArray(flat.value) ? flat.value : [flat.value];
28
+ effective.set(flat.name, {
29
+ name: flat.name,
30
+ value: [...base, ...added],
31
+ kind: prior?.kind ?? flat.kind,
32
+ from: occ.recordLabel,
33
+ contributors: prior ? [...prior.contributors, occ.recordLabel] : [occ.recordLabel],
34
+ });
35
+ }
36
+ else {
37
+ const base = Array.isArray(prior?.value) ? prior.value : [];
38
+ const removed = Array.isArray(flat.value) ? flat.value : [flat.value];
39
+ const removedJson = removed.map((v) => JSON.stringify(v));
40
+ const kept = base.filter((v) => !removedJson.includes(JSON.stringify(v)));
41
+ effective.set(flat.name, {
42
+ name: flat.name,
43
+ value: kept,
44
+ kind: prior?.kind ?? flat.kind,
45
+ from: occ.recordLabel,
46
+ contributors: prior ? [...prior.contributors, occ.recordLabel] : [occ.recordLabel],
47
+ });
48
+ }
49
+ }
50
+ }
51
+ return effective;
52
+ }