@aeonsparx/pi-unreal
Read-only Pi extension that resolves Unreal Engine associations and project/plugin descriptors from engine-authored schemas instead of guesswork.
Package details
Install @aeonsparx/pi-unreal from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@aeonsparx/pi-unreal- Package
@aeonsparx/pi-unreal- Version
1.1.0- Published
- Oct 28, 2026
- Downloads
- 488/mo · 488/wk
- Author
- luqmanrizal-aeonsparx
- License
- MIT
- Types
- extension, skill, prompt
- Size
- 380.5 KB
- Dependencies
- 2 dependencies · 2 peers
Pi manifest JSON
{
"skills": [
"./skills"
],
"prompts": [
"./prompts"
],
"extensions": [
"./extensions/unreal/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-unreal
Unreal Engine awareness for the Pi coding agent.
Open a UE project in a pi session and the agent stops guessing. It knows which engine
installation that .uproject really resolves to, what the .uproject / .uplugin files actually
say, which plugins exist on disk, and where a C++ symbol is declared — with the file, the line and
the owning module. No compiler, no Visual Studio, no Rider, no clangd required.
It is read-only: it never writes to your project, never touches the Windows registry, never launches the editor, and blocks three kinds of edit that damage Unreal work.
Usage
Requirements
- Windows, with PowerShell 7 (
pwsh) for registry-based engine discovery. Without it the extension still works: resolution falls back toUNREAL_ENGINE/UE_ROOT/PI_UNREAL_ENGINE_ROOT, the Launcher's install list, and a scan ofC:/Program Files/Epic Games. - An Unreal Engine installation the project can resolve to (Launcher install or source build). Descriptor field handling is transcribed from UE 5.8.
- Nothing else. No compiler, no IDE language service. clangd is optional enrichment for
compiledband theclangd --checkacceptance step.
Installation
pi install npm:@aeonsparx/pi-unreal # optionally pin to a specific version, reproducible
pi install git:github.com/Aeonsparx-Interactive/pi-unreal@v1.1 # follows patch releases on the v1.1 line
The npm: route installs from the registry and pins an exact version. The git: route clones the
repository and checks out the ref: a line alias (@v1.1) is a moving tag that patch releases
force-move, so pi picks up patches on update; pin @v1.1.0 for a fixed snapshot. Loading straight
from a clone still works and needs no install: pi -e ./extensions/unreal/index.ts.
What you get in a session
Context is detected for you. When the session starts in a directory that contains a
.uproject — the working directory itself, or any of up to 3 parent directories — one note is
shown before the first answer: the resolved engine root and version, installed vs source build,
the project's modules, the number of declared plugin references, and the conventions below. Ask
for a re-check any time with /unreal.
Ask in plain English. "Which engine is this project on?", "Is MassGameplay available to this
project?", "Where is FLpMovementDirectionThresholds defined?", "What do I need to link to use
UAnimInstance?" The agent picks the matching tool:
| Tool | Question it answers |
|---|---|
unreal_resolve_engine |
"Which engine does this .uproject use — and how do you know?" |
unreal_project_inspect |
Modules, target platforms, and every plugin reference resolved against the plugins that exist on disk |
unreal_plugin_inspect |
One .uplugin as the engine parses it: modules, loading phases, version fields, plugin dependencies |
unreal_symbol |
"Where is this class / struct / function / UPROPERTY / delegate declared?" One line per hit: kind, file:line, module |
unreal_symbol_list |
"What does this project define?" Names by prefix — ULp, ANiMy, UAnim |
unreal_context |
"What does this file/module include, define, and depend on?" Include paths, dependency lists, the real WITH_* defines; for a module, also whether the target's module graph contains it and how the .uproject treats its plugin |
unreal_index |
Builds and reports the source index, the path-only header index ("does this header exist in this engine version?"), and the clangd compile database on request |
Slash commands you can type yourself:
| Command | Result |
|---|---|
/unreal [project] |
Engine, modules and plugin-reference report for this directory |
/unreal-engines |
Every engine installation this machine can prove, and how each was found |
/unreal-symbol <symbol> |
Definition lookup, project + plugins, then engine if an engine index exists |
/unreal-module <Module> |
Module directory, public/private dependency lists, defines file, C++ standard, SDK |
/unreal-cache stats|clear |
Inspect or clear the cached indexes |
Example — /unreal:
project C:/Work/Lp/Lp.uproject
engine: C:/Program Files/Epic Games/UE_5.8
version 5.8.3 | installed build | association "5.8" (version) via dir-scan
Build.version: C:/Program Files/Epic Games/UE_5.8/Engine/Build/Build.version
modules: Lp (Runtime), LpEditor (Editor)
plugins declared: 25
plugin index: 897 .uplugin (cache hit)
plugin references:
Mover: resolved ← Engine · modules Mover(Runtime), MoverCVDData(Runtime), MoverEditor(UncookedOnly)
MassGameplay: resolved ← Engine · modules MassEntity(Runtime), MassCore(Runtime), MassSimulation(Runtime), …
SomeLocalPlugin: resolved ← Project · modules SomeLocalPlugin(Runtime)
RetiredPlugin: missing
totals: 22 resolved, 3 missing of 25 references
engine plugins available with NO .uproject reference (EnabledByDefault true): GameplayCameras, IKRig
Example — unreal_symbol for an engine type (engine index already built):
unreal_symbol 'AActor' scope=engine: 1 definition(s)
class AActor C:/Program Files/Epic Games/UE_5.8/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h:282 [Engine · engine]
That is a definition line, not a grep dump: grepping a hot engine class name returns ~176 files of noise, which is exactly what burns a session's context.
Example — unreal_index action: "headers", the question a symbol index cannot answer:
unreal_index headers → hdr-UE_5.8-1a40c1c3d516
37,788 header paths from 1,934 roots (1,655 engine-plugin root(s), 279 engine-source root(s))
'MoverState.h': does NOT exist as a file path in this engine version
closest real file names (files, not types):
Engine/Plugins/Experimental/Mover/Source/Mover/Public/MoverTypes.h [Mover]
Engine/Plugins/Experimental/Mover/Source/Mover/Public/MoverSimulationTypes.h [Mover]
Engine/Plugins/Experimental/Mover/Source/Mover/Public/MovementMode.h [Mover]
Engine/Plugins/Experimental/Mover/Source/Mover/Public/MovementModeStateMachine.h [Mover]
MoverState.h is the kind of name an agent invents. The answer says it is not a file, lists real
files, and never presents a file name as a type. The same call on
PhysicsControlComponent.h returns the path plus plugin PhysicsControl [Engine] · EnabledByDefault false · .uproject reference NO — the file exists, the project does not reference the plugin, and
those are different facts.
Example — the coverage lines a 0-row answer prints (nothing here says "the engine has no such type"):
unreal_symbol 'PhysicsControlComponent' scope=engine: 0 definition(s)
· no definitions matched 'PhysicsControlComponent' in scope 'engine'
· scanned engine index idx-engine-defs-b8d82464af4a: 279 engine-source root(s), 62 engine-plugin root(s)
· engine plugins on disk: 897, referenced by this .uproject: 25; the engine index scans referenced plugins only
· a plugin the project does not reference is not in any index root: pass engineAreas ["Plugins"] (…2.8× the Runtime scan) or ["Plugins/Animation/PhysicsControl"]
· next: unreal_symbol query 'PhysicsControlComponent' scope "all" prefix true, or unreal_index action "headers" name "PhysicsControlComponent.h"
Guardrails (automatic, nothing to enable)
| Situation | What happens |
|---|---|
An edit targeting *.uasset / *.umap |
Blocked — those are cooked binary assets; author them in the running editor |
A C++ edit while UnrealEditor is running |
Warning — freshly compiled C++ will not load into a running editor; close it, then build |
clang-format aimed at a *.cs file |
Blocked — clang-format applies the C# rule and can empty the file |
Costs: what is fast, and what is opt-in
| Operation | Time |
|---|---|
| Cached lookup (symbol, project facts) | ~0.3 s |
| First project/plugin index (≈2,400 files) | seconds |
Engine Runtime index — cold |
~1 minute |
Engine Runtime index — warm |
~6 s |
| UnrealBuildTool Query facts | ~7 s, then cached |
clangd compile database (unreal_index → compiledb) |
minutes, ~1 GB for the engine scope |
Because of that model:
- Symbol lookups never stall. If a question needs engine sources and no engine index exists, the
agent reports the cost and how to pay it, and asks you. Say yes once per machine —
/unreal-cache statsshows what you have. Engine scans default toengineAreas: ["Runtime"], which is the engine's own gameplay code (AActor,UAnimInstance,USceneComponent);EditorandDeveloperare opt-in and roughly triple the scan. An engine-scope index always includes the plugin roots your.uprojectenables, soUMoverComponentand the PoseSearch types are in it. Types shipped by plugins you do not reference (PhysicsControl,BlendStack,StateTree,GameplayAbilities) needengineAreas: ["Plugins"]— everyEngine/Pluginssource tree, ~2.8× theRuntimescan — orengineAreas: ["Plugins/Animation/PhysicsControl"]for one plugin. - "Does this header exist in this engine version?" is a path question, not a symbol question:
unreal_indexwithaction: "headers"(andname: "MoverState.h") answers it in seconds from a path walk, and says which plugin owns a header it finds, whether that plugin isEnabledByDefault, and whether your.uprojectreferences it. Invented names come back as not a file path plus the closest real file names — never as types. - Ask for
unreal_indexwithaction: "build",scope: "engine"when you want engine-wide navigation, andaction: "compiledb"when you want clangd's references/call hierarchy. - After a branch switch, a rebase, adding a plugin, or an engine version change, say "rebuild the
Unreal index" (that is
refresh: true). - Everything lives in
~/.pi/agent/unreal-cache(override with$PI_UNREAL_CACHE_DIR) — never inside your project tree or your engine installation. The compile database encodes this machine's absolute paths, so it is machine-local by design.
Troubleshooting
| Report | What to do |
|---|---|
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"). |
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. |
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). |
unreal_symbol finds nothing in scope engine |
Read the coverage lines the answer prints: they name the root kinds that were scanned. A plugin your .uproject does not reference is in no index root — engineAreas: ["Plugins"] (or one plugin subtree), then rebuild. |
unreal_symbol finds nothing and you are not sure the name exists |
unreal_index → action: "headers", name: "ThatName.h": a seconds-long path answer with the closest real file names. |
| Results look stale after a rebase or engine update | Re-run with refresh: true, or /unreal-cache clear. |
no plugin named "X" in the index |
That reference is genuinely not on disk. /unreal shows which roots were scanned. |
What it deliberately does not do
- It does not install, register, migrate, or "fix" an engine, and never writes registry keys.
- It does not build, cook, package, launch the editor, or edit
.inifiles. - It does not hand-edit assets, and blocks attempts to.
- Symbol lookup reports declarations. Type resolution, overloads, call sites, inheritance and diagnostics are clangd's job when clangd is installed. An empty clangd answer is evidence: on an installed engine the compile database has no engine translation units, so "clangd found nothing" in engine code is a property of the setup, not a bug this package fixes.
- A 0-row answer is a statement about the roots that were scanned, never about the engine. The
answer names its index keys and root kinds;
unreal_index→action: "status"prints the same. - pi-unreal answers C++ declaration, module, include-path and dependency facts. It does not
answer asset questions — UENUM index order, UStruct field types, UCLASS/CDO instance values,
Blueprint graphs. Those need the
unreal-engineeditor MCP with the editor running. .iniprovenance, live-coding state, and asset metadata are out of scope for v1.