modwright 0.1.1 → 0.1.3

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 (65) hide show
  1. package/README.md +123 -15
  2. package/bridges/bg3-se/README.md +15 -0
  3. package/bridges/cp2077-cet/ModWrightBridge/probes.lua +113 -0
  4. package/bridges/cp2077-cet/README.md +17 -1
  5. package/bridges/cp2077-cet/bridge.json +1 -1
  6. package/dist/core/bridge/pending.js +8 -1
  7. package/dist/core/bridge/stage.js +2 -2
  8. package/dist/core/build/run.js +14 -0
  9. package/dist/core/build/stage.js +3 -2
  10. package/dist/core/build/steps/core.js +4 -4
  11. package/dist/core/deploy/plan.js +68 -1
  12. package/dist/core/deploy/reload.js +5 -3
  13. package/dist/core/ini.js +26 -0
  14. package/dist/core/knowledge/facts.js +0 -12
  15. package/dist/core/ledger/hooks.js +22 -5
  16. package/dist/core/logs/registry.js +19 -0
  17. package/dist/core/logs/scan.js +65 -10
  18. package/dist/core/logs/triage.js +79 -2
  19. package/dist/core/managers/mo2.js +121 -0
  20. package/dist/core/managers/vortex.js +52 -0
  21. package/dist/core/ownership/index.js +364 -0
  22. package/dist/core/project/index.js +2 -1
  23. package/dist/core/project/load.js +7 -25
  24. package/dist/core/project/outside-root.js +36 -0
  25. package/dist/core/project/schema.js +38 -1
  26. package/dist/core/safety/backup.js +2 -2
  27. package/dist/core/safety/execute.js +12 -4
  28. package/dist/core/safety/restore.js +62 -0
  29. package/dist/core/testplan/registry.js +51 -2
  30. package/dist/core/text/converters/index.js +1 -1
  31. package/dist/core/text/toolchain.js +2 -2
  32. package/dist/core/toolchain/locate.js +13 -1
  33. package/dist/core/toolchain/run.js +6 -4
  34. package/dist/core/toolchain/types.js +1 -1
  35. package/dist/core/trust.js +105 -0
  36. package/dist/core/writes/index.js +206 -0
  37. package/dist/index.js +33 -9
  38. package/dist/server.js +252 -34
  39. package/dist/surfaces/baldursgate3/surface.js +3 -0
  40. package/dist/surfaces/baldursgate3/validators/story.js +3 -3
  41. package/dist/surfaces/baldursgate3/writes.js +152 -0
  42. package/dist/surfaces/cyberpunk2077/compat.js +8 -4
  43. package/dist/surfaces/cyberpunk2077/index/parsers/tweakxl-yaml.js +2 -2
  44. package/dist/surfaces/cyberpunk2077/logs.js +86 -6
  45. package/dist/surfaces/cyberpunk2077/surface.js +13 -1
  46. package/dist/surfaces/cyberpunk2077/validators/cr2w.js +9 -3
  47. package/dist/surfaces/cyberpunk2077/writes.js +67 -0
  48. package/dist/surfaces/eldenring/loaders.js +7 -1
  49. package/dist/surfaces/skyrimse/loadorder.js +2 -26
  50. package/dist/surfaces/skyrimse/surface.js +1 -0
  51. package/dist/surfaces/stardewvalley/logs.js +41 -0
  52. package/dist/surfaces/stardewvalley/surface.js +2 -0
  53. package/dist/surfaces/stardewvalley/writes.js +136 -0
  54. package/knowledge/baldursgate3/dialogue.osiris-goals.yaml +29 -0
  55. package/knowledge/baldursgate3/osiris.tags.yaml +78 -0
  56. package/knowledge/cyberpunk2077/cet.rtti-binding.yaml +227 -0
  57. package/knowledge/cyberpunk2077/cet.sandbox.yaml +22 -0
  58. package/knowledge/cyberpunk2077/codeware.overview.yaml +63 -16
  59. package/knowledge/cyberpunk2077/codeware.systems.yaml +152 -33
  60. package/knowledge/cyberpunk2077/entities.queries.yaml +275 -0
  61. package/knowledge/cyberpunk2077/redhottools.hotreload.yaml +30 -4
  62. package/knowledge/cyberpunk2077/rtti.game-systems.yaml +224 -0
  63. package/package.json +1 -1
  64. package/scaffolds/baldursgate3/new-project.yaml +4 -1
  65. package/scaffolds/cyberpunk2077/new-project.yaml +4 -1
@@ -0,0 +1,224 @@
1
+ game: cyberpunk2077
2
+ topic: rtti.game-systems
3
+ title: RTTI-confirmed game systems and entity/targeting classes ModWright's CET bridge probes touch
4
+ facts:
5
+ - id: cp2077.rtti.scriptgameinstance-getxxx-system-getters
6
+ claim: "ScriptGameInstance (the class behind CET's `Game` table, per probes.lua's GLOBAL_GETTERS and
7
+ its Game.GetPlayer()/GetDynamicEntitySystem()/GetTransactionSystem() call sites) has 93
8
+ RTTI-listed functions named GetXxx that return a ref<System> handle. Every game subsystem a
9
+ script can reach starts from one of these. Full list, `GetXxx -> ReturnClass`:
10
+ GetAINavigationSystem->AINavigationSystem, GetAchievementSystem->gameAchievementSystem,
11
+ GetActivityLogSystem->gameActivityLogSystem,
12
+ GetAnimationSystem->worldAnimationSystemScriptInterface,
13
+ GetAttitudeSystem->gameCAttitudeManager, GetAudioSystem->gameGameAudioSystem,
14
+ GetAutoSaveSystem->gameAutoSaveSystem, GetBlackboardSystem->gameBlackboardSystem,
15
+ GetCameraSystem->gameCameraSystem,
16
+ GetCharacterCustomizationSystem->gameuiICharacterCustomizationSystem,
17
+ GetCombatQueriesSystem->gameICombatQueriesSystem, GetCommunitySystem->gameCommunitySystem,
18
+ GetCompanionSystem->gameCompanionSystem, GetContainerManager->gameContainerManager,
19
+ GetCooldownSystem->gameICooldownSystem, GetCoverManager->AISmartCoverManager,
20
+ GetDamageSystem->gameDamageSystem, GetDebugCheatsSystem->gameDebugCheatsSystem,
21
+ GetDebugDrawHistorySystem->gameIDebugDrawHistorySystem,
22
+ GetDebugPlayerBreadcrumbs->gameDebugPlayerBreadcrumbs,
23
+ GetDebugVisualizerSystem->gameDebugVisualizerSystem, GetDelaySystem->gameDelaySystem,
24
+ GetDeviceSystem->gameDeviceSystem, GetDynamicSpawnSystem->gameDynamicSpawnSystem,
25
+ GetEffectorSystem->gameEffectorSystem,
26
+ GetEntitySpawnerEventsBroadcaster->gameEntitySpawnerEventsBroadcasterImpl,
27
+ GetFxSystem->gameFxSystem, GetGameEffectSystem->gameEffectSystem,
28
+ GetGameFeatureManager->gameGameFeatureManager, GetGameRulesSystem->gameIGameRulesSystem,
29
+ GetGameTagSystem->gameGameTagSystem, GetGamepadLightController->gameuiGamepadLightController,
30
+ GetGameplayLogicPackageSystem->gameGameplayLogicPackageSystem,
31
+ GetGlobalTVSystem->gameuiGlobalTvSystem, GetGodModeSystem->gameGodModeSystem,
32
+ GetInfluenceMapSystem->gameinfluenceSystem, GetInteractionManager->gameinteractionsCManager,
33
+ GetInventoryManager->gameInventoryManager, GetJournalManager->gameJournalManager,
34
+ GetLevelAssignmentSystem->gameLevelAssignmentSystem, GetLocationManager->gameLocationManager,
35
+ GetLootManager->gameLootManager, GetMappinSystem->gamemappinsMappinSystem,
36
+ GetMinimapSystem->gameMinimapSystem, GetMountingFacility->gamemountingIMountingFacility,
37
+ GetNavigationSystem->worldNavigationScriptInterface,
38
+ GetObjectPoolSystem->gameObjectPoolSystem, GetOnlineSystem->gameIOnlineSystem,
39
+ GetPersistencySystem->gamePersistencySystem, GetPhoneManager->questPhoneManager,
40
+ GetPhotoModeSystem->gamePhotoModeSystem, GetPingSystem->gamePingSystem,
41
+ GetPlayerManagerSystem->gameIPlayerManager, GetPlayerSystem->cpPlayerSystem,
42
+ GetPoliceRadioSystem->gamePoliceRadioSystem, GetPrereqManager->gamePrereqManager,
43
+ GetPreventionSpawnSystem->gamePreventionSpawnSystem,
44
+ GetProjectileSystem->gameProjectileSystem, GetQuestsContentSystem->questQuestsContentSystem,
45
+ GetQuestsSystem->questQuestsSystem, GetRacingSystem->vehicleRacingSystem,
46
+ GetRazerChromaEffectsSystem->gameRazerChromaEffectsSystem,
47
+ GetReactionSystem->AIReactionSystem, GetRestrictMovementAreaManager->AIGuardAreaManager,
48
+ GetRuntimeInfo->worldRuntimeInfo, GetSafeAreaManager->AISafeAreaManager,
49
+ GetSceneSystem->scnISceneSystem,
50
+ GetScriptableSystemsContainer->gameScriptableSystemsContainer,
51
+ GetScriptsDebugOverlaySystem->gameScriptsDebugOverlaySystem,
52
+ GetSenseManager->senseCSenseManager, GetSettingsSystem->userSettingsUserSettings,
53
+ GetSpatialQueriesSystem->gameSpatialQueriesSystem, GetStatPoolsSystem->gameStatPoolsSystem,
54
+ GetStatsDataSystem->gameStatsDataSystem, GetStatsSystem->gameStatsSystem,
55
+ GetStatusEffectSystem->gameStatusEffectSystem, GetStimuliSystem->gameStimuliSystem,
56
+ GetSubtitleHandlerSystem->gameuiSubtitleHandlerSystem,
57
+ GetTargetingSystem->gametargetingTargetingSystem,
58
+ GetTelemetrySystem->gameTelemetryTelemetrySystem,
59
+ GetTeleportationFacility->gameTeleportationFacility, GetTimeSystem->gameTimeSystem,
60
+ GetTrafficSystem->worldTrafficScriptInterface, GetTransactionSystem->gameTransactionSystem,
61
+ GetTutorialManager->questTutorialManager,
62
+ GetUIObjectsLoaderSystem->gameuiIUIObjectsLoaderSystem, GetUISystem->gameuiGameSystemUI,
63
+ GetVehicleSystem->gameVehicleSystem, GetVisionModeSystem->gameVisionModeSystem,
64
+ GetWardrobeSystem->gameWardrobeSystem, GetWatchdogSystem->gameIWatchdogSystem,
65
+ GetWeatherSystem->worldWeatherScriptInterface, GetWorkspotSystem->gameWorkspotGameSystem. All
66
+ 93 take no parameters besides the implicit `self` and return by value a `ref<System>` per the
67
+ type tag {\"a\": 26, \"c\": {\"b\": System}}."
68
+ status: inferred
69
+ source: 'rayshader/cp2077-nativedb classes.json, class entry {"b": "ScriptGameInstance"} (parent
70
+ null — it is the script-facing root), 107 functions total; the 93 above are every one whose
71
+ name starts with "Get" and whose "c" (return type) tag is {"a": 26, ...} (ref<T>). Read
72
+ 2026-09-11 with python3 against the committed dump.'
73
+ tags:
74
+ - rtti
75
+ - scriptgameinstance
76
+ - game-table
77
+ - system-getter
78
+ - bridge
79
+ detail: "The bridge's working assumption (nowhere written down before this fact) is that CET's
80
+ `Game` global IS a ScriptGameInstance handle and `Game.GetXxx()` in Lua is a 1:1 call onto
81
+ this list — that is how probes.lua's existing calls (Game.GetPlayer(),
82
+ Game.GetDynamicEntitySystem(), Game.GetTransactionSystem()) already behave, and
83
+ GetGameTagSystem/GetTargetingSystem in this same list are exactly the systems fact 4 and the
84
+ entities.queries.yaml facts already use through that same `Game.` spelling. Checked
85
+ knowledge/cyberpunk2077/cet.sandbox.yaml for a fact about the `Game` global specifically: it
86
+ has none — it documents the mod sandbox's globals-whitelist mechanism in general
87
+ (cp2077.cet.mod-sandbox-has-no-global-table: `_G` absent, lookup must be by name) but never
88
+ states what `Game` itself resolves to. So this mapping rests on probes.lua's own
89
+ GLOBAL_GETTERS table and its three cited call sites, not on a dump entry naming \"Game\" —
90
+ RTTI reflects native classes only, and `Game` is CET's own scripting binding on top of
91
+ ScriptGameInstance, so no dump entry could name it directly. Note GetPlayerSystem returns
92
+ cpPlayerSystem, not a class named after the pattern used elsewhere in this list, and
93
+ GetTargetingSystem returns gametargetingTargetingSystem, not gameITargetingSystem — see fact
94
+ 4."
95
+ - id: cp2077.rtti.playerpuppet-npcpuppet-gameobject-identity-parent-chain
96
+ claim: Both PlayerPuppet and NPCPuppet share the identical parent chain ScriptedPuppet -> gamePuppet
97
+ -> gamePuppetBase -> gameTimeDilatable -> gameObject -> entGameEntity -> entEntity ->
98
+ IScriptable -> ISerializable; they differ only in their own class, not in any ancestor.
99
+ entEntity's own chain is entEntity -> IScriptable -> ISerializable, and gameObject's is
100
+ gameObject -> entGameEntity -> entEntity -> IScriptable -> ISerializable. A probe written
101
+ against gameObject or entEntity methods therefore works unchanged on either puppet type.
102
+ status: inferred
103
+ source: "rayshader/cp2077-nativedb classes.json, walking each class entry's \"a\" (parent) field:
104
+ {\"b\": \"PlayerPuppet\", \"a\": \"ScriptedPuppet\"}, {\"b\": \"NPCPuppet\", \"a\":
105
+ \"ScriptedPuppet\"}, {\"b\": \"ScriptedPuppet\", \"a\": \"gamePuppet\"}, {\"b\":
106
+ \"gamePuppet\", \"a\": \"gamePuppetBase\"}, {\"b\": \"gamePuppetBase\", \"a\":
107
+ \"gameTimeDilatable\"}, {\"b\": \"gameTimeDilatable\", \"a\": \"gameObject\"}, {\"b\":
108
+ \"gameObject\", \"a\": \"entGameEntity\"}, {\"b\": \"entGameEntity\", \"a\": \"entEntity\"},
109
+ {\"b\": \"entEntity\", \"a\": \"IScriptable\"}, {\"b\": \"IScriptable\", \"a\":
110
+ \"ISerializable\"}, {\"b\": \"ISerializable\", \"a\": null}. Read 2026-09-11."
111
+ tags:
112
+ - rtti
113
+ - parent-chain
114
+ - playerpuppet
115
+ - npcpuppet
116
+ - gameobject
117
+ - ententity
118
+ - id: cp2077.rtti.ententity-gameobject-position-tag-identity-methods
119
+ claim: 'entEntity (RTTI functions, "self" param omitted): FindComponentByName(componentName: CName)
120
+ -> ref<entIComponent>, GetEntityID() -> entEntityID, GetWorldOrientation() -> Quaternion,
121
+ GetWorldPosition() -> Vector4. gameObject adds HasTag(tag: CName) -> Bool, IsPlayer() -> Bool,
122
+ IsNPC() -> Bool, IsPlayerControlled() -> Bool. GetClassName() -> CName is not redeclared on
123
+ either — it, IsA(className: CName) -> Bool and IsExactlyA(className: CName) -> Bool live
124
+ higher up, on IScriptable, and are inherited down the chain in the previous fact.'
125
+ status: inferred
126
+ source: "rayshader/cp2077-nativedb classes.json: class entEntity function entries
127
+ FindComponentByName, GetEntityID, GetWorldOrientation, GetWorldPosition; class gameObject (363
128
+ functions total) entries HasTag, IsPlayer, IsNPC, IsPlayerControlled; class IScriptable (5
129
+ functions) entries GetClassName, IsA, IsExactlyA, ToString, DetectScriptableCycles. Read
130
+ 2026-09-11."
131
+ tags:
132
+ - rtti
133
+ - ententity
134
+ - gameobject
135
+ - position
136
+ - tag
137
+ - identity
138
+ - findcomponent
139
+ detail: GetWorldPosition/GetWorldOrientation and FindComponentByName are already relied on by the
140
+ CET bridge's `xyz`/`quat`/`componentPresent` helpers (attested there against a shipped CET
141
+ mod's own calls), so this fact is the RTTI confirmation of calls the bridge already makes
142
+ rather than a new one to try. HasTag/IsPlayer/IsNPC are not yet called anywhere in probes.lua.
143
+ - id: cp2077.rtti.targetingsystem-crosshair-and-distance-queries
144
+ claim: "gameITargetingSystem is an empty interface (0 functions); the concrete class is
145
+ gametargetingTargetingSystem (41 functions), which is what ScriptGameInstance's
146
+ GetTargetingSystem() actually returns — not gameITargetingSystem, correcting the class name
147
+ entities.queries.yaml's cp2077.entities.spatial-and-targeting-systems-exist-but-are- heavier
148
+ used loosely. The \"what is near the crosshair\" functions, full signatures (\"self\"
149
+ omitted): GetObjectClosestToCrosshair(instigator: wref<gameObject>, angleDistance:
150
+ EulerAngles, query: gameTargetSearchQuery) -> ref<gameObject>; GetTargetClosestByDistance(
151
+ instigator: wref<gameObject>, query: gameTargetSearchQuery, position: Vector4, targetPartInfo:
152
+ gametargetingTargetPartInfo) -> ref<gameObject>; GetTargetParts(instigator: wref<gameObject>,
153
+ query: gameTargetSearchQuery, parts: array<gametargetingTargetPartInfo>) -> Bool; also
154
+ GetLookAtObject(instigator: wref<gameObject>, withLOS: Bool, ignoreTranparent: Bool) ->
155
+ ref<gameObject> and GetTrackedTargetObject(instigator: wref<gameObject>) -> ref<gameObject> as
156
+ lighter alternatives that take no query struct at all."
157
+ status: inferred
158
+ source: 'rayshader/cp2077-nativedb classes.json: class gameITargetingSystem {"a": "gameIGameSystem",
159
+ "f": []}; class gametargetingTargetingSystem {"a": "gameITargetingSystem", ...} with the 41
160
+ functions enumerated above by name; ScriptGameInstance function entry {"a":
161
+ "GetTargetingSystem", "c": {"a": 26, "c": {"b": "gametargetingTargetingSystem"}}}. Read
162
+ 2026-09-11.'
163
+ tags:
164
+ - rtti
165
+ - targetingsystem
166
+ - crosshair
167
+ - spatial-query
168
+ - gametargetsearchquery
169
+ related:
170
+ - cp2077.entities.spatial-and-targeting-systems-exist-but-are-heavier
171
+ - cp2077.entities.gameobject-getentitiesaroundobject-filter-constructors-exist
172
+ detail: "gameTargetSearchQuery's fields, checked directly against the dump: testedSet
173
+ (gameTargetingSet), searchFilter (gameTargetSearchFilter), includeSecondaryTargets (Bool),
174
+ ignoreInstigator (Bool), maxDistance (Float), filterObjectByDistance (Bool), queryTarget
175
+ (entEntityID), plus method SetComponentFilter(componentFilter:
176
+ gametargetingSystemScriptFilter) -> void. This matches entities.queries.yaml's
177
+ cp2077.entities.gameobject-getentitiesaroundobject-filter-constructors-exist field-for- field
178
+ — nothing to add there; see that fact for the TSF_* constructors that build searchFilter. None
179
+ of the four crosshair/distance/parts/lookAt calls above are attempted by probes.lua yet, and
180
+ none is attested from a CET mod or wiki page on disk — same unverified status as the rest of
181
+ the targeting surface."
182
+ - id: cp2077.rtti.gametagsystem-two-functions-already-documented
183
+ claim: gameGameTagSystem's two RTTI functions (GetAnyMatchingEntity, GetAllMatchingEntities) and
184
+ gameObject's native `tags`/HasTag are already fully recorded in
185
+ knowledge/cyberpunk2077/entities.queries.yaml's cp2077.entities.native-gametagsystem-
186
+ finds-entities-by-tag fact; this fact exists only so a reader of rtti.game-systems does not
187
+ miss that the tag system was already covered from the same dump.
188
+ status: inferred
189
+ source: knowledge/cyberpunk2077/entities.queries.yaml, fact
190
+ cp2077.entities.native-gametagsystem-finds-entities-by-tag (cites rayshader/cp2077-nativedb
191
+ classes.json class gameGameTagSystem directly).
192
+ tags:
193
+ - rtti
194
+ - gametagsystem
195
+ - hastag
196
+ - cross-reference
197
+ related:
198
+ - cp2077.entities.native-gametagsystem-finds-entities-by-tag
199
+ - id: cp2077.rtti.no-generic-component-enumeration-by-class-in-dump
200
+ claim: "No RTTI-listed function enumerates an entity's components by class or type. Searching every
201
+ function on entEntity, gameObject and entIComponent for \"Component\" in the name turns up
202
+ only lookup-by-name (FindComponentByName(componentName: CName) -> ref<entIComponent>, declared
203
+ identically on both entEntity and entIComponent) and a handful of gameObject methods that each
204
+ return one specific, hardcoded component type by its own dedicated getter —
205
+ GetUISlotComponent, GetSensesComponent, GetSourceShootComponent, GetSquadMemberComponent,
206
+ GetStatusEffectComponent, GetStimBroadcasterComponent, GetTargetShootComponent,
207
+ GetTargetTrackerComponent — plus OnRequestComponents, which is an event callback, not a query.
208
+ A probe wanting \"every component of class X on this entity\" has no RTTI call to reach for;
209
+ it would need to know the component's name up front for FindComponentByName, or be one of the
210
+ eight named kinds above."
211
+ status: inferred
212
+ source: 'rayshader/cp2077-nativedb classes.json: class entEntity functions filtered for substring
213
+ "Component" -> ["FindComponentByName"]; class gameObject (363 functions) filtered the same way
214
+ -> ["GetUISlotComponent", "OnRequestComponents", "GetSensesComponent",
215
+ "GetSourceShootComponent", "GetSquadMemberComponent", "GetStatusEffectComponent",
216
+ "GetStimBroadcasterComponent", "GetTargetShootComponent", "GetTargetTrackerComponent"]; class
217
+ entIComponent -> ["FindComponentByName"]. Read 2026-09-11 with the exact same query pattern
218
+ used for fact 3.'
219
+ tags:
220
+ - rtti
221
+ - component
222
+ - gotcha
223
+ - findcomponentbyname
224
+ - unattested
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modwright",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A game-agnostic MCP server for game modding, with per-game surfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,10 +19,13 @@ inputs:
19
19
  name: version
20
20
  prompt: Initial version (semver-ish, major.minor.revision).
21
21
  default: "0.1.0"
22
+ # Interpolated raw into modwright.json; the same guard the Stardew scaffold carries.
23
+ pattern: "^(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:\\.(?:0|[1-9][0-9]*))?(?:-(?:[a-zA-Z0-9]+[-.]?)+)?$"
22
24
  - type: string
23
25
  name: author
24
- prompt: Author name.
26
+ prompt: Author name (no double quotes or backslashes — it is written into modwright.json as-is).
25
27
  default: "Unknown"
28
+ pattern: '^[^"\\\x00-\x1f]+$'
26
29
  # A project can keep its
27
30
  # LSLib divine path as a toolchain override in modwright.json; a fresh
28
31
  # project on the same machine needs the same line or its first build
@@ -29,10 +29,13 @@ inputs:
29
29
  name: version
30
30
  prompt: Initial version (semver-ish, major.minor.revision).
31
31
  default: "0.1.0"
32
+ # Interpolated raw into modwright.json; the same guard the Stardew scaffold carries.
33
+ pattern: "^(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:\\.(?:0|[1-9][0-9]*))?(?:-(?:[a-zA-Z0-9]+[-.]?)+)?$"
32
34
  - type: string
33
35
  name: author
34
- prompt: Author name.
36
+ prompt: Author name (no double quotes or backslashes — it is written into modwright.json as-is).
35
37
  default: "Unknown"
38
+ pattern: '^[^"\\\x00-\x1f]+$'
36
39
  # Observed in practice (2026-09-12): the WolvenKit CLI override had to be
37
40
  # added to a fresh project by hand because the locator does not find a CLI
38
41
  # installed outside the usual places. The path is machine-specific and