Help us shape the future of Go! Take the 2026 Go Developer Survey

unity-cli-loop

module
v3.0.0-beta.71+incompa... Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: MIT

README

Unity CLI Loop

日本語

Unity License
ClaudeCode Cursor Codex Antigravity GitHubCopilot

logo-black-bg
(Logo inspired by Daft Punk's Discovery album art)

Let an AI agent compile, test, and operate your Unity project from popular LLM tools via CLI.

Designed to keep AI-driven development loops running autonomously inside your existing Unity projects.

Concept

Unity CLI Loop is a Unity integration tool designed so that AI can drive your Unity project forward with minimal human intervention. Tasks that humans typically handle manually—compiling, running the Test Runner, checking logs, editing scenes, and capturing windows to verify UI layouts—are exposed as tools that LLMs can orchestrate.

Unity CLI Loop is built around four core ideas:

  1. A self-hosted development loop where AI autonomously compiles, tests, inspects logs, and fixes issues. Uses compile, run-tests, get-logs, clear-console.
  2. AI-driven Unity Editor operation—scene building, object manipulation, menu execution, and UI refinement from screenshots. Uses execute-dynamic-code, screenshot.
  3. PlayMode automated testing—AI clicks buttons, drags elements, presses keys, records and replays input, and verifies game behavior. Uses simulate-mouse-ui, simulate-mouse-input, simulate-keyboard, record-input, replay-input, execute-dynamic-code, screenshot.
  4. Achieving the above with a minimal set of tools. See Design Philosophy.

https://github.com/user-attachments/assets/569a2110-7351-4cf3-8281-3a83fe181817

Installation

[!WARNING] The following software is required

  • Unity 2022.3 or later

Via Unity Package Manager

  1. Open Unity Editor
  2. Open Window > Package Manager
  3. Click the "+" button
  4. Select "Add package from git URL"
  5. Enter the following URL:
https://github.com/hatayama/unity-cli-loop.git?path=/Packages/src

Using Scoped registry in Unity Package Manager

  1. Open Project Settings window and go to Package Manager page
  2. Add the following entry to the Scoped Registries list:
Name: OpenUPM
URL: https://package.openupm.com
Scope(s): io.github.hatayama.uloopmcp
  1. Open Package Manager window and select OpenUPM in the My Registries section. Unity CLI Loop will be displayed.

[!NOTE] com.unity.inputsystem is now an optional dependency. Install it only if you want Input System features such as simulate-keyboard, simulate-mouse-input, record-input, replay-input, and the Recordings window. com.unity.test-framework is also optional. Install it only if you want the run-tests tool to execute Unity Test Runner.

Quickstart

Step 1: Install the CLI

Select Window > Unity CLI Loop > Settings. A dedicated window will open. If the CLI button is not highlighted in blue, press Install CLI.

The installer places the global uloop dispatcher on PATH. Project-specific uloop-project-runner binaries are downloaded into the user cache automatically from each project's .uloop/project-runner-pin.json.

Keep the v3 dispatcher installed when working with both v2 and v3 projects. If Unity resolves a project to a v2 io.github.hatayama.uloopmcp package, the dispatcher automatically installs the matching v2 uloop-cli release into its versioned user cache and delegates the command to it. The resolved package version takes precedence over a stale v3 project-runner pin left after a downgrade. The initial npm installation and the v2-mode notice are written to stderr so stdout remains the delegated command's output. V3 projects continue to use the project runner selected by their pin.

The global install, update, uninstall, completion (now a no-op stub; shell completion has been removed), and launch commands remain owned by the v3 dispatcher in every project. Other project commands, help, and the project-scoped version request are delegated for detected v2 projects.

V2 delegation requires Node.js 22 or later, including npm for the first command that populates the cache. Do not press Update CLI or Downgrade CLI in a v2 project's Settings window. These buttons are normally hidden because the delegated CLI reports the matching v2 version, but using one can restore a global npm CLI that hides the v3 dispatcher depending on PATH order.

CLI-only terminal install

Use this only when you want to install the standalone global CLI without opening Unity package setup.

Install gh and jq through your operating-system or package channel first. The bootstrap does not install or fall back from gh. Select the immutable dispatcher Release tag and its source branch; use refs/heads/main for a main release or refs/heads/v3-beta for a v3-beta release.

On macOS or Windows Git Bash:

REPOSITORY=hatayama/unity-cli-loop
RELEASE_TAG=dispatcher-v<RELEASE_VERSION>
SOURCE_REF=refs/heads/v3-beta
tmp_dir=$(mktemp -d)
gh release download "$RELEASE_TAG" --repo "$REPOSITORY" --pattern 'install.sh' --pattern 'install.sh.sigstore.json' --dir "$tmp_dir" && \
tag_sha=$(gh api "repos/$REPOSITORY/commits/$RELEASE_TAG" --jq .sha) && \
gh attestation verify "$tmp_dir/install.sh" --bundle "$tmp_dir/install.sh.sigstore.json" --repo "$REPOSITORY" --signer-workflow "$REPOSITORY/.github/workflows/dispatcher-publish.yml" --source-ref "$SOURCE_REF" --source-digest "$tag_sha" && \
manifest=$(jq -r '.dsseEnvelope.payload | @base64d | fromjson | .subject[] | "\(.digest.sha256)  \(.name)"' "$tmp_dir/install.sh.sigstore.json" | LC_ALL=C sort) && \
ULOOP_VERSION="$RELEASE_TAG" ULOOP_ARCHIVE_MANIFEST="$manifest" sh "$tmp_dir/install.sh"

On Windows PowerShell:

$repository = 'hatayama/unity-cli-loop'
$releaseTag = 'dispatcher-v<RELEASE_VERSION>'
$sourceRef = 'refs/heads/v3-beta'
$temporaryDirectory = New-Item -ItemType Directory -Force -Path (Join-Path $env:TEMP ([guid]::NewGuid()))
gh release download $releaseTag --repo $repository --pattern 'install.ps1' --pattern 'install.ps1.sigstore.json' --dir $temporaryDirectory.FullName
if ($LASTEXITCODE -ne 0) { throw 'Installer download failed.' }
$tagSha = gh api "repos/$repository/commits/$releaseTag" --jq .sha
if ($LASTEXITCODE -ne 0) { throw 'Release tag resolution failed.' }
gh attestation verify (Join-Path $temporaryDirectory.FullName 'install.ps1') --bundle (Join-Path $temporaryDirectory.FullName 'install.ps1.sigstore.json') --repo $repository --signer-workflow "$repository/.github/workflows/dispatcher-publish.yml" --source-ref $sourceRef --source-digest $tagSha
if ($LASTEXITCODE -ne 0) { throw 'Installer attestation verification failed.' }
$bundle = Get-Content -Raw -Encoding UTF8 (Join-Path $temporaryDirectory.FullName 'install.ps1.sigstore.json') | ConvertFrom-Json
$statement = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($bundle.dsseEnvelope.payload)) | ConvertFrom-Json
$manifest = [string]::Join("`n", @($statement.subject | ForEach-Object { "$($_.digest.sha256)  $($_.name)" } | Sort-Object))
$env:ULOOP_VERSION = $releaseTag
$env:ULOOP_ARCHIVE_MANIFEST = $manifest
& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $temporaryDirectory.FullName 'install.ps1')

After installing the native CLI, the installer automatically tries to remove the old npm package with npm uninstall -g uloop-cli. If npm is unavailable or the old command belongs to a different Node prefix, the installer prints the manual command to run:

npm uninstall -g uloop-cli

Do not install a v2 CLI globally to switch projects. If your terminal resolves uloop to an old npm installation instead of the native dispatcher, remove the npm installation and reinstall the native dispatcher:

npm uninstall -g uloop-cli
# Run the verified native installer above again.
which uloop
uloop --version

On Windows PowerShell:

npm uninstall -g uloop-cli
# Run the verified native installer above again.
Get-Command uloop
uloop --version
1

The Settings window shows whether the global uloop command is detected.

If you see the following display, the installation was successful.
2

Step 2: Install Skills

Select your target (Claude Code, Codex, etc.) and press the Install Skills button.
3

To install from terminal
# Install for Claude Code project
uloop skills install --claude

# Install for OpenAI Codex project
uloop skills install --codex

# Or install globally
uloop skills install --claude --global

That's it! After installing Skills, LLM tools can automatically handle instructions like these:

Your Instruction Skill Used by LLM Tools
"Launch Unity for this project" /uloop-launch
"Fix the compile errors" /uloop-compile
"Run the tests and tell me why they failed" /uloop-run-tests + /uloop-get-logs
"Check the scene hierarchy" /uloop-get-hierarchy
"Play the game and bring Unity to the front" /uloop-control-play-mode + /uloop-focus-window
"Bulk-update prefab parameters" /uloop-execute-dynamic-code
"Take a screenshot of Game View and adjust the UI layout" /uloop-screenshot + /uloop-execute-dynamic-code
"Record my gameplay input" /uloop-record-input
"Replay the recorded input" /uloop-replay-input
All 16 Bundled Skills
  • /uloop-launch - Launch Unity with correct version
  • /uloop-compile - Execute compilation
  • /uloop-get-logs - Get console logs
  • /uloop-run-tests - Run tests
  • /uloop-clear-console - Clear console
  • /uloop-focus-window - Bring Unity Editor to front
  • /uloop-get-hierarchy - Get scene hierarchy
  • /uloop-find-game-objects - Find GameObjects
  • /uloop-screenshot - Capture EditorWindow
  • /uloop-simulate-mouse-ui - Simulate mouse click, long-press, and drag on PlayMode UI elements
  • /uloop-simulate-mouse-input - Simulate mouse input in PlayMode via Input System (use --dry-run to check 3D physics hits)
  • /uloop-simulate-keyboard - Simulate keyboard input in PlayMode via Input System
  • /uloop-record-input - Record keyboard and mouse input during PlayMode
  • /uloop-replay-input - Replay recorded input during PlayMode
  • /uloop-control-play-mode - Control Play Mode
  • /uloop-execute-dynamic-code - Execute dynamic C# code
Direct CLI Usage (Advanced)

You can also call the CLI directly without using Skills:

# List available tools
uloop list

# Launch Unity project with correct version
uloop launch

# Launch with build target (Android, iOS, StandaloneOSX, etc.)
uloop launch -p Android

# Kill running Unity and restart
uloop launch -r

# Execute compilation
uloop compile

# Compile without waiting for Domain Reload
uloop compile --no-wait-for-domain-reload

# Get logs
uloop get-logs --max-count 10

# Run tests
uloop run-tests --filter-type all

# Execute dynamic code
uloop execute-dynamic-code --code 'using UnityEngine; Debug.Log("Hello from CLI!");'

Project Path Specification

If --project-path is omitted, the port is automatically selected from the Unity project detected in the current directory.

To operate multiple Unity instances from a single LLM tool, explicitly specify a project path:

# Specify by project path (absolute or relative)
uloop compile --project-path /Users/foo/my-unity-project
uloop compile --project-path ../other-project

Design Philosophy

Unity CLI Loop does not chase tool count. With dynamic C# code execution (execute-dynamic-code), almost any Unity Editor operation can be accomplished through a single tool.

Too many tools make it harder for AI to choose the right one. And even when tools are packaged as Skills, each tool's description still consumes the context window. We believe keeping tools to the essential minimum is good design.

Dedicated tools exist only for operations that dynamic code execution cannot handle by nature — such as frame-spanning input simulation and screenshot capture — and for operations called so frequently in the development loop, like compile and get-logs, that generating C# code each time would waste tokens.

Key Features

Development Loop Tools

1. compile - Execute Compilation

Performs AssetDatabase.Refresh() and then compiles, returning the results after Domain Reload completes. Can detect errors and warnings that built-in linters cannot find. You can choose between incremental compilation and forced full compilation. Use --no-wait-for-domain-reload only when you need the fire-and-forget path.

→ Execute compile, analyze error and warning content
→ Automatically fix relevant files
→ Verify with compile again
2. get-logs - Retrieve Logs Same as Unity Console

Filter by LogType or search target string with advanced search capabilities. You can also choose whether to include stacktrace. This allows you to retrieve logs while keeping the context small. MaxCount behavior: Returns the latest logs (tail-like behavior). When MaxCount=10, returns the most recent 10 logs. Advanced Search Features:

  • Regular Expression Support: Use UseRegex: true for powerful pattern matching
  • Stack Trace Search: Use SearchInStackTrace: true to search within stack traces
→ get-logs (LogType: Error, SearchText: "NullReference", MaxCount: 10)
→ get-logs (LogType: All, SearchText: "(?i).*error.*", UseRegex: true, MaxCount: 20)
→ get-logs (LogType: All, SearchText: "MyClass", SearchInStackTrace: true, MaxCount: 50)
→ Identify cause from stacktrace, fix relevant code
3. run-tests - Execute TestRunner (PlayMode, EditMode supported)

Executes Unity Test Runner and retrieves test results. You can set conditions with FilterType and FilterValue.

  • FilterType: all (all tests), exact (individual test method name), regex (class name or namespace), assembly (assembly name)
  • FilterValue: Value according to filter type (class name, namespace, etc.)
  • SaveBeforeRun: Saves unsaved loaded Scene changes and current Prefab Stage changes before running tests by default. When disabled, unsaved editor changes stop test execution instead of being discarded. Test results can be output as xml. The output path is returned so AI can read it. This is also a strategy to avoid consuming context.
→ run-tests (FilterType: exact, FilterValue: "PlayerControllerTests.TestJump")
→ run-tests (SaveBeforeRun: false, fail if editor changes are unsaved)
→ Check failed tests, fix implementation to pass tests

[!WARNING] During PlayMode test execution, Domain Reload is forcibly turned OFF. (Settings are restored after test completion) Note that static variables will not be reset during this period.

Unity Editor Automation & Discovery Tools
4. clear-console - Log Cleanup

Clear logs that become noise during log searches.

→ clear-console
→ Start new debug session
5. find-game-objects - Search Scene Objects

Retrieve objects and examine component parameters. Also retrieve information about currently selected GameObjects (multiple selection supported) in Unity Editor.

→ find-game-objects (RequiredComponents: ["Camera"])
→ Investigate Camera component parameters

→ find-game-objects (SearchMode: "Selected")
→ Get detailed information about currently selected GameObjects in Unity Editor (supports multiple selection)
6. get-hierarchy - Analyze Scene Structure

Retrieve information about the currently active Hierarchy in nested JSON format. Works at runtime as well. Automatic File Export: Retrieved hierarchy data is always saved as JSON in {project_root}/.uloop/outputs/HierarchyResults/ directory. The response only returns the file path, minimizing token consumption even for large datasets. Selection Mode: Use uloop get-hierarchy --use-selection to get hierarchy starting from currently selected GameObject(s) in Unity Editor. Supports multiple selection - when parent and child are both selected, only the parent is used as root to avoid duplicate traversal.

→ Understand parent-child relationships between GameObjects, discover and fix structural issues
→ Regardless of scene size, hierarchy data is saved to a file and the path is returned instead of raw JSON
→ uloop get-hierarchy --use-selection
→ Get hierarchy of currently selected GameObjects without specifying paths manually
7. focus-window - Bring Unity Editor Window to Front (macOS & Windows)

Ensures the Unity Editor window becomes the foreground application on macOS and Windows Editor builds. Great for keeping visual feedback in sync after other apps steal focus. (Linux is currently unsupported.)

8. screenshot - Take a Screenshot of EditorWindow

Take a screenshot of any EditorWindow as a PNG. Specify the window name (the text displayed in the title bar/tab) to capture. When multiple windows of the same type are open (e.g., 3 Inspector windows), all windows are saved with numbered filenames. Supports three matching modes: exact (default), prefix, and contains - all case-insensitive. Use CaptureMode: rendering when you need coordinates for simulate-mouse-input (including --dry-run) or simulate-mouse-ui; convert raw image pixels with ScreenshotToInputFormula, or pass annotated SimX/SimY values directly.

→ screenshot (WindowName: "Console")
→ screenshot (CaptureMode: rendering, AnnotateRaycastGrid: true)
→ Save Console window state as PNG
→ Provide visual feedback to AI
9. control-play-mode - Control Play Mode

Control Unity Editor's Play Mode. Supports three actions: Play (start/resume), Stop, and Pause.

→ control-play-mode (Action: Play)
→ Start Play Mode to verify game behavior
→ control-play-mode (Action: Pause)
→ Pause to inspect state
10. execute-dynamic-code - Dynamic C# Code Execution

Execute C# code dynamically within Unity Editor.

Async support:

  • You can write await in your snippet (Task/ValueTask/UniTask and any awaitable type)
  • Cancellation is propagated when you pass a CancellationToken to the tool

When enabled, dynamic code execution runs with full Unity Editor process permissions and can use Unity APIs, .NET APIs, and project assemblies. Disable this tool with the Tool Settings toggle when AI agents should not execute arbitrary C#.

→ execute-dynamic-code (Code: "GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); return \"Cube created\";")
→ Rapid prototype verification, batch processing automation
→ Full Unity Editor API access for trusted automation
PlayMode Automated Testing Tools
11. simulate-mouse-ui - Simulate Mouse Input on PlayMode UI

Simulate mouse click, long-press, and drag on PlayMode UI elements. Uses EventSystem and ExecuteEvents to dispatch pointer events directly — works independently of both old and new Input System. For game logic that reads Input System (e.g. Mouse.current.leftButton.wasPressedThisFrame), use simulate-mouse-input instead.

Supports 6 actions: Click, LongPress, Drag (one-shot), DragStart/DragMove/DragEnd (split drag).

→ screenshot (CaptureMode: rendering, AnnotateElements: true)
→ Get element coordinates from AnnotatedElements (SimX/SimY)
→ simulate-mouse-ui (Action: Click, X: 400, Y: 300)
→ simulate-mouse-ui (Action: LongPress, X: 400, Y: 300, Duration: 5.0)
→ simulate-mouse-ui (Action: Drag, FromX: 100, FromY: 500, X: 400, Y: 300)
→ simulate-mouse-ui (Action: DragStart, X: 100, Y: 500)
→ simulate-mouse-ui (Action: DragMove, X: 200, Y: 400, DragSpeed: 300)
→ simulate-mouse-ui (Action: DragEnd, X: 400, Y: 300)

https://github.com/user-attachments/assets/c7ee9103-c282-4f90-8b01-64bb17400f3e

12. simulate-mouse-input - Simulate Mouse Input in PlayMode via Input System

Simulate mouse input in PlayMode via Input System. Injects button clicks, mouse delta, and scroll wheel directly into Mouse.current for game logic that reads Input System. Unlike simulate-mouse-ui which fires EventSystem pointer events for uGUI, this tool targets game logic that reads Mouse.current directly. Real mouse injection is available only when the Input System package is installed, and Active Input Handling must be set to Input System Package (New) or Both in Player Settings.

Use --dry-run to check what a top-left Game View coordinate hits in 3D physics without injecting input. Dry-run works in EditMode and does not require the Input System package.

Click, LongPress, and dry-run coordinates use the same top-left Game View coordinates as screenshot --capture-mode rendering; do not flip Y before calling the tool.

Supports 5 actions: Click, LongPress, MoveDelta, SmoothDelta, Scroll.

→ simulate-mouse-input (DryRun: true, X: 960, Y: 540)
→ simulate-mouse-input (Action: Click, X: 400, Y: 300)
→ simulate-mouse-input (Action: Click, X: 400, Y: 300, Button: Right)
→ simulate-mouse-input (Action: LongPress, X: 400, Y: 300, Duration: 2.0)
→ simulate-mouse-input (Action: MoveDelta, DeltaX: 100, DeltaY: 0)
→ simulate-mouse-input (Action: Scroll, ScrollY: 120)
→ simulate-mouse-input (Action: SmoothDelta, DeltaX: 300, DeltaY: 0, Duration: 0.5)
13. simulate-keyboard - Simulate Keyboard Input in PlayMode

Simulate keyboard key input in PlayMode via Input System. Supports single key taps, sustained holds, and multi-key combinations (e.g. Shift+W for sprinting). This tool is available only when the Input System package is installed, and Active Input Handling must be set to Input System Package (New) or Both in Player Settings. Game code must read input via Input System API (e.g. Keyboard.current[Key.W].isPressed), not legacy Input.GetKey().

Supports 3 actions: Press (one-shot tap or timed hold), KeyDown (hold key down), KeyUp (release held key). Use Press for edge-triggered gameplay such as Keyboard.current.spaceKey.wasPressedThisFrame; KeyDown emits only one initial press edge and then becomes held state, so use KeyDown/KeyUp only when the test intentionally needs a held key.

→ simulate-keyboard (Action: Press, Key: Space)
→ simulate-keyboard (Action: Press, Key: W, Duration: 2.0)
→ simulate-keyboard (Action: KeyDown, Key: LeftShift)
→ simulate-keyboard (Action: KeyDown, Key: W)
→ screenshot (CaptureMode: rendering)
→ simulate-keyboard (Action: KeyUp, Key: W)
→ simulate-keyboard (Action: KeyUp, Key: LeftShift)
14. record-input - Record Input During PlayMode

Record keyboard and mouse input during PlayMode frame-by-frame into a JSON file. Captures key presses, mouse movement, clicks, and scroll events via Input System device state diffing. This tool is available only when the Input System package is installed.

→ record-input (Action: Start)
→ record-input (Action: Start, Keys: "W,A,S,D,Space")
→ record-input (Action: Stop)
→ JSON file saved to .uloop/outputs/InputRecordings/
15. replay-input - Replay Recorded Input During PlayMode

Replay recorded keyboard and mouse input during PlayMode. Loads a JSON recording and injects input frame-by-frame via Input System. Supports looping and progress monitoring. This tool is available only when the Input System package is installed.

→ replay-input (Action: Start)
→ replay-input (Action: Start, InputPath: "scripts/my-play.json", Loop: true)
→ replay-input (Action: Status)
→ replay-input (Action: Stop)

Terminal-driven E2E coverage is available through one runner per shell family:

sh scripts/run-posix-e2e.sh --project-path /path/to/unity-project
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-windows-e2e.ps1

run-posix-e2e.sh uses the built native CLI binary by default, passes an explicit --project-path to every uloop invocation, and runs CLI recovery/readiness, input record/replay, and simulate-mouse UI coverage in one sequence.

Unity CLI Loop Extension Development

Unity CLI Loop enables efficient development of project-specific tools without requiring changes to the core package. The type-safe design allows for reliable custom tool implementation in minimal time. (If you ask AI, they should be able to make it for you soon ✨)

You can publish your extension tools on GitHub and reuse them across other projects.

[!TIP] For AI-assisted development: Detailed implementation guides are available in .claude/rules/cli.md for CLI and Skills development. These guides are automatically loaded by Claude Code when working in the relevant directories.

View Implementation Guide

Step 1: Create Schema Class (define parameters):

using io.github.hatayama.UnityCliLoop.ToolContracts;

public class MyCustomSchema : UnityCliLoopToolSchema
{
    public string MyParameter { get; set; } = "default_value";

    public MyEnum EnumParameter { get; set; } = MyEnum.Option1;
}

public enum MyEnum
{
    Option1 = 0,
    Option2 = 1,
    Option3 = 2
}

Step 2: Create Response Class (define return data):

using io.github.hatayama.UnityCliLoop.ToolContracts;

public class MyCustomResponse : UnityCliLoopToolResponse
{
    public string Result { get; set; }
    public bool Success { get; set; }

    public MyCustomResponse(string result, bool success)
    {
        Result = result;
        Success = success;
    }

    // Required parameterless constructor
    public MyCustomResponse() { }
}

Step 3: Create Tool Class:

using System.Threading;
using System.Threading.Tasks;
using io.github.hatayama.UnityCliLoop.ToolContracts;

[UnityCliLoopTool]
public class MyCustomTool : UnityCliLoopTool<MyCustomSchema, MyCustomResponse>
{
    public override string ToolName => "my-custom-tool";

    // Executed on main thread
    protected override Task<MyCustomResponse> ExecuteAsync(MyCustomSchema parameters, CancellationToken ct)
    {
        // Type-safe parameter access
        string param = parameters.MyParameter;
        MyEnum enumValue = parameters.EnumParameter;

        // Check for cancellation before long-running operations
        ct.ThrowIfCancellationRequested();

        // Implement custom logic here
        string result = ProcessCustomLogic(param, enumValue);
        bool success = !string.IsNullOrEmpty(result);

        // For long-running operations, periodically check for cancellation
        // ct.ThrowIfCancellationRequested();

        return Task.FromResult(new MyCustomResponse(result, success));
    }

    private string ProcessCustomLogic(string input, MyEnum enumValue)
    {
        // Implement custom logic
        return $"Processed '{input}' with enum '{enumValue}'";
    }
}

Please also refer to Custom Tool Samples.

Custom Skills for Your Tools

When you create a custom tool, you can create a Skill/ subfolder within the tool folder and place a SKILL.md file there. This allows LLM tools to automatically discover and use your custom tool through the Skills system.

How it works:

  1. Create a Skill/ subfolder in your custom tool's folder
  2. Place SKILL.md inside the Skill/ folder
  3. Run uloop skills install --claude to install all skills (bundled + project)
  4. LLM tools will automatically recognize your custom skill

Directory structure:

Assets/Editor/CustomTools/MyTool/
├── MyTool.cs           # Tool implementation
└── Skill/
    ├── SKILL.md        # Skill definition (required)
    └── references/     # Additional files (optional)
        └── usage.md

SKILL.md format:

---
name: uloop-my-custom-tool
description: "Description of what the tool does and when to use it."
---

# uloop my-custom-tool

Detailed documentation for the tool...

Scanned locations (searches for Skill/SKILL.md files):

  • Assets/**/Editor/<ToolFolder>/Skill/SKILL.md
  • Packages/*/Editor/<ToolFolder>/Skill/SKILL.md
  • Library/PackageCache/*/Editor/<ToolFolder>/Skill/SKILL.md

[!TIP]

  • Add internal: true to the frontmatter to exclude a skill from installation (useful for internal/debug tools)
  • Additional files in the Skill/ folder (such as references/, scripts/, assets/) are also copied during installation

See HelloWorld sample for a complete example.

Other

Native Go CLI Development

Run the native Go CLI checks before changing files under cli:

scripts/check-go-cli.sh

The check script verifies formatting with goimports and gofumpt, runs go vet ./..., runs golangci-lint, and then runs go test ./... for every Go module in the repository. Install golangci-lint first if it is not available on your PATH.

Use the existing build script when you need to refresh local development binaries under dist:

scripts/build-go-cli.sh
Unity CLI Loop Files

UserSettings/UnityMcpSettings.json stores per-user editor session state and should always remain local-only. The file name is a historical compatibility name.

The .uloop/ directory at the project root stores CLI cache, tool registry, and runtime outputs. Most of its contents are local-only, but some files can optionally be git-tracked for team sharing.

File Purpose Git-track?
project-runner-pin.json Project runner version contract used by the global dispatcher Yes
settings.tools.json Per-tool enable/disable preferences Optional
tools.json Auto-generated CLI tool registry No
outputs/ Runtime outputs (test results, screenshots, hierarchy dumps) No

[!TIP] Recommended .gitignore pattern

**/.uloop/*
!**/.uloop/project-runner-pin.json
!**/.uloop/settings.tools.json

This ignores auto-generated files and runtime outputs while allowing the dispatcher pin and team-shared configuration to be tracked. Remove the ! line if you don't need to share tool enable/disable preferences.

License

MIT License

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL