Skip to content
Back to skills

Cleaner

ASecurity

Clean up and audit a Unity project

  • 1,812 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 7, 2026
toolspythonrails

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add Besty0728/Unity-Skills --skill cleaner --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cleaner?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cleaner
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/besty0728-cleaner/badge)](https://www.skillsdirectory.com/skills/besty0728-cleaner)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: unity-cleaner
description: Clean up and audit a Unity project
---

> **Before calling any skill in this module:** if you are about to call a skill with parameters guessed from its name or description, STOP — read this file (or fetch its schema via `GET /skills/recommend?includeSchema=true`) first. If you already have the parameter definitions from recommend/schema, you may proceed straight to dryRun.

## Triggers
- Reducing project bloat
- Hunting duplicate or orphaned assets
- Auditing before a release
- 给项目瘦身、排查重复或孤立资源、发布前审计

# Unity Cleaner Skills

> **Safety**: `cleaner_delete_assets` uses a **two-step confirmToken handshake**. Call without `confirmToken` to preview; call again with the returned token (5-minute TTL) to actually delete. There is no `dryRun` parameter.

## Guardrails

**Operating Mode** (v1.9 three-tier):
- **Approval** (default): all analyze/query skills (`cleaner_find_unused_assets`, `cleaner_find_duplicates`, `cleaner_find_missing_references`, `cleaner_get_asset_usage`, `cleaner_find_empty_folders`, `cleaner_find_large_assets`, `cleaner_get_dependency_tree`) are SemiAuto — run directly.
- **Auto** / **Bypass**: SemiAuto and FullAuto run directly.
- Auto-forbidden in this module: `cleaner_delete_assets`, `cleaner_delete_empty_folders` (both carry `SkillOperation.Delete`). In Approval/Auto these return `MODE_FORBIDDEN` — they are reachable only under Bypass mode or via a user-managed Allowlist entry; the grant flow does **not** unlock them.
- `cleaner_fix_missing_scripts` is an `Execute | Modify` operation (it replaces missing component references with null in place, no asset deletion), so it **does not** trigger the NeverInSemi gate. In Auto / Bypass it runs directly; in Approval it still requires the standard grant handshake before execution.
- `cleaner_delete_assets` additionally uses a two-step `confirmToken` handshake even when the mode gate allows it — preview first (no token), then confirm with the returned token (5-minute TTL).

**DO NOT** (common hallucinations):
- `cleaner_delete` / `cleaner_remove` do not exist → cleaner skills only find/report; use `asset_delete` to actually remove
- `cleaner_fix` does not exist → use `cleaner_fix_missing_scripts` specifically for missing script references
- `cleaner_scan` / `cleaner_find_unused` do not exist → use specific skills: `cleaner_find_unused_assets`, `cleaner_find_duplicates`, `cleaner_find_missing_references`, `cleaner_find_empty_folders`, `cleaner_find_large_assets`

**Routing**:
- To delete found assets → use `asset` module's `asset_delete` / `asset_delete_batch`
- For project validation → use `validation` module

## Skills Overview

| Skill | Description |
|-------|-------------|
| `cleaner_find_unused_assets` | Find assets not referenced by others |
| `cleaner_find_duplicates` | Find duplicate files by content hash |
| `cleaner_find_missing_references` | Find missing scripts/asset references |
| `cleaner_delete_assets` | Delete assets via two-step confirmToken (preview → confirm) |
| `cleaner_get_asset_usage` | Find what references a specific asset |
| `cleaner_find_empty_folders` | Find empty folders in the project |
| `cleaner_find_large_assets` | Find largest assets by file size |
| `cleaner_delete_empty_folders` | Delete all empty folders |
| `cleaner_fix_missing_scripts` | Remove missing script components from GameObjects |
| `cleaner_get_dependency_tree` | Get dependency tree for an asset |

---

## Skills

### cleaner_find_unused_assets
Find potentially unused assets of a specific type.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `assetType` | string | No | "Material" | Asset type filter |
| `searchPath` | string | No | "Assets" | Search path |
| `limit` | int | No | 100 | Max results |

**Returns**: `{success, assetType, potentiallyUnusedCount, assets: [{path, name, type, sizeBytes}]}`

```python
# Find unused materials
result = call_skill("cleaner_find_unused_assets", assetType="Material")

# Find unused textures in a specific folder
result = call_skill("cleaner_find_unused_assets", 
    assetType="Texture2D", searchPath="Assets/Textures")
```

### cleaner_find_duplicates
Find duplicate files by MD5 hash.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `assetType` | string | No | "Texture2D" | Asset type |
| `searchPath` | string | No | "Assets" | Search path |
| `limit` | int | No | 50 | Max groups |

**Returns**: `{success, duplicateGroupCount, totalWastedBytes, totalWastedMB, groups: [{count, sizeBytes, wastedBytes, files}], skipped: [{path, reason}]}` — `skipped` lists files that could not be read for hashing, so they are not silently missing from the groups.

```python
# Find duplicate textures
result = call_skill("cleaner_find_duplicates", assetType="Texture2D")
print(f"Wasted space: {result['totalWastedMB']:.2f} MB")
```

### cleaner_find_missing_references
Find components with missing scripts or null references.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `includeInactive` | bool | No | true | Include inactive objects |

**Returns**: `{success, issueCount, missingScripts, missingReferences, issues: [{type, gameObject, path, ...}]}`

```python
# Scan for all missing references
result = call_skill("cleaner_find_missing_references")
print(f"Missing scripts: {result['missingScripts']}")
print(f"Missing references: {result['missingReferences']}")
```

### cleaner_delete_assets
Delete specified assets with **two-step confirmation**.

> ⚠️ **Safety First**: Deletion requires TWO calls - first preview, then confirm.

**Step 1 - Preview** (no confirmToken):

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `paths` | string[] | No* | Asset paths to delete |

*Required for this preview step; omitted in Step 2, which deletes exactly the paths already captured by `confirmToken`.

**Returns**: `{action: "preview", confirmToken, assetsToDelete, message}`

**Step 2 - Confirm** (with confirmToken):

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `confirmToken` | string | Yes | Token from preview step |

**Returns**: `{action: "deleted", deletedCount, results}`

```python
# Step 1: Preview what will be deleted
preview = call_skill("cleaner_delete_assets", 
    paths=["Assets/Unused/mat1.mat", "Assets/Unused/mat2.mat"])
print(preview['message'])  # Shows: "⚠️ PREVIEW ONLY - 2 assets will be deleted..."
print(preview['assetsToDelete'])  # Full list with sizes

# Step 2: Confirm deletion using token (expires in 5 minutes)
if input("Proceed? (y/n): ") == 'y':
    result = call_skill("cleaner_delete_assets", 
        confirmToken=preview['confirmToken'])
    print(result['message'])  # "Successfully deleted 2 assets"
```

### cleaner_get_asset_usage
Find what objects reference a specific asset.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `assetPath` | string | Yes | Asset path |
| `limit` | int | No | Max results (default 50) |

**Returns**: `{success, asset, usedByCount, usedBy: [{path, name, type}]}`

```python
# Check what uses a texture
result = call_skill("cleaner_get_asset_usage",
    assetPath="Assets/Textures/player.png")
```

### `cleaner_find_empty_folders`
Find empty folders in the project.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchPath` | string | No | "Assets" | Search path |

**Returns:** `{ success, count, folders }`

```python
# Find empty folders
result = call_skill("cleaner_find_empty_folders")
print(f"Found {result['count']} empty folders")
```

### `cleaner_find_large_assets`
Find largest assets by file size.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchPath` | string | No | "Assets" | Search path |
| `limit` | int | No | 20 | Max results |
| `minSizeBytes` | long | No | 0 | Minimum file size in bytes |

**Returns:** `{ success, count, assets: [{ path, sizeBytes, sizeMB }] }`

```python
# Find top 10 largest assets over 1 MB
result = call_skill("cleaner_find_large_assets", limit=10, minSizeBytes=1048576)
for a in result['assets']:
    print(f"{a['sizeMB']:.2f} MB - {a['path']}")
```

### `cleaner_delete_empty_folders`
Delete all empty folders.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchPath` | string | No | "Assets" | Search path |

**Returns:** `{ success, deleted, total }`

```python
# Delete all empty folders
result = call_skill("cleaner_delete_empty_folders")
print(f"Deleted {result['deleted']} of {result['total']} empty folders")
```

### `cleaner_fix_missing_scripts`
Remove missing script components from GameObjects.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `includeInactive` | bool | No | true | Include inactive objects |

**Returns:** `{ success, removedComponents }`

```python
# Remove all missing script components
result = call_skill("cleaner_fix_missing_scripts")
print(f"Removed {result['removedComponents']} missing script components")
```

### `cleaner_get_dependency_tree`
Get dependency tree for an asset.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `assetPath` | string | Yes | - | Asset path |
| `recursive` | bool | No | true | Recursively resolve dependencies |

**Returns:** `{ success, assetPath, dependencyCount, dependencies: [{ path, type }] }`

```python
# Get full dependency tree for a prefab
result = call_skill("cleaner_get_dependency_tree",
    assetPath="Assets/Prefabs/Player.prefab")
print(f"Dependencies: {result['dependencyCount']}")
```

---

## Example Workflow: Clean Project

```python
import unity_skills

# 1. Find all missing references and fix them
missing = unity_skills.call_skill("cleaner_find_missing_references")
for issue in missing['issues']:
    if issue['type'] == 'MissingScript':
        print(f"⚠️ Missing script on: {issue['path']}")

# 2. Find duplicate textures
dupes = unity_skills.call_skill("cleaner_find_duplicates", assetType="Texture2D")
if dupes['totalWastedMB'] > 10:
    print(f"🗑️ {dupes['totalWastedMB']:.1f} MB wasted on duplicates")

# 3. Find unused materials
unused = unity_skills.call_skill("cleaner_find_unused_assets", assetType="Material")
print(f"📦 {unused['potentiallyUnusedCount']} potentially unused materials")

# 4. Preview cleanup
paths_to_delete = [a['path'] for a in unused['assets'][:5]]
preview = unity_skills.call_skill("cleaner_delete_assets", 
    paths=paths_to_delete)
print(f"Would free: {preview['totalMB']:.2f} MB")
# To actually delete, call again with preview['confirmToken'] within 5 minutes.
```

---
## Exact Signatures

Exact names, parameters, defaults, and returns are defined by `GET /skills/schema` or `unity_skills.get_skill_schema()`, not by this file.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.