@aeonsparx/pi-unreal 0.0.0-stage → 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aeonsparx Interactive
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,203 @@
1
- # Temporary Holding Version
1
+ # pi-unreal
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Unreal Engine awareness for the Pi coding agent.**
4
+
5
+ Open a UE project in a `pi` session and the agent stops guessing. It knows which engine
6
+ installation that `.uproject` really resolves to, what the `.uproject` / `.uplugin` files actually
7
+ say, which plugins exist on disk, and where a C++ symbol is declared — with the file, the line and
8
+ the owning module. No compiler, no Visual Studio, no Rider, no clangd required.
9
+
10
+ It is **read-only**: it never writes to your project, never touches the Windows registry, never
11
+ launches the editor, and blocks three kinds of edit that damage Unreal work.
12
+
13
+ ## What you get in a session
14
+
15
+ **Context is detected for you.** When the session starts in a directory that contains a
16
+ `.uproject` — the working directory itself, or any of up to 3 parent directories — one note is
17
+ shown before the first answer: the resolved engine root and version, installed vs source build,
18
+ the project's modules, the number of declared plugin references, and the conventions below. Ask
19
+ for a re-check any time with `/unreal`.
20
+
21
+ **Ask in plain English.** "Which engine is this project on?", "Is MassGameplay available to this
22
+ project?", "Where is `FLpMovementDirectionThresholds` defined?", "What do I need to link to use
23
+ `UAnimInstance`?" The agent picks the matching tool:
24
+
25
+ | Tool | Question it answers |
26
+ | --- | --- |
27
+ | `unreal_resolve_engine` | "Which engine does this `.uproject` use — and how do you know?" |
28
+ | `unreal_project_inspect` | Modules, target platforms, and every plugin reference resolved against the plugins that exist on disk |
29
+ | `unreal_plugin_inspect` | One `.uplugin` as the engine parses it: modules, loading phases, version fields, plugin dependencies |
30
+ | `unreal_symbol` | "Where is this class / struct / function / `UPROPERTY` / delegate declared?" One line per hit: kind, file:line, module |
31
+ | `unreal_symbol_list` | "What does this project define?" Names by prefix — `ULp`, `ANiMy`, `UAnim` |
32
+ | `unreal_context` | "What does this file/module include, define, and depend on?" Include paths, dependency lists, the real `WITH_*` defines |
33
+ | `unreal_index` | Builds and reports the source index, and the clangd compile database on request |
34
+
35
+ **Slash commands you can type yourself:**
36
+
37
+ | Command | Result |
38
+ | --- | --- |
39
+ | `/unreal [project]` | Engine, modules and plugin-reference report for this directory |
40
+ | `/unreal-engines` | Every engine installation this machine can prove, and how each was found |
41
+ | `/unreal-symbol <symbol>` | Definition lookup, project + plugins, then engine if an engine index exists |
42
+ | `/unreal-module <Module>` | Module directory, public/private dependency lists, defines file, C++ standard, SDK |
43
+ | `/unreal-cache stats\|clear` | Inspect or clear the cached indexes |
44
+
45
+ Example — `/unreal`:
46
+
47
+ ```text
48
+ project C:/Work/Lp/Lp.uproject
49
+ engine: C:/Program Files/Epic Games/UE_5.8
50
+ version 5.8.3 | installed build | association "5.8" (version) via dir-scan
51
+ Build.version: C:/Program Files/Epic Games/UE_5.8/Engine/Build/Build.version
52
+ modules: Lp (Runtime), LpEditor (Editor)
53
+ plugins declared: 25
54
+ plugin index: 897 .uplugin (cache hit)
55
+ plugin references:
56
+ MassGameplay: resolved ← Engine
57
+ ModularGameplay: resolved ← Engine
58
+ SomeLocalPlugin: resolved ← Project
59
+ RetiredPlugin: missing
60
+ totals: 22 resolved, 3 missing of 25 references
61
+ ```
62
+
63
+ Example — `unreal_symbol` for an engine type (engine index already built):
64
+
65
+ ```text
66
+ unreal_symbol 'AActor' scope=engine: 1 definition(s)
67
+ class AActor C:/Program Files/Epic Games/UE_5.8/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h:282 [Engine · engine]
68
+ ```
69
+
70
+ That is a definition line, not a grep dump: grepping a hot engine class name returns ~176 files of
71
+ noise, which is exactly what burns a session's context.
72
+
73
+ ## Guardrails (automatic, nothing to enable)
74
+
75
+ | Situation | What happens |
76
+ | --- | --- |
77
+ | An edit targeting `*.uasset` / `*.umap` | **Blocked** — those are cooked binary assets; author them in the running editor |
78
+ | A C++ edit while `UnrealEditor` is running | **Warning** — freshly compiled C++ will not load into a running editor; close it, then build |
79
+ | `clang-format` aimed at a `*.cs` file | **Blocked** — clang-format applies the C# rule and can empty the file |
80
+
81
+ ## Costs: what is fast, and what is opt-in
82
+
83
+ | Operation | Time |
84
+ | --- | --- |
85
+ | Cached lookup (symbol, project facts) | ~0.3 s |
86
+ | First project/plugin index (≈2,400 files) | seconds |
87
+ | Engine `Runtime` index — cold | ~1 minute |
88
+ | Engine `Runtime` index — warm | ~6 s |
89
+ | UnrealBuildTool Query facts | ~7 s, then cached |
90
+ | clangd compile database (`unreal_index` → `compiledb`) | minutes, ~1 GB for the engine scope |
91
+
92
+ Because of that model:
93
+
94
+ - Symbol lookups never stall. If a question needs engine sources and no engine index exists, the
95
+ agent reports the cost and how to pay it, and asks you. Say **yes** once per machine —
96
+ `/unreal-cache stats` shows what you have. Engine scans default to `engineAreas: ["Runtime"]`,
97
+ which is enough for gameplay base classes (`AActor`, `UAnimInstance`, `USceneComponent`);
98
+ `Editor` and `Developer` are opt-in and roughly triple the scan.
99
+ - Ask for `unreal_index` with `action: "build"`, `scope: "engine"` when you want engine-wide
100
+ navigation, and `action: "compiledb"` when you want clangd's references/call hierarchy.
101
+ - After a branch switch, a rebase, adding a plugin, or an engine version change, say "rebuild the
102
+ Unreal index" (that is `refresh: true`).
103
+ - Everything lives in `~/.pi/agent/unreal-cache` (override with `$PI_UNREAL_CACHE_DIR`) — never
104
+ inside your project tree or your engine installation. The compile database encodes this
105
+ machine's absolute paths, so it is machine-local by design.
106
+
107
+ ## Requirements
108
+
109
+ - **Windows**, with **PowerShell 7** (`pwsh`) for registry-based engine discovery. Without it the
110
+ extension still works: resolution falls back to `UNREAL_ENGINE` / `UE_ROOT` /
111
+ `PI_UNREAL_ENGINE_ROOT`, the Launcher's install list, and a scan of
112
+ `C:/Program Files/Epic Games`.
113
+ - An Unreal Engine installation the project can resolve to (Launcher install or source build).
114
+ Descriptor field handling is transcribed from UE 5.8.
115
+ - Nothing else. No compiler, no IDE language service. clangd is optional enrichment for
116
+ `compiledb` and the `clangd --check` acceptance step.
117
+
118
+ ## Troubleshooting
119
+
120
+ | Report | What to do |
121
+ | --- | --- |
122
+ | `engine: AMBIGUOUS — more than one valid installation matches` | Two roots match the association. Pass the one you want as `engineRootHint` (e.g. `engineRootHint: "C:/Program Files/Epic Games/UE_5.8"`). |
123
+ | `engine: UNRESOLVED` with rejected candidates | Read the reasons — a stale registry key, a missing `Build.version`, a version mismatch. The report lists remediation steps; the extension never writes registry keys for you. |
124
+ | `no .uproject within 4 levels above …` | The search walks up from the working directory. Pass the project explicitly: `project: "C:/Work/Lp/Lp.uproject"` (a directory containing one works too). |
125
+ | `unreal_symbol` finds nothing in scope `engine` | Build the engine index first (`unreal_index` → `action: "build"`, `scope: "engine"`). |
126
+ | Results look stale after a rebase or engine update | Re-run with `refresh: true`, or `/unreal-cache clear`. |
127
+ | `no plugin named "X" in the index` | That reference is genuinely not on disk. `/unreal` shows which roots were scanned. |
128
+
129
+ ## What it deliberately does not do
130
+
131
+ - It does not install, register, migrate, or "fix" an engine, and never writes registry keys.
132
+ - It does not build, cook, package, launch the editor, or edit `.ini` files.
133
+ - It does not hand-edit assets, and blocks attempts to.
134
+ - Symbol lookup reports **declarations**. Type resolution, overloads, call sites, inheritance and
135
+ diagnostics are clangd's job when clangd is installed.
136
+ - `.ini` provenance, live-coding state, and asset metadata are out of scope for v1.
137
+
138
+ ## Install
139
+
140
+ Published as **`@aeonsparx/pi-unreal`** on npmjs.
141
+
142
+ ```bash
143
+ pi install npm:@aeonsparx/pi-unreal@1.0.0 # pinned version, reproducible
144
+ pi install git:github.com/Aeonsparx-Interactive/pi-unreal@v1.0 # follows patch releases on the v1.0 line
145
+ ```
146
+
147
+ The `npm:` route installs from the registry and pins an exact version. The `git:` route clones the
148
+ repository and checks out the ref: a line alias (`@v1.0`) is a moving tag that patch releases
149
+ force-move, so `pi` picks up patches on update; pin `@v1.0.2` for a fixed snapshot. Loading straight
150
+ from a clone still works and needs no install: `pi -e ./extensions/unreal/index.ts`.
151
+
152
+ ## Releasing (maintainers)
153
+
154
+ | `package.json` bump | GitHub Release | npmjs |
155
+ | --- | --- | --- |
156
+ | `major` / `minor` | **new** release `v<major>.<minor>` | staged `<major>.<minor>.<patch>` |
157
+ | `patch` | **updates** the existing `v<major>.<minor>` release | staged `<major>.<minor>.<patch>` |
158
+
159
+ Merge a PR that bumps `version` into `main`. `.github/workflows/release.yml` runs `bun test` on
160
+ Linux and Windows, pushes the immutable tag `v<major>.<minor>.<patch>`, moves the `v<major>.<minor>`
161
+ alias, creates or updates that release, then stages the tarball with `npm stage publish`. **Nothing
162
+ is public until a human approves it** (2FA, from any machine):
163
+
164
+ ```bash
165
+ npm stage list @aeonsparx/pi-unreal # stage ids (no 2FA)
166
+ npm stage view <stage-id> # metadata
167
+ npm stage download <stage-id> # inspect the exact tarball that will publish
168
+ npm stage approve <stage-id> # 2FA - this is the publish act
169
+ npm stage reject <stage-id> # 2FA - frees the staged version number
170
+ ```
171
+
172
+ The same queue is on npmjs.com (Staged Packages tab). `npm stage` requires npm >= 11.15.0
173
+ (`npm install -g npm@12`) and Node >= 22.14.0. `NPM_TOKEN` (repository secret) is a Granular Access
174
+ Token with Read+Write and **no** 2FA bypass: CI can stage, only you can publish. Staged versions
175
+ occupy their version number, so a rejected stage burns `x.y.z` until `npm stage reject` frees it.
176
+ Provenance is deliberately off in v1 (`--provenance` needs a Provenance-scope token, and the
177
+ attestation path through stage-then-approve is unverified).
178
+
179
+ ## Developers
180
+
181
+ ```bash
182
+ bun test # 66 tests: resolver, descriptors, plugins, cache, gate, scanner, index, compile-db pipeline, fixture hygiene
183
+ bun run typecheck # tsc --noEmit
184
+ bun test/live-check.ts "C:/path/to/Project.uproject" # live machine smoke
185
+ bun test/keys.census.mjs "C:/Program Files/Epic Games/UE_5.8/Engine/Plugins"
186
+ bun run tmp/smoke.mjs project "C:/path/to/Project" # symbol index against a real project
187
+ bun run tmp/smoke.mjs query "C:/path/to/Project" # UnrealBuildTool Query round trip
188
+ bun run tmp/smoke.mjs engine "C:/path/to/Project" # Engine/Source index (minutes, opt-in)
189
+ bun run tmp/smoke.mjs compiledb "C:/path/to/Project" # clangd database + `clangd --check` acceptance
190
+ ```
191
+
192
+ From a clone, load the extension into a session without installing it:
193
+
194
+ ```bash
195
+ pi -e ./extensions/unreal/index.ts
196
+ ```
197
+
198
+ Layout: `extensions/unreal/index.ts` (tools, commands, context card, convention gate),
199
+ `extensions/unreal/lib/` (resolver, descriptors, plugin index, symbol index, UBT Query, compile
200
+ database), `prompts/unreal-context.md`, `skills/ue-engine-resolution/`, `test/`.
201
+
202
+ Design notes and measured internals — engine resolution algorithm, descriptor fidelity, plugin
203
+ discovery, the index and compile-database pipeline — are in [docs/internals.md](docs/internals.md).
@@ -0,0 +1,184 @@
1
+ # pi-unreal internals
2
+
3
+ Design and implementation notes: how the tools derive their answers, and the measured numbers behind
4
+ the cost model. Read the README for day-to-day usage; this file is the engine-facing detail that
5
+ the README deliberately leaves out.
6
+
7
+ ## Symbol index (no compiler, no IDE)
8
+
9
+ `unreal_symbol`, `unreal_context`, and `unreal_index` answer navigation questions from two
10
+ compiler-free sources:
11
+
12
+ 1. **UnrealBuildTool Query** (`-Mode=Query`: `TargetDetails`, `TargetModuleReferences`) — the
13
+ engine's own record of targets, modules, module dependencies, include paths, and forced
14
+ includes. Measured on UE 5.8: 6.0 s for 1,079 modules, 2.1 s for the dependency graph, and it
15
+ needs no compiler. That is why MSVC-only machines (Visual Studio, Rider) are first class here:
16
+ Visual Studio's `Browse.VC.db` is encrypted with no public API, Rider's resolver is in-process
17
+ through RiderLink, and both derive their context from the same UBT rules this layer reads.
18
+ 2. **A regex definition scanner** over the project, its enabled plugins, and the engine areas its
19
+ modules actually depend on — `class`/`struct`/`enum`/`union`, `UFUNCTION`, `UPROPERTY`,
20
+ delegates, aliases, with `Owner:Member` qualified names and forward declarations marked.
21
+
22
+ Artifacts live in `$PI_UNREAL_CACHE_DIR/indexes/<key>/` (`meta.json`, `defs.tsv`, `files.tsv`,
23
+ plus `includes.tsv` in `full` mode): plain TSV — greppable, inspectable, shared across sessions
24
+ and projects — and never inside the project tree or the engine installation.
25
+
26
+ Measured on this box (UE 5.8.3 installed engine; Lp project with 25 enabled plugins):
27
+
28
+ | Scope | Files | Definitions | Cold build |
29
+ | --- | --- | --- | --- |
30
+ | Project + plugins | 2,405 | 38,901 | 6.7 s |
31
+ | Engine `Runtime` (shipped default: 279 module roots) | 18,566 | 436,729 | 57.6 s cold, 5.7 s warm |
32
+ | Engine `Runtime/Core` + `Runtime/CoreUObject` | 3,094 | 70,548 | 16 s |
33
+ | Cached lookup | — | — | ~0.3 s |
34
+
35
+ Cost discipline is the design. A lookup builds the project index (seconds) and uses an engine
36
+ index only if one exists; `scope: "engine"` on a cold machine returns
37
+ `engine index not built — build it with unreal_index (action: "build", scope: "engine")` instead of
38
+ spending minutes on a scan nobody asked for. `unreal_index` is the explicit, paid path.
39
+
40
+ Ranking inside `unreal_symbol`: exact (0) → qualified member (1) → prefix (2) → substring (3).
41
+ Forward declarations rank last, so `unreal_symbol AActor` returns
42
+ `Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h:282` (the `UCLASS` declaration), not
43
+ the `class AActor;` line in `PhysicsCollision.h`.
44
+ `unreal_symbol LpGameModeBase` finds `ALpGameModeBase` (the UE prefix is added for you); `unreal_symbol FLpMovementDirectionThresholds:F` finds a single-letter `UPROPERTY` field; `unreal_context LpCharacterTypes.h` resolves a bare file name through the index. Engine areas are derived from module
45
+ dependencies (`.uproject` modules → recursive `PublicDependencyModuleNames` → plugin-supplied
46
+ modules → `Engine/Source/<area>/*`), so the index contains what this project can compile against,
47
+ not the whole engine.
48
+
49
+ One engine fact worth knowing: UBT Query reports `"defines": []` for every module. The effective
50
+ preprocessor set lives in a forced include —
51
+ `Intermediate/Build/<Platform>/<Config>/<Target>/Development/<Module>/Definitions.<Module>.h` —
52
+ and `unreal_context` reads that file instead of guessing `WITH_*` values. Query is also never
53
+ run with `-NoIntellisense`: that flag produces a syntactically valid skeleton with an empty
54
+ `moduleToCompileSettings`, and `payloadOk()` rejects it.
55
+
56
+ ## clangd enrichment (`unreal_index` action `compiledb`)
57
+
58
+ The definition index is the compiler-free path. When you want clangd's semantic power (references,
59
+ call hierarchy, diagnostics), the database it needs comes from the engine: `UnrealBuildTool
60
+ -Modes=GenerateClangDatabase`, normalized in the pi-unreal cache.
61
+
62
+ The UnrealBuildTool invocation is the part nobody documents, so it is encoded in one place
63
+ (`lib/compiledb.ts`):
64
+
65
+ - The target is **three positional tokens** — `Win64 Development LpEditor`. `TargetDescriptor` only
66
+ inspects arguments that do not start with `-`, and its `+` split requires every token to be the
67
+ same kind, so `-Target=LpEditor` fails with `No platforms specified for target` and
68
+ `Win64+Development+LpEditor` fails with `The platform name Development is not a valid platform
69
+ name`. `-Platform=`/`-Configuration=` set `BuildConfiguration` only and produce a duplicate
70
+ platform error next to the positional tokens.
71
+ - `-OutputDir=` must already exist (UnrealBuildTool throws
72
+ `DirectoryNotFoundException: Could not find a part of the path ...\compile_commands.json`), and
73
+ the mode's own `-NoPCH -NoVFS -Compiler=Clang` additions mean the emitted driver is
74
+ `clang-cl.exe`, not `cl.exe`.
75
+ - `-Include=` rules match nothing useful (measured: `Source/Lp`, `*/Source/Lp/*`,
76
+ `C:/Users/.../Source/Lp/**` all yield an empty database), so scoping happens after generation.
77
+
78
+ Normalization is Epic's `stripPostPch.py` recipe, ported: expand the per-module `@file.rsp`
79
+ response files, drop `/Yu* /Fm* /Fe* /Fp*` and everything after `/link`, drop PCH translation
80
+ units (`SharedPCH.*.h.cpp`, `PCH.*.h.cpp`) and header entries, then keep the requested scope. The
81
+ database is written to `$PI_UNREAL_CACHE_DIR/compiledb/<target>.<platform>.<config>/` — it encodes
82
+ this machine's absolute paths and toolchain version, so it is a machine artifact, never a repo one.
83
+
84
+ `check: "<file>"` runs `clangd --check` against the result, which is the acceptance test. Measured
85
+ on Lp (UE 5.8.3, installed engine, VS 18 LLVM clangd): UnrealBuildTool 1.5-1.7 s, 4 entries,
86
+ 4 response files expanded, 0 switches dropped; `clangd --check` built a 62 MB preamble and
87
+ reported **`All checks completed, 0 errors`** in ~2.6 s.
88
+
89
+ The honest limits of that 4-entry number: an installed (precompiled) engine contributes no compile
90
+ actions, so the database covers the project's own translation units, not `Engine/Source`. Engine
91
+ files still work under clangd because the project's `.clangd` supplies the include paths — exactly
92
+ what Epic's Unreal-clangd VS Code extension does (its generated `Lp.0.rsp` contains only `/FI`
93
+ plus 523 `/I` lines, and `compile_commands.json` there has 6 project entries and no engine entry).
94
+ Re-run with `refresh: true` after changing `Build.cs` files, adding modules, or switching engine
95
+ version.
96
+
97
+ ## How engine resolution works
98
+
99
+ Mirrors `FDesktopPlatformWindows::GetAvailableEngineDescriptions`
100
+ (`Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp:459-511`) and
101
+ `VerifyEngineGuid` (`DesktopPlatformWindows.cpp:615-664`):
102
+
103
+ | `.uproject` association | Source of truth |
104
+ | --- | --- |
105
+ | engine-root path | used directly, validated |
106
+ | `5.8` (Launcher install) | `HKLM:\SOFTWARE\EpicGames\Unreal Engine\<ver>` → `InstalledDirectory`, then `<installDirDefault>\UE_<ver>` |
107
+ | `{GUID}` (source build) | `HKCU:\SOFTWARE\Epic Games\Unreal Engine\Builds` |
108
+ | empty | walk up 12 levels looking for `Engine/Config/BaseEngine.ini` |
109
+
110
+ Below the association: `LauncherInstalled.dat`, `HKLM` versions, `UNREAL_ENGINE` / `UE_ROOT` /
111
+ `PI_UNREAL_ENGINE_ROOT`, and a scan of `C:/Program Files/Epic Games` for `UE_*`. Explicit
112
+ `engineRootHint` outranks every registry source.
113
+
114
+ Every candidate is validated before use — `Engine/Binaries` + `Engine/Build`
115
+ (`DesktopPlatformBase.cpp:401-421`) **and** `Engine/Build/Build.version` matching the
116
+ association. Stale registry keys and env pointers are reported as rejected candidates with the
117
+ reason, never silently dropped. One survivor → `resolved`; several → `ambiguous`; none →
118
+ `unresolved` + remediation. Nothing is ever written back to the registry.
119
+
120
+ Registry reads run through one memoized `pwsh -NoProfile` snapshot (8 s timeout). If `pwsh` is
121
+ missing the snapshot degrades to `available: false` and the resolver continues with
122
+ file-based sources — the extension never fails at import time.
123
+
124
+ ## Plugin discovery
125
+
126
+ Mirrors `UnrealBuildTool.System.ReadAvailablePlugins` (`System/Plugins.cs:322`):
127
+ Engine `Engine/Plugins` → Project `Plugins/` → Mod `Mods/` → `AdditionalPluginDirectories`,
128
+ with later scans shadowing earlier ones on name collision. `Binaries/`, `Intermediate/`,
129
+ `Saved/`, `.git/`, `ThirdParty/` are skipped. Reference semantics mirror `PluginManager`:
130
+ `Enabled` / `Optional` / `Activate` (`PluginReferenceDescriptor.cpp:132-172`); an engine-version
131
+ mismatch is a warning, not an error (`PluginManager.cpp:2690-2712`).
132
+
133
+ Indexes are cached on disk keyed by the mtime of every scanned directory plus the engine
134
+ changelist, with a 10-minute backstop (`MAX_AGE_MS`) because content-only `.uplugin` edits do
135
+ not move the parent directory's mtime.
136
+
137
+ ## Descriptor fidelity
138
+
139
+ Field names, host types, and loading phases are transcribed from the engine's serializers
140
+ (UE 5.8): `ProjectDescriptor.cpp:100-225`, `PluginDescriptor.cpp:263-409`,
141
+ `ModuleDescriptor.cpp`, `PluginReferenceDescriptor.cpp:130-180`, cross-checked against 897
142
+ `.uplugin` files in UE 5.8 `Engine/Plugins`. Consequences:
143
+
144
+ - `.uproject` / `.uplugin` are parsed as JSONC (comments and trailing commas are legal in the
145
+ engine's reader).
146
+ - `Modules` is an array of objects keyed by `Name`; `Type` is required
147
+ (`Found Module entry 'X' with a missing 'Type' field`), `LoadingPhase` defaults to `Default`.
148
+ - A plugin reference without `"Enabled"` is rejected by UE 5.8 — reported, not guessed.
149
+ - Unknown keys are reported as `… is not serialized by FPluginDescriptor`, and the known-key
150
+ set is wide enough (including legacy `Whitelist*`/`Blacklist*` and pre-5.x keys) that genuine
151
+ engine files produce no noise.
152
+ - `.uplugin` has no `Enabled` key; enablement is `EnabledByDefault` + the project reference.
153
+ - `IsBetaVersion` is metadata in UE 5.8 (no engine/UBT beta gate exists).
154
+
155
+ ## Known limits
156
+
157
+ - Registry access needs `pwsh` (PowerShell 7). Without it, registry sources are unavailable and
158
+ resolution relies on env, directory scans, and inline associations.
159
+ - `unreal_resolve_engine` reports what the machine can prove. It does not install, register, or
160
+ migrate engines, and never writes registry keys (the engine's `RegisterEngineInstallation`
161
+ does; this package deliberately does not).
162
+ - v1 is inspection only. `.ini` provenance is out of scope.
163
+ - The definition index is a scanner, not a parser: it finds declarations (`class`, `struct`,
164
+ `enum`, `union`, `UFUNCTION`/`UPROPERTY`/`UCLASS`-marked members, delegates, aliases) and
165
+ records `Owner:Member` qualified names. It does not resolve types, overloads, call sites, or
166
+ inheritance — that is what clangd is for when it is installed. Macro-heavy code can still be
167
+ misread: the scanner is regex-based, so a definition hidden inside an unusual macro spelling may
168
+ be missed, and `#if` branches are all recorded (a symbol declared in a disabled branch is a
169
+ candidate, not a guarantee).
170
+ - `moduleRefs` and `includePaths` come from UnrealBuildTool Query, which needs a buildable
171
+ target for the project; when Query fails the index still builds and says so.
172
+ - The engine index is never built implicitly: scans are opt-in through `unreal_index`.
173
+ - `action: "compiledb"` needs UnrealBuildTool (so an engine installation) and, for the
174
+ `clangd --check` acceptance step, a clangd binary — `CLANGD_PATH`, the Visual Studio LLVM
175
+ toolset, the WinGet LLVM package, or `C:/Program Files/LLVM/bin`. Without clangd the database is
176
+ still produced and reported; only the check is skipped.
177
+ - On an installed engine the compile database covers the project's translation units (engine
178
+ modules are precompiled and contribute no compile actions). The engine-wide scope of
179
+ `project+engine` is the source-built case, and there it is a ~1 GB artifact taking minutes.
180
+ - Engine area selection is a cost dial, not a completeness claim. `DEFAULT_ENGINE_AREAS =
181
+ ["Runtime"]` covers every gameplay type (`AActor`, `UAnimInstance`, `USceneComponent`) at
182
+ 18,566 files; `Editor` and `Developer` are opt-in via `engineAreas` and roughly triple the scan.
183
+ - Index reuse requires the *same root set*: an index built over two modules is never used to answer
184
+ a `Runtime`-wide query. Partial coverage reported as an answer is worse than a rebuild.