---
name: asset-management
display_name: Asset Discovery & Management
description: Search, find, open, move, duplicate, save, delete, import, and export assets
vibeue_classes:
  - AssetDiscoveryService
unreal_classes:
  - EditorAssetLibrary
---

# Asset Discovery & Management Skill

## Critical Rules

### ⚠️ `search_assets` Does NOT Search Engine Content

`search_assets(term, type)` **only searches `/Game/` content**. There is NO third parameter.

```python
# WRONG - will error (only 2 params exist)
assets = unreal.AssetDiscoveryService.search_assets("Cube", "", True)

# CORRECT - use list_assets_in_path for engine content
engine_assets = unreal.AssetDiscoveryService.list_assets_in_path("/Engine")
cubes = [a for a in engine_assets if "Cube" in str(a.asset_name)]
```

### ⚠️ UE 5.7 Property Changes

| WRONG (old) | CORRECT (UE 5.7) |
|-------------|------------------|
| `asset.name` | `asset.asset_name` |
| `asset.path` | `asset.package_path` |
| `asset.asset_class` | `asset.asset_class_path` |

### ⚠️ Importing Image Files From Disk — Use the Built-in Importer

To bring an image file from disk into the Content Browser, use the **`manage_asset` import action**
(or `AssetDiscoveryService.import_asset`). Do **NOT** call `unreal.AssetToolsHelpers...import_asset_tasks`
or `ImportAssets` from `execute_python_code` — those pump the game-thread task graph and trip a
`RecursionGuard` assertion that **crashes the editor** when run from inside a tool call.

```python
# PREFERRED — MCP tool (robust, no crash)
# manage_asset(action="import",
#              source_file_path="C:/Images/rocks.jpg",
#              destination_path="/Game/UI/Textures",
#              asset_name="T_Rocks")

# Python equivalent (same safe C++ path)
import unreal
path, err = unreal.AssetDiscoveryService.import_asset(
    "C:/Images/rocks.jpg", "/Game/UI/Textures", "T_Rocks")
print(path or err)

# WRONG — crashes the editor (TaskGraph RecursionGuard assertion)
# tasks = [unreal.AssetImportTask()]; unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks)
```

Supported image formats: png, jpg, jpeg, bmp, tga, dds, exr, hdr, tiff, tif, psd, pcx.

### ⚠️ Asset Paths Must Be Content Browser Paths

Use `/Game/...` paths, **not** file system paths.

```python
# WRONG - file system path
asset = unreal.AssetDiscoveryService.find_asset_by_path("C:/Projects/Content/BP_Player.uasset")

# CORRECT - content browser path
asset = unreal.AssetDiscoveryService.find_asset_by_path("/Game/Blueprints/BP_Player")
```

### ⚠️ Never Emulate Move/Rename with Duplicate + Delete

Duplicating creates a new asset identity. References stay pointed at the original asset, so deleting the original after a duplicate can break those references.

Use a real move instead:

```python
import unreal

unreal.AssetDiscoveryService.move_asset(
    "/Game/StateTree/STT_Rotate",
    "/Game/StateTree/Tasks/STT_Rotate"
)

# MCP equivalent
# manage_asset(action="move", source_path="/Game/StateTree/STT_Rotate", destination_path="/Game/StateTree/Tasks/STT_Rotate")
```

### ⚠️ Methods NOT Available

| Does NOT Exist | Alternative |
|----------------|-------------|
| `asset_exists()` | `find_asset_by_path()` → check for None |
| `is_asset_in_use()` | `get_asset_referencers()` → check if empty |
| `rename_asset()` | `move_asset(old_path, new_path)` |
| `export_asset()` | `export_texture()` for textures only |

---

## Workflows

### Search Pattern

```python
import unreal

# By name (partial match) - ONLY searches /Game/ content
results = unreal.AssetDiscoveryService.search_assets("Player", "Blueprint")
for asset in results:
    print(f"{asset.asset_name}: {asset.package_path}")

# By folder
assets = unreal.AssetDiscoveryService.list_assets_in_path("/Game/Blueprints")

# By type only
all_bps = unreal.AssetDiscoveryService.get_assets_by_type("Blueprint")
```

### Search Engine Content

```python
import unreal

# Search engine content (e.g., for built-in meshes like Cube)
engine_assets = unreal.AssetDiscoveryService.list_assets_in_path("/Engine")
cubes = [a for a in engine_assets if "Cube" in str(a.asset_name)]

# Filter by type
meshes = unreal.AssetDiscoveryService.list_assets_in_path("/Engine", "StaticMesh")
```

### Check Exists Pattern

```python
import unreal

asset = unreal.AssetDiscoveryService.find_asset_by_path("/Game/BP_Player")
if asset:
    print(f"Found: {asset.asset_name}")
else:
    print("Not found - create it")
```

### Duplicate Pattern

```python
import unreal

success = unreal.AssetDiscoveryService.duplicate_asset("/Game/BP_Enemy", "/Game/BP_EnemyBoss")
if success:
    print("Duplicated")
```

### Move Pattern

```python
import unreal

success = unreal.AssetDiscoveryService.move_asset(
    "/Game/StateTree/STT_Rotate",
    "/Game/StateTree/Tasks/STT_Rotate"
)
if success:
    print("Moved without breaking references")
```

### Save Pattern

```python
import unreal

# Save specific asset
unreal.AssetDiscoveryService.save_asset("/Game/BP_Player")

# Save all dirty assets
count = unreal.AssetDiscoveryService.save_all_assets()
print(f"Saved {count} assets")
```

### Check References Before Delete

```python
import unreal

refs = unreal.AssetDiscoveryService.get_asset_referencers("/Game/SM_Weapon")
if len(refs) == 0:
    unreal.AssetDiscoveryService.delete_asset("/Game/SM_Weapon")
else:
    print(f"In use by {len(refs)} assets")
```

### Import/Export Textures

```python
import unreal

# Import (disk → Content Browser). Returns (asset_path, error); asset_path is "" on failure.
# Pass a destination FOLDER + optional asset name.
path, err = unreal.AssetDiscoveryService.import_asset(
    "C:/Textures/logo.png", "/Game/Textures", "T_Logo")
print(path or err)

# import_texture(src, dest_asset_path) still works (takes a full asset path) and now uses the
# same crash-safe importer under the hood:
unreal.AssetDiscoveryService.import_texture("C:/Textures/logo.png", "/Game/Textures/T_Logo")

# Export (project → file system)
unreal.AssetDiscoveryService.export_texture("/Game/Textures/T_Logo", "C:/Exports/logo.png")
```

> Prefer the `manage_asset` import action in tool flows:
> `manage_asset(action="import", source_file_path="C:/Textures/logo.png", destination_path="/Game/Textures", asset_name="T_Logo")`

### Editor State & Content Browser

```python
import unreal

# Get all open assets
open_assets = unreal.AssetDiscoveryService.get_open_assets()
print(f"Editing {len(open_assets)} assets")

# Check if specific asset is open
if unreal.AssetDiscoveryService.is_asset_open("/Game/BP_Player"):
    print("BP_Player is open")

# Get selected assets in Content Browser
selected = unreal.AssetDiscoveryService.get_content_browser_selections()
for asset in selected:
    print(asset.asset_name)

# Get primary selection
asset = unreal.AssetData()
if unreal.AssetDiscoveryService.get_primary_content_browser_selection(asset):
    print(f"Selected: {asset.asset_name}")
```

### Create Non-Standard Asset Types (Factory Pattern)

Assets not covered by VibeUE services (e.g., `LandscapeGrassType`) require `AssetToolsHelpers` + a factory:

```python
import unreal

asset_tools = unreal.AssetToolsHelpers.get_asset_tools()

# LandscapeGrassType
factory = unreal.LandscapeGrassTypeFactory()
lgt = asset_tools.create_asset("LGT_MyGrass", "/Game/Landscape", unreal.LandscapeGrassType, factory)

# After creation, set properties via set_editor_property, then save
unreal.EditorAssetLibrary.save_asset("/Game/Landscape/LGT_MyGrass")
```

> **Tip:** Use `discover_python_module("unreal", name_filter="Factory")` to find available factories for other asset types.

### ⚠️ search_assets May Not Find All Asset Types

`search_assets(term, type)` searches by name/type but may return empty for niche types like `LandscapeGrassType`. If search returns nothing:

1. Use `get_assets_by_type("LandscapeGrassType")` for type-specific queries
2. Use `list_assets_in_path("/Game/SomeFolder")` and filter in Python
3. Asset class names must match UE class names exactly (e.g., `LandscapeGrassType`, not `GrassType`)

### Common Asset Types for Filtering

- `Blueprint` - Blueprint classes
- `WidgetBlueprint` - UMG Widget Blueprints
- `Texture2D` - Texture assets
- `StaticMesh` - Static mesh assets
- `SkeletalMesh` - Skeletal mesh assets
- `Material` - Materials
- `MaterialInstanceConstant` - Material instances
- `DataTable` - Data Tables
- `PrimaryDataAsset` - Primary Data Assets
- `LandscapeGrassType` - Landscape grass/vegetation scatter types
- `LandscapeLayerInfoObject` - Landscape paint layer info
