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,49 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const execFileAsync = promisify(execFile);
4
+ export async function findRunningProcesses(names) {
5
+ const wanted = new Map(names.map((name) => [name.toLowerCase(), name]));
6
+ try {
7
+ if (process.platform === "win32") {
8
+ const { stdout } = await execFileAsync("tasklist", ["/FO", "CSV", "/NH"], {
9
+ windowsHide: true,
10
+ timeout: 10_000,
11
+ });
12
+ const running = [];
13
+ for (const line of stdout.split(/\r?\n/)) {
14
+ const match = line.match(/^"([^"]+)","(\d+)"/);
15
+ if (!match)
16
+ continue;
17
+ const name = wanted.get(match[1].toLowerCase());
18
+ if (name)
19
+ running.push({ name, pid: Number(match[2]) });
20
+ }
21
+ return { checked: true, running };
22
+ }
23
+ const { stdout } = await execFileAsync("ps", ["-A", "-o", "pid=,args="], { timeout: 10_000 });
24
+ const running = matchPsArgs(stdout, names);
25
+ return { checked: true, running };
26
+ }
27
+ catch {
28
+ return { checked: false, running: [] };
29
+ }
30
+ }
31
+ function escapeRegExp(text) {
32
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
33
+ }
34
+ export function matchPsArgs(stdout, names) {
35
+ const patterns = names.map((name) => {
36
+ const bare = name.replace(/\.exe$/i, "");
37
+ return { name, re: new RegExp(`(?:^|[/\\\\])${escapeRegExp(bare)}(?:\\.exe)?(?:\\s|$)`, "i") };
38
+ });
39
+ const running = [];
40
+ for (const line of stdout.split("\n")) {
41
+ const match = line.trim().match(/^(\d+)\s+(.+)$/);
42
+ if (!match)
43
+ continue;
44
+ const hit = patterns.find((p) => p.re.test(match[2]));
45
+ if (hit)
46
+ running.push({ name: hit.name, pid: Number(match[1]) });
47
+ }
48
+ return running;
49
+ }
@@ -0,0 +1,83 @@
1
+ function basenameOf(p) {
2
+ const parts = p.replace(/\\/g, "/").split("/").filter((part) => part.length > 0);
3
+ return parts[parts.length - 1] ?? p;
4
+ }
5
+ export function defaultArtifactName(project) {
6
+ const archiveSource = project.sources.find((s) => s.kind === "archive-staging");
7
+ if (archiveSource)
8
+ return basenameOf(archiveSource.path);
9
+ return project.name.replace(/[^A-Za-z0-9]+/g, "");
10
+ }
11
+ function defaultForTypedStepName(step, project) {
12
+ const name = defaultArtifactName(project);
13
+ switch (step) {
14
+ case "stage":
15
+ return { step: "stage", sources: project.sources.map((s) => s.id) };
16
+ case "assert-absent":
17
+ return { step: "assert-absent", patterns: [] };
18
+ case "assert-present":
19
+ return { step: "assert-present", paths: [] };
20
+ case "zip":
21
+ return { step: "zip", from: "${stage}", name: `${name}-\${version}.zip` };
22
+ case "wolvenkit-pack":
23
+ return { step: "wolvenkit-pack", from: "${stage}", out: `${name}.archive`, verbosity: "Minimal" };
24
+ case "divine-lsx-to-lsf":
25
+ return { step: "divine-lsx-to-lsf", in: "${stage}", glob: "**/*.lsx", deleteSource: false };
26
+ case "divine-loca":
27
+ return { step: "divine-loca", in: "${stage}", glob: "**/*.xml" };
28
+ case "divine-create-package":
29
+ return { step: "divine-create-package", from: "${stage}", out: `${name}.pak` };
30
+ case "blender-export":
31
+ case "wolvenkit-import":
32
+ case "verify-listing":
33
+ case "exec":
34
+ throw new Error(`build step "${step}" has no default parameters (it names a specific file or command this project's ` +
35
+ `file must supply) — write it out as a typed step object instead of a bare string.`);
36
+ default:
37
+ throw new Error(`Unknown build step "${step}". Known bare-string shorthands: "wolvenkit-pack", ` +
38
+ `"stage-mods-public-generated", "pack-pak", or any typed step's own name.`);
39
+ }
40
+ }
41
+ export function expandBareString(entry, project) {
42
+ const name = defaultArtifactName(project);
43
+ switch (entry) {
44
+ case "wolvenkit-pack":
45
+ return [
46
+ { step: "stage", id: "stage", sources: project.sources.map((s) => s.id) },
47
+ { step: "wolvenkit-pack", from: "${stage}", out: `${name}.archive`, verbosity: "Minimal" },
48
+ ];
49
+ case "pack-pak":
50
+ return [{ step: "divine-create-package", from: "${stage}", out: `${name}.pak` }];
51
+ case "stage-mods-public-generated":
52
+ return [{ step: "stage", id: "stage", sources: ["mods", "public", "generated"] }];
53
+ default:
54
+ return [defaultForTypedStepName(entry, project)];
55
+ }
56
+ }
57
+ function withStepDefaults(step) {
58
+ switch (step.step) {
59
+ case "wolvenkit-pack":
60
+ return { ...step, verbosity: step.verbosity ?? "Minimal" };
61
+ case "divine-lsx-to-lsf":
62
+ return { ...step, glob: step.glob ?? "**/*.lsx", deleteSource: step.deleteSource ?? false };
63
+ case "divine-loca":
64
+ return { ...step, glob: step.glob ?? "**/*.xml" };
65
+ case "exec":
66
+ return { ...step, args: step.args ?? [], expectOutputs: step.expectOutputs ?? [] };
67
+ default:
68
+ return step;
69
+ }
70
+ }
71
+ export function normalizeBuildSteps(project) {
72
+ const steps = project.build?.steps ?? [];
73
+ const out = [];
74
+ for (const entry of steps) {
75
+ if (typeof entry === "string") {
76
+ out.push(...expandBareString(entry, project));
77
+ }
78
+ else {
79
+ out.push(withStepDefaults(entry));
80
+ }
81
+ }
82
+ return out;
83
+ }
@@ -0,0 +1,74 @@
1
+ import { getSurface } from "../../surfaces/index.js";
2
+ import { normalizeBuildSteps } from "./build-steps.js";
3
+ export function describeProject(resolved) {
4
+ const { project } = resolved;
5
+ const warnings = [...resolved.warnings];
6
+ const surface = getSurface(project.game);
7
+ const game = surface ? { id: surface.id, name: surface.name } : { id: project.game, name: project.game };
8
+ const rootLabels = new Map();
9
+ if (surface) {
10
+ const placeholder = { gameId: surface.id, root: "<install>", source: "manual" };
11
+ for (const root of surface.modRoots(placeholder)) {
12
+ rootLabels.set(root.id, root.label);
13
+ }
14
+ }
15
+ const deploy = project.deploy
16
+ ? {
17
+ targets: project.deploy.targets.map((target) => {
18
+ const rootLabel = rootLabels.get(target.rootId);
19
+ if (surface && !rootLabel) {
20
+ warnings.push(`deploy target rootId "${target.rootId}" is not a known ModRoot on surface "${surface.id}"`);
21
+ }
22
+ return {
23
+ rootId: target.rootId,
24
+ rootLabel,
25
+ from: target.from,
26
+ into: target.into,
27
+ waitForGameExit: target.waitForGameExit,
28
+ backup: target.backup,
29
+ locked: target.locked,
30
+ mode: target.mode,
31
+ prune: target.prune,
32
+ variant: target.variant,
33
+ };
34
+ }),
35
+ junctions: project.deploy.junctions,
36
+ waitMinutes: project.deploy.waitMinutes,
37
+ }
38
+ : undefined;
39
+ let normalizedSteps;
40
+ try {
41
+ normalizedSteps = project.build?.steps ? normalizeBuildSteps(project).map((s) => `${s.id ?? s.step}${s.variant ? ` [variant: ${s.variant}]` : ""}`) : undefined;
42
+ }
43
+ catch {
44
+ normalizedSteps = undefined;
45
+ }
46
+ return {
47
+ name: project.name,
48
+ game,
49
+ version: project.version,
50
+ authors: project.authors,
51
+ identity: project.identity,
52
+ sources: resolved.sources,
53
+ build: resolved.build
54
+ ? {
55
+ staging: resolved.build.staging,
56
+ absStaging: resolved.build.absStaging,
57
+ outDir: resolved.build.outDir,
58
+ absOutDir: resolved.build.absOutDir,
59
+ outDirExists: resolved.build.outDirExists,
60
+ steps: resolved.build.steps,
61
+ normalizedSteps,
62
+ exclude: resolved.build.exclude,
63
+ variantNames: resolved.build.variants ? Object.keys(resolved.build.variants) : undefined,
64
+ }
65
+ : undefined,
66
+ deploy,
67
+ dependencies: project.dependencies,
68
+ publish: project.publish,
69
+ toolchain: project.toolchain,
70
+ testProfile: project.testProfile,
71
+ reload: project.reload,
72
+ warnings,
73
+ };
74
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./types.js";
2
+ export { modProjectSchema, parseProject } from "./schema.js";
3
+ export { findProjectFile, loadProject } from "./load.js";
4
+ export { describeProject } from "./describe.js";
5
+ export { defaultArtifactName, normalizeBuildSteps } from "./build-steps.js";
@@ -0,0 +1,146 @@
1
+ import { promises as fs } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { isDirectory, pathExists } from "../fsutil.js";
4
+ import { getSurface, surfaceIds } from "../../surfaces/index.js";
5
+ import { parseProject } from "./schema.js";
6
+ import { PROJECT_FILE } from "./types.js";
7
+ export async function findProjectFile(startDir) {
8
+ let dir = path.resolve(startDir);
9
+ for (;;) {
10
+ const candidate = path.join(dir, PROJECT_FILE);
11
+ if (await pathExists(candidate))
12
+ return candidate;
13
+ const parent = path.dirname(dir);
14
+ if (parent === dir)
15
+ return undefined;
16
+ dir = parent;
17
+ }
18
+ }
19
+ function looksAbsolute(p) {
20
+ return path.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p) || p.startsWith("\\\\");
21
+ }
22
+ function isWithin(parent, child) {
23
+ const rel = path.relative(parent, child);
24
+ return rel === "" || (!(rel === ".." || rel.startsWith(`..${path.sep}`)) && !path.isAbsolute(rel));
25
+ }
26
+ async function resolveProjectFile(fileOrDir) {
27
+ const abs = path.resolve(fileOrDir);
28
+ if (await isDirectory(abs)) {
29
+ const found = await findProjectFile(abs);
30
+ if (!found) {
31
+ throw new Error(`No ${PROJECT_FILE} found in ${abs} or any parent directory`);
32
+ }
33
+ return found;
34
+ }
35
+ if (!(await pathExists(abs))) {
36
+ throw new Error(`Project file not found: ${abs}`);
37
+ }
38
+ return abs;
39
+ }
40
+ export async function loadProject(fileOrDir) {
41
+ const file = await resolveProjectFile(fileOrDir);
42
+ const root = path.dirname(file);
43
+ const text = await fs.readFile(file, "utf8");
44
+ let raw;
45
+ try {
46
+ raw = JSON.parse(text);
47
+ }
48
+ catch (err) {
49
+ throw new Error(`${file}: invalid JSON: ${err.message}`);
50
+ }
51
+ const project = parseProject(raw);
52
+ const surface = getSurface(project.game);
53
+ if (!surface) {
54
+ throw new Error(`Unknown game "${project.game}" in ${file}. Known: ${surfaceIds().join(", ")}`);
55
+ }
56
+ const warnings = [];
57
+ const resolveFromRoot = (p) => (path.isAbsolute(p) ? p : path.join(root, p));
58
+ const seenSourceIds = new Set();
59
+ const sources = await Promise.all(project.sources.map(async (source) => {
60
+ if (seenSourceIds.has(source.id)) {
61
+ warnings.push(`duplicate source id "${source.id}"`);
62
+ }
63
+ seenSourceIds.add(source.id);
64
+ if (looksAbsolute(source.path)) {
65
+ warnings.push(`source "${source.id}" uses an absolute path (${source.path}); portable projects should use paths relative to ${PROJECT_FILE}`);
66
+ }
67
+ const absPath = resolveFromRoot(source.path);
68
+ const exists = await pathExists(absPath);
69
+ if (!exists) {
70
+ warnings.push(`source "${source.id}" does not exist: ${absPath}`);
71
+ }
72
+ return { ...source, absPath, exists };
73
+ }));
74
+ let build;
75
+ if (project.build) {
76
+ if (looksAbsolute(project.build.outDir)) {
77
+ warnings.push(`build.outDir is an absolute path (${project.build.outDir}); portable projects should use a path relative to ${PROJECT_FILE}`);
78
+ }
79
+ if (project.build.staging && looksAbsolute(project.build.staging)) {
80
+ warnings.push(`build.staging is an absolute path (${project.build.staging}); portable projects should use a path relative to ${PROJECT_FILE}`);
81
+ }
82
+ const absOutDir = resolveFromRoot(project.build.outDir);
83
+ const outDirExists = await pathExists(absOutDir);
84
+ const absStaging = project.build.staging ? resolveFromRoot(project.build.staging) : undefined;
85
+ build = { ...project.build, absOutDir, outDirExists, absStaging };
86
+ for (const source of sources) {
87
+ if (isWithin(source.absPath, absOutDir)) {
88
+ warnings.push(`build.outDir (${absOutDir}) is inside source "${source.id}" (${source.absPath})`);
89
+ }
90
+ }
91
+ if (absStaging) {
92
+ if (absStaging === root) {
93
+ warnings.push(`build.staging is the project root — packing from there would sweep up the whole repository (the "never pack the repo root" trap)`);
94
+ }
95
+ for (const source of sources) {
96
+ if (absStaging === source.absPath) {
97
+ warnings.push(`build.staging equals source "${source.id}"'s path (${absStaging}) — packing from there would sweep up the whole source tree instead of a clean staged copy`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ if (project.deploy) {
103
+ const sourceIdSet = new Set(sources.map((s) => s.id));
104
+ for (const target of project.deploy.targets) {
105
+ const isSourceId = sourceIdSet.has(target.from);
106
+ let isUnderOutDir = false;
107
+ if (build) {
108
+ const absFrom = path.isAbsolute(target.from) ? target.from : path.join(build.absOutDir, target.from);
109
+ isUnderOutDir = isWithin(build.absOutDir, absFrom);
110
+ }
111
+ const isModWrightOwned = !looksAbsolute(target.from) && /^\.modwright[\\/]/.test(target.from);
112
+ if (!isSourceId && !isUnderOutDir && !isModWrightOwned) {
113
+ warnings.push(`deploy target for root "${target.rootId}": from "${target.from}" is neither a source id nor a path under build.outDir` +
114
+ (build ? "" : " (project has no build.outDir configured)"));
115
+ }
116
+ }
117
+ }
118
+ const escapes = (p) => p !== undefined && (looksAbsolute(p) || p.split(/[\\/]/).includes(".."));
119
+ for (const target of project.deploy?.targets ?? []) {
120
+ if (escapes(target.into)) {
121
+ warnings.push(`deploy target "${target.from}": into "${target.into}" leaves the "${target.rootId}" root (absolute or "..")`);
122
+ }
123
+ }
124
+ for (const junction of project.deploy?.junctions ?? []) {
125
+ if (escapes(junction.linkPath)) {
126
+ warnings.push(`junction linkPath "${junction.linkPath}" leaves the "${junction.rootId}" root (absolute or "..")`);
127
+ }
128
+ }
129
+ for (const step of project.build?.steps ?? []) {
130
+ if (typeof step !== "object" || step === null)
131
+ continue;
132
+ const s = step;
133
+ if ((s.step === "wolvenkit-pack" || s.step === "divine-create-package") && escapes(s.out)) {
134
+ warnings.push(`build step ${s.step}: out "${s.out}" leaves build.outDir (absolute or "..")`);
135
+ }
136
+ if (s.step === "zip" && escapes(s.name))
137
+ warnings.push(`build step zip: name "${s.name}" leaves build.outDir (absolute or "..")`);
138
+ if (s.step === "exec" && s.cwd !== undefined && !s.cwd.includes("${stage}") && escapes(s.cwd)) {
139
+ warnings.push(`build step exec: cwd "${s.cwd}" leaves the project (absolute or "..")`);
140
+ }
141
+ }
142
+ const toolchain = project.toolchain
143
+ ? Object.fromEntries(Object.entries(project.toolchain).map(([id, p]) => [id, looksAbsolute(p) ? p : resolveFromRoot(p)]))
144
+ : undefined;
145
+ return { file, root, project: toolchain ? { ...project, toolchain } : project, sources, build, warnings };
146
+ }
@@ -0,0 +1,264 @@
1
+ import { z } from "zod";
2
+ import { SUPPORTED_PROJECT_SCHEMA_VERSIONS } from "./types.js";
3
+ import { bridgeTierSchema } from "../bridge/types.js";
4
+ const projectSourceSchema = z
5
+ .object({
6
+ id: z.string().min(1),
7
+ path: z.string().min(1),
8
+ kind: z.string().min(1),
9
+ notes: z.string().optional(),
10
+ })
11
+ .strict();
12
+ const dependencyKindSchema = z.enum(["loader", "framework", "tool", "mod"]);
13
+ const projectDependencySchema = z
14
+ .object({
15
+ id: z.string().min(1),
16
+ kind: dependencyKindSchema,
17
+ minVersion: z.string().optional(),
18
+ url: z.string().optional(),
19
+ notes: z.string().optional(),
20
+ })
21
+ .strict();
22
+ const stepBase = {
23
+ id: z.string().min(1).optional(),
24
+ label: z.string().optional(),
25
+ optional: z.boolean().optional(),
26
+ variant: z.string().min(1).optional(),
27
+ };
28
+ const stageSourceEntrySchema = z
29
+ .object({
30
+ source: z.string().min(1).optional(),
31
+ output: z.string().min(1).optional(),
32
+ into: z.string().optional(),
33
+ include: z.array(z.string().min(1)).optional(),
34
+ exclude: z.array(z.string().min(1)).optional(),
35
+ variant: z.string().optional(),
36
+ })
37
+ .strict()
38
+ .refine((entry) => (entry.source === undefined) !== (entry.output === undefined), {
39
+ message: 'a stage source entry needs exactly one of "source" (a project source id) or "output" (an earlier step\'s artifact)',
40
+ });
41
+ const buildStepSchema = z.discriminatedUnion("step", [
42
+ z
43
+ .object({
44
+ ...stepBase,
45
+ step: z.literal("stage"),
46
+ sources: z.array(z.union([z.string().min(1), stageSourceEntrySchema])),
47
+ into: z.string().optional(),
48
+ exclude: z.array(z.string().min(1)).optional(),
49
+ })
50
+ .strict(),
51
+ z
52
+ .object({
53
+ ...stepBase,
54
+ step: z.literal("assert-absent"),
55
+ in: z.string().optional(),
56
+ patterns: z.array(z.string().min(1)),
57
+ })
58
+ .strict(),
59
+ z
60
+ .object({
61
+ ...stepBase,
62
+ step: z.literal("assert-present"),
63
+ in: z.string().optional(),
64
+ paths: z.array(z.string().min(1)),
65
+ })
66
+ .strict(),
67
+ z
68
+ .object({
69
+ ...stepBase,
70
+ step: z.literal("zip"),
71
+ from: z.string(),
72
+ name: z.string(),
73
+ })
74
+ .strict(),
75
+ z
76
+ .object({
77
+ ...stepBase,
78
+ step: z.literal("blender-export"),
79
+ blend: z.string(),
80
+ script: z.string(),
81
+ out: z.string(),
82
+ })
83
+ .strict(),
84
+ z
85
+ .object({
86
+ ...stepBase,
87
+ step: z.literal("wolvenkit-import"),
88
+ glb: z.string(),
89
+ base: z.string(),
90
+ outDir: z.string(),
91
+ })
92
+ .strict(),
93
+ z
94
+ .object({
95
+ ...stepBase,
96
+ step: z.literal("wolvenkit-pack"),
97
+ from: z.string(),
98
+ out: z.string(),
99
+ verbosity: z.enum(["Minimal", "Normal", "Diagnostic"]).default("Minimal"),
100
+ })
101
+ .strict(),
102
+ z
103
+ .object({
104
+ ...stepBase,
105
+ step: z.literal("divine-lsx-to-lsf"),
106
+ in: z.string(),
107
+ glob: z.string().default("**/*.lsx"),
108
+ deleteSource: z.boolean().default(false),
109
+ })
110
+ .strict(),
111
+ z
112
+ .object({
113
+ ...stepBase,
114
+ step: z.literal("divine-loca"),
115
+ in: z.string(),
116
+ glob: z.string().default("**/*.xml"),
117
+ })
118
+ .strict(),
119
+ z
120
+ .object({
121
+ ...stepBase,
122
+ step: z.literal("divine-create-package"),
123
+ from: z.string(),
124
+ out: z.string(),
125
+ })
126
+ .strict(),
127
+ z
128
+ .object({
129
+ ...stepBase,
130
+ step: z.literal("verify-listing"),
131
+ of: z.string(),
132
+ forbid: z.array(z.string()).optional(),
133
+ require: z.array(z.string()).optional(),
134
+ })
135
+ .strict(),
136
+ z
137
+ .object({
138
+ ...stepBase,
139
+ step: z.literal("exec"),
140
+ command: z.string(),
141
+ args: z.array(z.string()).default([]),
142
+ cwd: z.string().optional(),
143
+ expectOutputs: z.array(z.string()).default([]),
144
+ timeoutMs: z.number().int().positive().optional(),
145
+ inputs: z.array(z.string().min(1)).optional(),
146
+ })
147
+ .strict(),
148
+ ]);
149
+ const buildVariantSchema = z
150
+ .object({
151
+ addSources: z.array(z.string().min(1)).optional(),
152
+ addSteps: z.array(buildStepSchema).optional(),
153
+ dropExclude: z.array(z.string().min(1)).optional(),
154
+ })
155
+ .strict();
156
+ const projectBuildSchema = z
157
+ .object({
158
+ staging: z.string().min(1).optional(),
159
+ outDir: z.string().min(1),
160
+ steps: z.array(z.union([z.string().min(1), buildStepSchema])).optional(),
161
+ exclude: z.array(z.string().min(1)).optional(),
162
+ variants: z.record(buildVariantSchema).optional(),
163
+ })
164
+ .strict();
165
+ const deployTargetSchema = z
166
+ .object({
167
+ rootId: z.string().min(1),
168
+ from: z.string().min(1),
169
+ into: z.string().optional(),
170
+ waitForGameExit: z.boolean().optional(),
171
+ backup: z.boolean().optional(),
172
+ locked: z.boolean().optional(),
173
+ mode: z.enum(["pak", "loose"]).optional(),
174
+ prune: z.array(z.string().min(1)).optional(),
175
+ variant: z.string().optional(),
176
+ exclude: z.array(z.string().min(1)).optional(),
177
+ })
178
+ .strict();
179
+ const junctionSchema = z
180
+ .object({
181
+ linkPath: z.string().min(1),
182
+ rootId: z.string().min(1),
183
+ target: z.string().min(1),
184
+ })
185
+ .strict();
186
+ const deploySchema = z
187
+ .object({
188
+ targets: z.array(deployTargetSchema),
189
+ junctions: z.array(junctionSchema).optional(),
190
+ waitMinutes: z.number().int().min(0).max(30).optional(),
191
+ })
192
+ .strict();
193
+ const reloadRuleSchema = z
194
+ .object({
195
+ match: z.string().min(1),
196
+ action: z.enum(["hot", "hot-degraded", "save-reload", "restart", "unknown"]),
197
+ via: z.string().optional(),
198
+ caveat: z.string().optional(),
199
+ })
200
+ .strict();
201
+ const reloadSchema = z.object({ rules: z.array(reloadRuleSchema) }).strict();
202
+ const bridgePolicySchema = z
203
+ .object({
204
+ mode: z.enum(["verify", "workbench"]),
205
+ grant: z.array(bridgeTierSchema).optional(),
206
+ })
207
+ .strict();
208
+ const projectBridgeSchema = z.object({ policy: bridgePolicySchema.optional() }).strict();
209
+ const publishTargetSchema = z
210
+ .object({
211
+ target: z.enum(["nexus", "modio", "github", "zip"]),
212
+ id: z.string().optional(),
213
+ include: z.array(z.string().min(1)).optional(),
214
+ notes: z.string().optional(),
215
+ })
216
+ .strict();
217
+ const testProfileSchema = z
218
+ .object({
219
+ saveName: z.string().optional(),
220
+ notes: z.string().optional(),
221
+ })
222
+ .strict();
223
+ export const modProjectSchema = z
224
+ .object({
225
+ schemaVersion: z.union(SUPPORTED_PROJECT_SCHEMA_VERSIONS.map((v) => z.literal(v))),
226
+ name: z.string().min(1),
227
+ game: z.string().min(1),
228
+ version: z.string().optional(),
229
+ authors: z.array(z.string().min(1)).optional(),
230
+ identity: z.record(z.string()).optional(),
231
+ sources: z.array(projectSourceSchema),
232
+ build: projectBuildSchema.optional(),
233
+ deploy: deploySchema.optional(),
234
+ dependencies: z.array(projectDependencySchema).optional(),
235
+ toolchain: z.record(z.string()).optional(),
236
+ publish: z.array(publishTargetSchema).optional(),
237
+ testProfile: testProfileSchema.optional(),
238
+ reload: reloadSchema.optional(),
239
+ bridge: projectBridgeSchema.optional(),
240
+ testPlans: z.array(z.string().min(1)).optional(),
241
+ })
242
+ .strict();
243
+ function formatPath(segments) {
244
+ if (segments.length === 0)
245
+ return "(root)";
246
+ let out = "";
247
+ for (const segment of segments) {
248
+ if (typeof segment === "number") {
249
+ out += `[${segment}]`;
250
+ }
251
+ else {
252
+ out += out ? `.${segment}` : segment;
253
+ }
254
+ }
255
+ return out;
256
+ }
257
+ export function parseProject(json) {
258
+ const result = modProjectSchema.safeParse(json);
259
+ if (!result.success) {
260
+ const lines = result.error.issues.map((issue) => `${formatPath(issue.path)}: ${issue.message}`);
261
+ throw new Error(`Invalid modwright.json:\n${lines.join("\n")}`);
262
+ }
263
+ return result.data;
264
+ }
@@ -0,0 +1,3 @@
1
+ export const PROJECT_FILE = "modwright.json";
2
+ export const PROJECT_SCHEMA_VERSION = 2;
3
+ export const SUPPORTED_PROJECT_SCHEMA_VERSIONS = [1, 2];