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,548 @@
1
+ # new-item-chain (Cyberpunk 2077). A TweakXL record cloned from a
2
+ # vanilla/project `$base`, the `.xl` localization + factory entries, a
3
+ # factory CSV row, an `en-us.json.json` row pair, and one CR2W source under
4
+ # `sources/entities/<slug>/` cloned from a `template-record` input
5
+ # (`root.ent.json` for an item base, `appearance.app.json` for a weapon
6
+ # base; see the two branches below). `base`'s blueprint check is a
7
+ # `requires.through` worked example.
8
+ #
9
+ # The factory row is written as plain CSV text via `insert-csv-row`, not a
10
+ # JSON source plus a `convert deserialize` step. The shipped
11
+ # `factory_parts.csv` is compiled CR2W, but no fixture or validator in this
12
+ # repo carries a confirmed JSON-source shape for a compiled CSV (unlike
13
+ # `.ent`/`.app`/`en-us.json`, all attested by real examples):
14
+ # `cp2077.garments.factory-csv-rows` itself reads the staged file as plain
15
+ # CSV text, and the proven-clean `jacket.ok.synthetic` fixture ships its
16
+ # `factory_parts.csv` as plain text too. Inventing an unconfirmed JSON shape
17
+ # for the one file with no attested example would be guessing, so a real
18
+ # release build's compiled-CR2W factory CSV is left to a WolvenKit pass by
19
+ # hand. The `.inkatlas` entry the catalog also lists is left to the manual
20
+ # checklist for the same reason `new-item`'s recolor is: registering an icon
21
+ # needs UV coordinates only a human (or a GUI atlas tool, which does not
22
+ # exist yet) can supply.
23
+ #
24
+ # Deserialize steps:
25
+ # - The two `convert deserialize` build steps use the positional-input,
26
+ # directory-output form (`convert deserialize <src> -o <dir>`; see
27
+ # new-sector-node.yaml's matching note), via two `*CompiledDir` inputs:
28
+ # smaller than a general "directory of this path" derived value in the
29
+ # engine for the one pair of callers that need it.
30
+ # - WolvenKit.CLI names a `convert deserialize` output by stripping
31
+ # exactly one trailing `.json` off the source file's own name, confirmed
32
+ # on three real files (`x.streamingsector.json` -> `x.streamingsector`,
33
+ # `x.ent.json` -> `x.ent`, and `en-us.source.json` -> `en-us.source`,
34
+ # not `en-us.json`). The compiled localization resource the
35
+ # `.archive.xl` manifest points at (`mod\<slug>\en-us.json`) is itself a
36
+ # CR2W resource named `en-us.json`, so its JSON source has to be named
37
+ # `en-us.json.json` for the tool's real rule to produce it. A name like
38
+ # `en-us.source.json` only works for people who compile in the WolvenKit
39
+ # GUI and rename the output themselves afterward; it is not a convention
40
+ # ModWright's unattended `build` can honour. So the free-form file
41
+ # inputs are collapsed into a directory-only pair (`enUsSourceDir`,
42
+ # `entCompiledDir`) with both filenames fixed by the scaffold itself,
43
+ # `en-us.json.json` on the source side and `en-us.json` on the compiled
44
+ # side, so the two agree by construction instead of trusting a
45
+ # caller-supplied name to happen to match. The same fix applies to
46
+ # new-garment-refit's `resource.patch` and new-sector-node. No engine
47
+ # change is needed: both are plain `${input.*}` interpolation directly in
48
+ # each op's `path`/`args`/`expectOutputs`. The manifest names one
49
+ # mod/<slug> folder for both compiled files, so one compiled directory
50
+ # serves both.
51
+ #
52
+ # The blueprint:
53
+ # - No real vanilla base is already an iconic blueprint. A
54
+ # `requires.through` check ("the blueprint must already be an iconic
55
+ # blueprint") was run against 15 real vanilla iconic and non-iconic
56
+ # weapons and satisfied by NONE of them: every real weapon's blueprint
57
+ # carries only plain `<Family>_WeaponMod1/2Element` children, never an
58
+ # `Iconic*ModElement`. A shipped iconic weapon mod never inherits an
59
+ # iconic slot from its vanilla `$base` either: it authors its OWN
60
+ # blueprint, `$base`'d off the vanilla base's own blueprint, adding the
61
+ # iconic element itself (fact
62
+ # cp2077.tweakdb.iconic-blueprint-required-for-mod-slot). So `requires`
63
+ # only asks that the base's `blueprint` flat, when present, resolves to
64
+ # a real record (a weapon precondition, not an iconic one; a non-weapon
65
+ # base with no `blueprint` flat at all stays not-applicable); a
66
+ # project-owned `<ItemName>Blueprint` record is authored, `$base`'d off
67
+ # the base's own real blueprint (exposed into `${template.baseBlueprint}`
68
+ # by the `requires` entry's `expose`, engine.ts), and the new item's own
69
+ # `blueprint:` flat is set to it. Both emits are gated on
70
+ # `when: { templateDefined: baseBlueprint }` (a general emit guard,
71
+ # engine.ts/types.ts) so a non-weapon base's chain is untouched.
72
+ # - `iconicModElementName` (default `Items.IconicRangedWeaponModElement`,
73
+ # confirmed in game) is a plain input, not hardcoded. The melee
74
+ # equivalent is Items.IconicMeleeWeaponModElement (slot
75
+ # AttachmentSlots.IconicMeleeWeaponMod1), read from the installed game's
76
+ # blueprint_elements.tweak on 2026-09-11 (fact
77
+ # cp2077.tweakdb.melee.iconic-melee-mod-element-is-items-iconicmeleeweaponmodelement);
78
+ # a melee item chain passes that name. Separately worth knowing: a
79
+ # shipped melee iconic (a katana) never clones a blueprint at all. It
80
+ # sets `blueprint: Items.Iconic_Melee_Blueprint`, an existing vanilla
81
+ # record that already carries the iconic slot (fact
82
+ # cp2077.tweakdb.melee.iconic-quality-pattern-confirmed-across-mods), so
83
+ # a melee caller may prefer overriding this scaffold's own `blueprint:`
84
+ # flat to that vanilla record afterward instead of trusting an unproven
85
+ # `iconicModElementName`.
86
+ # - The new blueprint record's shape follows the shipped one: `$base`
87
+ # plus a restated `rootElement` whose `childElements` is a plain list,
88
+ # with no `!append` tag (a shipped file uses `!append` only on the item
89
+ # record's own top-level list flats, never inside `rootElement`). The
90
+ # shipped file also restates the base blueprint's `slot:` and its
91
+ # inherited childElements. Those are values only the index knows, so a
92
+ # `blueprint-root` lookup renders the base root (slot, named elements by
93
+ # name, inline elements as nested mappings) and
94
+ # `requires[].through.expose` carries it into the emit. An
95
+ # iconic-only restatement gave the item no slots at all (observed
96
+ # 2026-09-11).
97
+ # - `cp2077.vehicles.inkatlas-uv-range` is not in `satisfies`: it only
98
+ # applies to `*.inkatlas.json` files, which this scaffold never writes
99
+ # (the inkatlas registration is the manual checklist's own job, per the
100
+ # "no GUI atlas tool exists yet" note above).
101
+ # `cp2077.vehicles.inkatlas-part-resolves` stays: it is project-scoped
102
+ # (`appliesTo: project`), so it genuinely runs every time.
103
+ #
104
+ # The chain has two branches (2026-09-12), keyed on the same `baseBlueprint`
105
+ # guard the blueprint emits use.
106
+ # - A base WITHOUT a blueprint flat (a garment, a loot item): the item
107
+ # branch. The root entity is the appearance dictionary
108
+ # (cp2077.items.wiring.root-entity-is-appearance-lookup-dictionary), so
109
+ # a root.ent is cloned from the template with its first appearance
110
+ # pointed at mod\<slug>\appearance.app. This branch has a known gap:
111
+ # nothing this scaffold writes points AT that root.ent (the record names
112
+ # the factory row and the row names the .app), so wiring (or dropping)
113
+ # the root entity is the modder's own job.
114
+ # - A base WITH a blueprint flat (all 15 real vanilla weapons checked
115
+ # carry one): the weapon branch.
116
+ # A vanilla weapon record names no entity of its own: Items.Base_Weapon
117
+ # sets entityName=weapon_root, which weapons.csv maps to
118
+ # base\weapons\weapon_root.ent, a shared root with an EMPTY appearances
119
+ # list, and its look is the .app that weapons_appearances.csv maps its
120
+ # appearanceResourceName to (Preset_Kenshin_Default ->
121
+ # base\weapons\firearms\handgun\arasaka_kenshin\w_handgun__arasaka_kenshin.app).
122
+ # Cloning weapon_root.ent therefore cannot work (the appearance rewrite
123
+ # indexes appearances[0] of an empty list) and would be pointless if it
124
+ # did. A shipped weapon mod ships NO root entity at all: its
125
+ # factory_parts.csv maps the weapon's row to mod\<slug>\<weapon>.app
126
+ # and the record's appearanceResourceName names that row (fact
127
+ # cp2077.items.wiring.weapon-look-is-an-app-row-not-a-root-entity). So
128
+ # for a weapon base the template must be the base's own .app (kind
129
+ # weapon-app), cloned verbatim to sources/.../appearance.app.json with
130
+ # only its ArchiveFileName rewritten, compiled beside en-us.json, and
131
+ # named by the factory row this scaffold already writes. The two
132
+ # write-json-from-template emits and the two deserialize steps are
133
+ # mutually exclusive on `baseBlueprint`, the same way the two record
134
+ # blocks are; `entTemplate`'s expectKind map keys the .app kind for a
135
+ # WeaponItem base so a weapon-ent (or an unrelated entity such as a
136
+ # loot-container .ent) is refused with the reason.
137
+ # One gap the guard language cannot close: the branch keys on
138
+ # `baseBlueprint`, `expectKind` keys on `baseType`, so a WeaponItem
139
+ # base carrying no blueprint flat would demand an .app template and
140
+ # then run the ITEM branch on it: `setAtPath` would write
141
+ # appearances.0.name onto the .app's first appearance *handle* instead
142
+ # of refusing (jsonpath.ts only throws on a missing array element, not
143
+ # a wrong-shaped one). No such base has been seen (the 15 checked all
144
+ # carry a blueprint), and `when` has no form for "the base type is X";
145
+ # closing it properly needs an engine-side guard.
146
+ id: cyberpunk2077.new-item-chain
147
+ game: cyberpunk2077
148
+ title: New Cyberpunk 2077 item chain (TweakXL + ArchiveXL + entity)
149
+ summary: Clone a vanilla/project item's TweakXL record, register it in an .xl manifest with a factory row and localization entries, and clone its root .ent (an item base) or its appearance .app (a weapon base) from a template.
150
+ modTypes: [weapon, item, loot, garment]
151
+ needs: { index: true }
152
+ inputs:
153
+ - type: string
154
+ name: itemName
155
+ prompt: "Item name, used for the TweakDB record and LocKey namespace, e.g. TestBlade."
156
+ pattern: "^[A-Za-z][A-Za-z0-9_]*$"
157
+ - type: string
158
+ name: modSlug
159
+ prompt: "Lowercase depot folder name for this item, e.g. testblade (used in every mod\\\\<slug>\\\\... path)."
160
+ pattern: "^[a-z][a-z0-9_]*$"
161
+ - type: index-ref
162
+ name: base
163
+ prompt: "$base record this item's TweakXL record inherits from."
164
+ lookup: { kind: chain, params: { name: "${input.base}" } }
165
+ requires:
166
+ # No real vanilla base is already an iconic blueprint (see the header
167
+ # note), so the check is a weapon
168
+ # precondition, not an iconic one — the base's blueprint, when
169
+ # present, must name a real record, or the project-owned blueprint
170
+ # this scaffold builds below would inherit from nothing. `through`
171
+ # fires only when `$base`'s effective `blueprint` flat is present at
172
+ # all — a garment/loot item with no blueprint flat is not a weapon and
173
+ # this check does not apply to it (`expose` then leaves
174
+ # `${template.baseBlueprint}` unset, and the emits gated on it do not
175
+ # run either).
176
+ # The base's record type (WeaponItem, Clothing, ...) is exposed for the entTemplate's kind check below; any value
177
+ # passes here, an absent type leaves ${template.baseType} unset.
178
+ - path: type
179
+ expose: baseType
180
+ matches: ".*"
181
+ reason: "the base's record type is read to check the .ent template's kind against it (any value passes this entry)."
182
+ - path: "flats.blueprint.value"
183
+ expose: baseBlueprint
184
+ through: { kind: chain, param: name, path: name }
185
+ reason: "the base's blueprint flat, once resolved, must name a real TweakDB record — the project-owned blueprint this scaffold builds inherits from it directly (the iconic-mod slot is always a project-authored blueprint, never something a real vanilla base already carries)."
186
+ # A project-owned blueprint that restated only the iconic element gave
187
+ # the item no slots at all (observed 2026-09-11). The
188
+ # base blueprint's root is restated in full from the index (lookup
189
+ # blueprint-root: its slot, its named elements by name, its inline
190
+ # elements as nested mappings), with the iconic element put first,
191
+ # and lands under the emitted `rootElement:` as ${template.baseRootYaml}.
192
+ - path: "flats.blueprint.value"
193
+ through:
194
+ kind: blueprint-root
195
+ param: name
196
+ path: rootYaml
197
+ params: { indent: "4", prepend: "${input.iconicModElementName}" }
198
+ expose: baseRootYaml
199
+ reason: "the base's blueprint must carry a root element this scaffold can restate (a rootElement, or slot/childElements flats along its $base chain): a derived blueprint that restates only the iconic element gives the item no attachment slots at all."
200
+ - type: string
201
+ name: displayName
202
+ prompt: "Player-facing item name."
203
+ - type: string
204
+ name: description
205
+ prompt: "Item description text."
206
+ - type: string
207
+ name: appearanceName
208
+ prompt: "Appearance name this item's root .ent should declare (item branch: a base with no blueprint flat). A weapon base's cloned .app keeps the base's own appearance names and this value is not used."
209
+ default: "default"
210
+ - type: string
211
+ name: iconicModElementName
212
+ prompt: "childElements entry the new project-owned blueprint appends to grant the iconic-mod attachment slot (only used when the base has its own blueprint flat at all). Ranged bases use the default; a melee base passes Items.IconicMeleeWeaponModElement (slot AttachmentSlots.IconicMeleeWeaponMod1, the vanilla melee iconics' own element)."
213
+ default: "Items.IconicRangedWeaponModElement"
214
+ - type: path
215
+ name: tweaksFile
216
+ prompt: "Destination .yaml under a project source of kind \"tweaks\" (created if missing)."
217
+ mustExist: false
218
+ - type: path
219
+ name: xlFile
220
+ prompt: "Destination .archive.xl manifest (created with an empty skeleton if missing). Put it beside the staged folder, e.g. archive-staging/<Project>.archive.xl, not inside it: the pack step stages the archive-staging source, and a manifest inside it would ship in the archive where ArchiveXL never reads it. It is registered as a project source and deployed to archive/pc/mod by its file name."
221
+ mustExist: false
222
+ - type: path
223
+ name: factoryCsvFile
224
+ prompt: "Destination factory CSV under archive-staging (created with a header row if missing)."
225
+ mustExist: false
226
+ - type: path
227
+ name: enUsSourceDir
228
+ prompt: "Directory the localization JSON source should be written to (created with an empty entries[] skeleton if missing). Its filename is fixed by this scaffold — en-us.json.json, not a free-form name — because WolvenKit.CLI's convert deserialize names its output by stripping exactly one trailing \".json\" off the source's own filename: only a source literally named en-us.json.json strips to the en-us.json the .archive.xl manifest points at (a name like en-us.source.json strips to en-us.source instead)."
229
+ mustExist: false
230
+ - type: path
231
+ name: entSourceDir
232
+ prompt: "Directory the cloned CR2W JSON source is written to, typically sources/entities/<slug>. The file name is fixed by this scaffold: root.ent.json for an item base (compiled to mod\\<slug>\\root.ent) or appearance.app.json for a weapon base (the factory row this scaffold writes points at mod\\<slug>\\appearance.app), because WolvenKit.CLI's convert deserialize names its output by stripping exactly one trailing .json off the source's own name."
233
+ mustExist: false
234
+ - type: path
235
+ name: entCompiledDir
236
+ prompt: "Directory the compiled root.ent (or appearance.app, for a weapon base) and en-us.json both land in, typically <archive-staging>/mod/<slug> — passed to WolvenKit.CLI's convert deserialize -o, which takes a directory, not a file. Both file names are fixed by this scaffold to match the manifest and the factory row, which name the same mod/<slug> folder, so one directory serves both (a separate enUsCompiledDir input could only ever agree with this one or be wrong)."
237
+ mustExist: false
238
+ - type: template-record
239
+ name: entTemplate
240
+ prompt: "Cached template-record directory (record.json + content.json) this item's look is cloned from. Its record kind has to fit the base: for a WeaponItem base the base's own appearance resource (kind weapon-app — the .app that weapons_appearances.csv maps the base's appearanceResourceName to; a vanilla weapon has no entity of its own to clone, its weapon_root.ent carries no appearances, and a shipped weapon mod ships an .app and no root entity); for a Clothing base the root entity (kind garment-ent, clothing-ent or item-ent). Observed in game: a loot-container .ent cloned for a weapon hung the game on equip."
241
+ expectKind:
242
+ by: baseType
243
+ # The index keeps a record's type as written: the vanilla dump says
244
+ # WeaponItem, a TweakXL yaml says gamedataWeaponItem_Record; both
245
+ # spellings are keyed.
246
+ map:
247
+ WeaponItem: "^weapon-app$"
248
+ gamedataWeaponItem_Record: "^weapon-app$"
249
+ Clothing: "^(garment|clothing|item)-ent$"
250
+ gamedataClothing_Record: "^(garment|clothing|item)-ent$"
251
+ reason: "A weapon base's look is the .app its appearanceResourceName maps to through weapons_appearances.csv, never a root entity (Items.Base_Weapon's weapon_root.ent has an empty appearances list, and a shipped weapon mod ships an .app and no .ent), and a loot-container .ent cloned for a weapon hung the game on equip (observed in game). Extract the base's .app with `template extract` (kind weapon-app; the path is the base's row in base\\gameplay\\factories\\items\\weapons\\weapons_appearances.csv) for a weapon, or its root entity (kind garment-ent) for clothing, and pass that directory."
252
+ ids:
253
+ - name: recordName
254
+ slot: "Items.${project.name}_${input.itemName}"
255
+ form: tweak-record
256
+ collision:
257
+ project: true
258
+ index: { kind: record, param: name }
259
+ # Always allocated (cheap, collision-checked like any other id), but only ever written when `${template.baseBlueprint}` is set
260
+ # — the two emits below that reference it are both gated on that.
261
+ - name: blueprintName
262
+ slot: "Items.${project.name}_${input.itemName}Blueprint"
263
+ form: tweak-record
264
+ collision:
265
+ project: true
266
+ index: { kind: record, param: name }
267
+ emits:
268
+ # `${ids.recordName}` gets exactly ONE occurrence in this file — these two variants are mutually exclusive (`templateDefined`
269
+ # / `templateUndefined` on the same key), never both. A record redeclared
270
+ # a second time in the same file is a real, confirmed TweakXL "Ambiguous
271
+ # definition" compile failure for at least the dotted-path edit form
272
+ # (cp2077.tweakdb.melee.dotted-path-mod-of-same-file-record-fails); this
273
+ # scaffold does not risk finding out whether a second full-block
274
+ # occurrence is any safer.
275
+ # `appearanceResourceName` names the factory row this scaffold writes
276
+ # (observed 2026-09-11): without it the record inherited the
277
+ # base's appearance, the spawned item wore the base's model, and
278
+ # cp2077.garments.factory-csv-rows, which checks only rows a record names,
279
+ # never looked at the row's unstaged appearance.app. With it, that rule
280
+ # blocks until the .app is staged, which is the truth of the chain.
281
+ - op: insert-block
282
+ path: "${input.tweaksFile}"
283
+ when: { templateDefined: baseBlueprint }
284
+ createIfMissing: ""
285
+ anchor: append
286
+ body: |
287
+ ${ids.recordName}:
288
+ $base: ${input.base}
289
+ displayName: LocKey#${project.name}-${input.itemName}-Name
290
+ localizedDescription: LocKey#${project.name}-${input.itemName}-Description
291
+ appearanceResourceName: ${input.itemName}
292
+ blueprint: ${ids.blueprintName}
293
+ - op: insert-block
294
+ path: "${input.tweaksFile}"
295
+ when: { templateUndefined: baseBlueprint }
296
+ createIfMissing: ""
297
+ anchor: append
298
+ body: |
299
+ ${ids.recordName}:
300
+ $base: ${input.base}
301
+ displayName: LocKey#${project.name}-${input.itemName}-Name
302
+ localizedDescription: LocKey#${project.name}-${input.itemName}-Description
303
+ appearanceResourceName: ${input.itemName}
304
+ # The shipped-mod pattern: a project-owned blueprint, `$base`'d off the base's own real blueprint,
305
+ # carrying the iconic-mod element — instead of demanding (unsatisfiably,
306
+ # per every real vanilla base checked) that the base already be iconic.
307
+ # Skipped entirely for a non-weapon base with no blueprint flat at all.
308
+ #
309
+ # The emitted shape is a shipped, in-game-verified one (fact
310
+ # cp2077.tweakdb.iconic-blueprint-required-for-mod-slot): `$base` plus a
311
+ # restated `rootElement` whose `childElements` is a plain list naming the
312
+ # iconic element, with NO `!append` tag. A shipped file uses `!append`
313
+ # only on the item record's own top-level list flats (`tags:`,
314
+ # `statModifiers:`), never inside `rootElement`, and nothing confirms
315
+ # TweakXL applies a list-mutation tag at that nesting depth, so this
316
+ # scaffold does not invent one.
317
+ #
318
+ # The shipped file restates the base blueprint's `slot:` and its whole
319
+ # inherited childElements list alongside the iconic element, because a
320
+ # restated rootElement is what the in-game-verified file does. The base's
321
+ # root arrives rendered from the index through the `blueprint-root` lookup
322
+ # (the `requires` entry above), iconic element first, so the emitted
323
+ # record is complete.
324
+ - op: insert-block
325
+ path: "${input.tweaksFile}"
326
+ when: { templateDefined: baseBlueprint }
327
+ anchor: append
328
+ body: |
329
+ ${ids.blueprintName}:
330
+ $base: ${template.baseBlueprint}
331
+ rootElement:
332
+ ${template.baseRootYaml}
333
+ - op: write-text
334
+ path: "${input.xlFile}"
335
+ ifExists: skip
336
+ body: |
337
+ {
338
+ "localization": { "onscreens": {} },
339
+ "factories": []
340
+ }
341
+ - op: insert-xl-entry
342
+ path: "${input.xlFile}"
343
+ pointer: localization.onscreens.en-us
344
+ value: "mod\\${input.modSlug}\\en-us.json"
345
+ - op: insert-xl-entry
346
+ path: "${input.xlFile}"
347
+ pointer: factories.-
348
+ value: "mod\\${input.modSlug}\\factory_parts.csv"
349
+ - op: insert-csv-row
350
+ path: "${input.factoryCsvFile}"
351
+ row: { name: "${input.itemName}", path: "mod\\${input.modSlug}\\appearance.app", shared: "false" }
352
+ - op: write-text
353
+ path: "${input.enUsSourceDir}/en-us.json.json"
354
+ ifExists: skip
355
+ body: |
356
+ {
357
+ "Header": {
358
+ "WolvenKitVersion": "8.20.0",
359
+ "WKitJsonVersion": "0.0.9",
360
+ "GameVersion": 2310,
361
+ "DataType": "CR2W",
362
+ "ArchiveFileName": "mod\\${input.modSlug}\\en-us.json"
363
+ },
364
+ "Data": {
365
+ "Version": 195,
366
+ "BuildVersion": 0,
367
+ "RootChunk": {
368
+ "$type": "JsonResource",
369
+ "cookingPlatform": "PLATFORM_PC",
370
+ "root": {
371
+ "HandleId": "0",
372
+ "Data": { "$type": "localizationPersistenceOnScreenEntries", "entries": [] }
373
+ }
374
+ },
375
+ "EmbeddedFiles": []
376
+ }
377
+ }
378
+ - op: insert-xl-entry
379
+ path: "${input.enUsSourceDir}/en-us.json.json"
380
+ pointer: Data.RootChunk.root.Data.entries.-
381
+ value:
382
+ $type: localizationPersistenceOnScreenEntry
383
+ femaleVariant: "${input.displayName}"
384
+ maleVariant: ""
385
+ primaryKey: "0"
386
+ secondaryKey: "${project.name}-${input.itemName}-Name"
387
+ - op: insert-xl-entry
388
+ path: "${input.enUsSourceDir}/en-us.json.json"
389
+ pointer: Data.RootChunk.root.Data.entries.-
390
+ value:
391
+ $type: localizationPersistenceOnScreenEntry
392
+ femaleVariant: "${input.description}"
393
+ maleVariant: ""
394
+ primaryKey: "0"
395
+ secondaryKey: "${project.name}-${input.itemName}-Description"
396
+ # Item branch (see the header): a base with no
397
+ # blueprint flat clones a root entity and points its first appearance at
398
+ # the mod's own .app.
399
+ - op: write-json-from-template
400
+ path: "${input.entSourceDir}/root.ent.json"
401
+ when: { templateUndefined: baseBlueprint }
402
+ template: entTemplate
403
+ rewrite:
404
+ - path: Header.ArchiveFileName
405
+ value: "mod\\${input.modSlug}\\root.ent"
406
+ - path: Data.RootChunk.appearances.0.name.$value
407
+ value: "${input.appearanceName}"
408
+ - path: Data.RootChunk.appearances.0.appearanceName.$value
409
+ value: "${input.appearanceName}"
410
+ - path: Data.RootChunk.appearances.0.appearanceResource.DepotPath.$value
411
+ value: "mod\\${input.modSlug}\\appearance.app"
412
+ - path: Data.RootChunk.defaultAppearance.$value
413
+ value: "${input.appearanceName}"
414
+ strip: []
415
+ renumberHandles: true
416
+ # Weapon branch: a base with a blueprint flat clones the base's own .app
417
+ # verbatim (its appearance names are the base's; the record inherits
418
+ # whichever the base selects) under the name the factory row above
419
+ # already points at. Only the archive-internal name is rewritten — the
420
+ # extracted header carries the extraction's temp path, never a depot path.
421
+ - op: write-json-from-template
422
+ path: "${input.entSourceDir}/appearance.app.json"
423
+ when: { templateDefined: baseBlueprint }
424
+ template: entTemplate
425
+ rewrite:
426
+ - path: Header.ArchiveFileName
427
+ value: "mod\\${input.modSlug}\\appearance.app"
428
+ strip: []
429
+ renumberHandles: true
430
+ # Against a fresh new-project output (no `build` block yet at all), a
431
+ # patch-project without `build.outDir` would leave modwright.json failing
432
+ # to reload ("build.outDir: Required"), the same gap new-sector-node and
433
+ # mesh-export-preset close. `add.outDir` only fills a genuinely missing
434
+ # `build.outDir`; a project that already set one keeps its own value
435
+ # (engine.ts's merge).
436
+ # The two deserialize steps go ahead of the
437
+ # project's stage step (`before: stage`; a project with no stage step gets
438
+ # them appended, with a line in the report's skipped list), or the compiled
439
+ # .ent and en-us.json land after the archive was already packed. The
440
+ # manifest this scaffold writes becomes a project source of kind xl (so
441
+ # the manifest rules see it wherever it lives) and an archive deploy
442
+ # target; both are idempotent across applies (engine.ts's merge).
443
+ # The deserialize step for the cloned CR2W source follows
444
+ # the branch (root.ent for an item base, appearance.app for a weapon base),
445
+ # so it lives in its own guarded patch-project op; the engine accumulates
446
+ # every patch-project op that ran into one merge, in op order (each step
447
+ # goes in ahead of the stage step, so the entity or appearance compiles
448
+ # first, then the localization), and a guarded op that did not run adds
449
+ # nothing.
450
+ - op: patch-project
451
+ when: { templateUndefined: baseBlueprint }
452
+ add:
453
+ steps:
454
+ - step: exec
455
+ id: "deserialize-${input.modSlug}-ent"
456
+ command: wolvenkit-cli
457
+ args: ["convert", "deserialize", "${input.entSourceDir}/root.ent.json", "-o", "${input.entCompiledDir}"]
458
+ expectOutputs: ["${input.entCompiledDir}/root.ent"]
459
+ before: stage
460
+ - op: patch-project
461
+ when: { templateDefined: baseBlueprint }
462
+ add:
463
+ steps:
464
+ - step: exec
465
+ id: "deserialize-${input.modSlug}-app"
466
+ command: wolvenkit-cli
467
+ args: ["convert", "deserialize", "${input.entSourceDir}/appearance.app.json", "-o", "${input.entCompiledDir}"]
468
+ expectOutputs: ["${input.entCompiledDir}/appearance.app"]
469
+ before: stage
470
+ - op: patch-project
471
+ add:
472
+ outDir: "archive-staging"
473
+ sources:
474
+ - id: "xl-${input.modSlug}"
475
+ path: "${input.xlFile}"
476
+ kind: xl
477
+ steps:
478
+ - step: exec
479
+ id: "deserialize-${input.modSlug}-enus"
480
+ command: wolvenkit-cli
481
+ args: ["convert", "deserialize", "${input.enUsSourceDir}/en-us.json.json", "-o", "${input.entCompiledDir}"]
482
+ expectOutputs: ["${input.entCompiledDir}/en-us.json"]
483
+ before: stage
484
+ deployTargets:
485
+ - rootId: archive
486
+ from: "${input.xlFile}"
487
+ locked: false
488
+ backup: true
489
+ templates:
490
+ - ref: entTemplate
491
+ from: "${template.entTemplate.contentPath}"
492
+ provenanceFrom: entTemplate
493
+ # Audited against a fresh new-project output
494
+ # (test/unit/scaffold/catalog-apply.test.ts). `entSourceDir`/`enUsSourceDir`
495
+ # both typically land under `sources/...`, invisible to
496
+ # `cp2077.vehicles.cr2w-unique-ids`/`cr2w-resource-path-shape` unless
497
+ # `cyberpunk2077.new-project` declares a `sources`-kind project source for
498
+ # it (it does, once for every scaffold in this position), so no further
499
+ # change to this file's satisfies[] is needed. The `patch-project` op's
500
+ # `add.outDir` covers a project with no `build` block yet.
501
+ satisfies:
502
+ - cp2077.tweaks.yaml-lint
503
+ - cp2077.tweaks.base-exists
504
+ - cp2077.tweaks.type-known
505
+ - cp2077.tweaks.flat-name-exists
506
+ - cp2077.tweaks.record-name-exists
507
+ - cp2077.tweaks.append-on-list-flat
508
+ - cp2077.tweaks.dotted-path-same-file
509
+ - cp2077.tweaks.instances-resolve
510
+ - cp2077.garments.factory-csv-rows
511
+ - cp2077.garments.xl-entries-resolve
512
+ - cp2077.history.xl-manifest-lint
513
+ - cp2077.history.clean-pack-json-sweep
514
+ - cp2077.vehicles.cr2w-unique-ids
515
+ - cp2077.vehicles.cr2w-resource-path-shape
516
+ - cp2077.vehicles.inkatlas-part-resolves
517
+ manual:
518
+ - title: Icon, inkatlas and (for a weapon base) the modifier slot
519
+ steps:
520
+ - "Draw the item's icon art and register a part + UV rect for it in the mod's .inkatlas (no GUI atlas tool exists yet — see this scaffold's own header note)."
521
+ - "If ${input.base} has a blueprint, ${ids.blueprintName} restates the base blueprint's whole root (slot and every child, from the index) with ${input.iconicModElementName} first, as a hand-written blueprint does; read the block and drop any element the item should not carry (for example a scope element on a weapon that has none). Then confirm it loads without a TweakXL-<timestamp>.log error, spawn the item, open the modding screen and confirm the base's own attachment slots are present beside the IconicWeaponModLegendary slot, and that it is populated with an iconic modifier's stats once one is authored on ${input.iconicModElementName}."
522
+ - "For a weapon base, ${input.entSourceDir}/appearance.app.json is the base's own .app verbatim (the base's meshes, so the item renders as the base until they are swapped): edit its appearances' mesh paths to the mod's own meshes when the look should change, and stage those meshes before packing. A vanilla weapon .app carries every preset appearance the base ships (the Kenshin's has ten); a shipped weapon mod's .app keeps just the single `default` one and its record names no appearanceName flat at all, so trimming the clone down to `default` is what the one working precedent did. For an item base, stage ${input.entSourceDir}/root.ent.json's appearance.app and referenced meshes before packing — the scaffold clones only the root .ent, not the mesh/appearance chain."
523
+ expected: "The item shows a real icon, and (for a weapon base) ${ids.blueprintName} loads cleanly with the iconic slot present and the base's own attachment slots intact on the modding screen."
524
+ claim:
525
+ idSuffix: spawns-with-stats
526
+ claim: "Game.AddToInventory(\"${ids.recordName}\", 1) yields an item whose displayName reads \"${input.displayName}\"."
527
+ testRows:
528
+ - gate: I-1
529
+ title: "Record exists with the right base"
530
+ # cp2077.tweakdb.flat reads a flat's raw TweakDB value — for displayName
531
+ # that is a LocKey handle, not the literal ${input.displayName} text
532
+ # (observed in game: TweakDB:GetFlat(...) returned a
533
+ # LocKey userdata) — so the probe confirms the flat *resolves*, not that
534
+ # it reads back as the display text verbatim; a human still confirms the
535
+ # actual rendered text in the manual checklist above.
536
+ expected: "cp2077.tweakdb.record confirms ${ids.recordName} exists; cp2077.tweakdb.flat confirms its displayName flat resolves (to a LocKey, not literal text — a human confirms the rendered text reads \"${input.displayName}\")."
537
+ manual: false
538
+ probes:
539
+ - kind: cp2077.tweakdb.record
540
+ params: { id: "${ids.recordName}" }
541
+ - kind: cp2077.tweakdb.flat
542
+ params: { id: "${ids.recordName}.displayName" }
543
+ - gate: I-2
544
+ title: "Icon and description review"
545
+ expected: "The spawned item's icon and description render correctly, and (if iconic) the modifier slot is populated."
546
+ manual: true
547
+ probeKinds: []
548
+ evidence: ["ModWright research"]