{"id":50782744,"url":"https://github.com/rivgoo/ushell","last_synced_at":"2026-06-12T05:01:41.254Z","repository":{"id":361799316,"uuid":"1217985745","full_name":"Rivgoo/UShell","owner":"Rivgoo","description":"A high-performance, reflection-free, and architecturally clean developer console for Unity","archived":false,"fork":false,"pushed_at":"2026-06-01T09:10:06.000Z","size":3104,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-01T11:10:10.263Z","etag":null,"topics":["console","developer-tools","game-development","in-game","in-game-console","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rivgoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T12:22:09.000Z","updated_at":"2026-06-01T09:08:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Rivgoo/UShell","commit_stats":null,"previous_names":["rivgoo/ushell"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/Rivgoo/UShell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rivgoo%2FUShell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rivgoo%2FUShell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rivgoo%2FUShell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rivgoo%2FUShell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rivgoo","download_url":"https://codeload.github.com/Rivgoo/UShell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rivgoo%2FUShell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34229624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["console","developer-tools","game-development","in-game","in-game-console","unity"],"created_at":"2026-06-12T05:01:39.884Z","updated_at":"2026-06-12T05:01:41.244Z","avatar_url":"https://github.com/Rivgoo.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"Assets/Plugins/UShell/Graphics/Editor/UShell_Icon.png\" alt=\"UShell Logo\" width=\"128\"/\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eUShell\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Unity-2021.3%2B-black?logo=unity\" alt=\"Unity Version\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License: MIT\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TextMeshPro-Required-orange\" alt=\"TextMeshPro Required\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PRs-Welcome-brightgreen.svg\" alt=\"PRs Welcome\"\u003e\n\u003c/p\u003e\n\n**UShell** is a developer-grade runtime console for Unity built around a statically-typed, zero-reflection registration API. It discards the attribute-scanning approach in favour of an explicit Fluent Builder pattern — giving you predictable startup cost, compile-time safety, and full architectural control.\n\n---\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [🏛️ Architecture \\\u0026 Core Philosophy](#️-architecture--core-philosophy)\n- [📦 Installation \\\u0026 Quick Start](#-installation--quick-start)\n  - [Option 1: Unity Package (Recommended)](#option-1-unity-package-recommended)\n  - [Option 2: Package Manager (Git URL)](#option-2-package-manager-git-url)\n  - [Option 3: Manual](#option-3-manual)\n  - [Prerequisites](#prerequisites)\n  - [First Run](#first-run)\n- [⚙️ Bootstrapping \\\u0026 Configuration](#️-bootstrapping--configuration)\n  - [Option A: Component-Based Setup (Drag \\\u0026 Drop)](#option-a-component-based-setup-drag--drop)\n  - [Option B: Modular Injection (`IShellConfigurator`)](#option-b-modular-injection-ishellconfigurator)\n  - [Option C: Pure Code Setup (Zenject / VContainer)](#option-c-pure-code-setup-zenject--vcontainer)\n- [🔄 Dynamic Runtime Reconfiguration](#-dynamic-runtime-reconfiguration)\n  - [The Configuration Transaction Pattern](#the-configuration-transaction-pattern)\n  - [Concurrency \\\u0026 Execution Safety Guarantees](#concurrency--execution-safety-guarantees)\n  - [Transactional Fluent API Reference](#transactional-fluent-api-reference)\n  - [Complete Reconfiguration Example](#complete-reconfiguration-example)\n- [🛠️ Profiles \\\u0026 the Fluent API](#️-profiles--the-fluent-api)\n  - [Creating a Profile](#creating-a-profile)\n  - [`ICommandBuilder`](#icommandbuilder)\n  - [`ICommandConfigurator` — Metadata](#icommandconfigurator--metadata)\n  - [`ICommandConfigurator` — Parameter Binding](#icommandconfigurator--parameter-binding)\n  - [`ICommandConfigurator` — Execution Delegates](#icommandconfigurator--execution-delegates)\n  - [Complete Profile Example](#complete-profile-example)\n- [⏳ Interactive Commands (`ICommandContext`)](#-interactive-commands-icommandcontext)\n  - [`ICommandContext` API](#icommandcontext-api)\n  - [`IProgressReporter` API](#iprogressreporter-api)\n  - [Interactive Example](#interactive-example)\n- [🔍 Autocomplete \\\u0026 Suggestions](#-autocomplete--suggestions)\n  - [Static Suggestions](#static-suggestions)\n  - [Dynamic Suggestions](#dynamic-suggestions)\n  - [Reusable Provider Class](#reusable-provider-class)\n- [🔠 Custom Type Parsers (`ITypeParser\u003cT\u003e`)](#-custom-type-parsers-itypeparsert)\n  - [Implementation Contract](#implementation-contract)\n  - [Registration](#registration)\n  - [ScriptableObject Lookup Example](#scriptableobject-lookup-example)\n- [💾 Session State \\\u0026 Macros](#-session-state--macros)\n  - [Variable Syntax in the Console](#variable-syntax-in-the-console)\n  - [Built-In Macro Commands](#built-in-macro-commands)\n  - [Accessing Session State from C#](#accessing-session-state-from-c)\n- [🎨 Output, Formatting \\\u0026 Theming](#-output-formatting--theming)\n  - [`ShellProfile` Output Methods](#shellprofile-output-methods)\n  - [`PrintTable` and `TableStyle`](#printtable-and-tablestyle)\n  - [`ShellPalette` — Design Tokens](#shellpalette--design-tokens)\n  - [`RichText` Utility](#richtext-utility)\n  - [`ProfileFormatter` Layout Helpers](#profileformatter-layout-helpers)\n- [🖥️ UI Customization \\\u0026 Input Adapters](#️-ui-customization--input-adapters)\n  - [`UShellUIConfiguration` ScriptableObject](#ushelluiconfiguration-scriptableobject)\n  - [Custom Input Provider (`IInputProvider`)](#custom-input-provider-iinputprovider)\n- [🔌 Programmatic API (`IUShellAPI`)](#-programmatic-api-iushellapi)\n  - [Quick Examples](#quick-examples)\n  - [`IUShellAPI` Reference](#iushellapi-reference)\n    - [Properties](#properties)\n    - [Events](#events)\n    - [Methods](#methods)\n- [| `BeginConfiguration()` | `IConfigurationTransaction` | Spawns a fluent, transactional Unit of Work to dynamically add/remove profiles and type parsers in-place during runtime. |](#-beginconfiguration--iconfigurationtransaction--spawns-a-fluent-transactional-unit-of-work-to-dynamically-addremove-profiles-and-type-parsers-in-place-during-runtime-)\n- [📚 Built-In Profiles Reference](#-built-in-profiles-reference)\n  - [`ConsoleManagementProfile`](#consolemanagementprofile)\n  - [`EnvironmentInfoProfile`](#environmentinfoprofile)\n  - [`ApplicationSettingsProfile`](#applicationsettingsprofile)\n  - [`SceneManagementProfile`](#scenemanagementprofile)\n  - [`MathUtilityProfile`](#mathutilityprofile)\n  - [`RuntimeDiagnosticsProfile`](#runtimediagnosticsprofile)\n  - [`GameObjectProfile`](#gameobjectprofile)\n  - [`HttpProfile`](#httpprofile)\n  - [`FileIOProfile`](#fileioprofile)\n- [🔬 Architecture Deep Dive](#-architecture-deep-dive)\n  - [Execution Pipeline Detail](#execution-pipeline-detail)\n  - [Key Interfaces and Responsibilities](#key-interfaces-and-responsibilities)\n  - [Assembly Layout](#assembly-layout)\n- [📄 License](#-license)\n\n---\n\n## 🏛️ Architecture \u0026 Core Philosophy\n\nBefore writing a single line of code, it is worth understanding the four principles that govern every design decision in UShell:\n\n**1. Zero-Reflection / Explicit Registration.** There are no `[ConsoleCommand]` attributes. Commands are constructed using a `ShellBuilder` via a Fluent Builder pattern. Consequence: zero assembly scanning at startup, zero hidden GC spikes, full dead-code-elimination compatibility.\n\n**2. The Three-Stage Execution Pipeline.**\n- **Lexing → Parsing → AST.** User input (e.g., `spawn -count 5 \"Orc\"`) is tokenized by the `Lexer` and transformed into a typed Abstract Syntax Tree with nodes such as `CommandNode`, `LiteralNode`, `VariableNode`, `ArrayNode`, and `NamedArgumentNode`.\n- **Binding.** The `ArgumentBinder` walks the AST and maps each node to a C# object using registered `ITypeParser\u003cT\u003e` implementations.\n- **Invocation.** The resolved `ICommandInvoker` (one of the `ActionInvoker` / `FuncInvoker` / `InteractiveFuncInvoker` variants) is called with the fully-typed argument array.\n\n**3. Strict Dependency Inversion.** `IShellCore` — the engine — knows nothing about `Canvas`, `TextMeshPro`, or `UnityEngine.InputSystem`. It communicates exclusively through two abstractions: `IConsolePrinter` (output) and `IInputProvider` (input). This means the entire core is testable in plain C# without Unity.\n\n**4. Interactive, Non-Blocking Execution.** Commands may suspend their own execution flow to await user confirmation, collect free-form text input, or stream live progress bars — all without blocking Unity's main thread and without requiring any coroutines.\n\n---\n\n## 📦 Installation \u0026 Quick Start\n\n**Requirements:** Unity 2021.3+ · TextMeshPro · Unity New Input System\n\n### Option 1: Unity Package (Recommended)\n\n1. Navigate to the [**Releases**](../../releases) page.\n2. Download the latest `UShell-vX.X.X.unitypackage`.\n3. Drag the package into the Unity Project window and import all assets.\n\n### Option 2: Package Manager (Git URL)\n\nWindow \u003e Package Manager \u003e + \u003e Add package from git URL…\n```\nhttps://github.com/Rivgoo/UShell.git?path=/Assets/Plugins/UShell\n```\n\n### Option 3: Manual\n\nCopy `Assets/Plugins/UShell` from this repository into your project's `Assets` folder.\n\n### Prerequisites\n\n- **TextMeshPro:** `Window → TextMeshPro → Import TMP Essential Resources`\n- **Input System:** `Project Settings → Player → Active Input Handling` must include the New Input System.\n\n### First Run\n\n1. Drag the **UShellManager** prefab from `Assets/Plugins/UShell/` into your scene.\n2. Press Play. Press **Backquote / Tilde** to toggle the console.\n3. Type `help` and press **Enter**.\n\n---\n\n## ⚙️ Bootstrapping \u0026 Configuration\n\nUShell supports three integration styles. Choose the one that matches your project's architecture.\n\n---\n\n### Option A: Component-Based Setup (Drag \u0026 Drop)\n\n`ComponentShellBootstrapper` is a `MonoBehaviour` that lives on the same `GameObject` as `UShellManager`. It reads configuration from the Unity Inspector and discovers `IShellConfigurator` components on child objects automatically.\n\n**Inspector fields:**\n\n| Field | Default | Description |\n|---|---|---|\n| `ActiveEnvironment` | `Development` | The active `EnvironmentTag`. Commands restricted to other environments are excluded at build time. |\n| `MirrorLogsToUnityConsole` | `false` | Also writes every shell log to `UnityEngine.Debug`. |\n| `IncludeConsoleManagementProfile` | `true` | `help`, `clear`, `history`, `alias`, macro commands. |\n| `IncludeApplicationSettingsProfile` | `true` | `screen.resolution`, `time.scale`, `gfx.fps`. |\n| `IncludeSceneManagementProfile` | `true` | `scene.load`, `scene.active`, `scene.list`. |\n| `IncludeMathUtilityProfile` | `true` | `eval`, `random`, `convert`. |\n| `IncludeEnvironmentInfoProfile` | `true` | `info`, `platform`, `game.version`. |\n| `IncludeGameObjectProfile` | `true` | `go.teleport`, `go.active`, `go.info`. |\n| `IncludeRuntimeDiagnosticsProfile` | `true` | `stats`, `mem`, `gc`, `objects`, `layer.find`. |\n| `IncludeHttpProfile` | `true` | `http.get`, `http.post`, `http.put`, `http.delete`. |\n| `IncludeFileIOProfile` | `true` | `file.read`, `file.write`, `screenshot`. |\n\n\u003e **⚠️ ARCHITECTURE RULE:** `ComponentShellBootstrapper` auto-discovers every `IShellConfigurator` on its **child** GameObjects. Your custom configurators must be placed as children of the `UShellManager` GameObject in the scene hierarchy.\n\n---\n\n### Option B: Modular Injection (`IShellConfigurator`)\n\n`IShellConfigurator` is the recommended extension point for adding your own commands without touching the `UShellManager` prefab. Implement it on any `MonoBehaviour` and attach it as a child of the `UShellManager` GameObject.\n\n```csharp\nusing UnityEngine;\nusing UShell.Runtime.Unity.Bootstrapping;\nusing UShell.Runtime.Core.Bootstrapping;\n\npublic class GameShellConfigurator : MonoBehaviour, IShellConfigurator\n{\n    [SerializeField] private ItemDatabase _itemDatabase;\n\n    public void Configure(ShellBuilder builder, ShellBootstrapContext context)\n    {\n        // context gives you access to the shared printer, session state, history, etc.\n        builder.AddProfile(new ItemProfile(context.Printer, _itemDatabase));\n        builder.AddProfile(new NetworkAdminProfile(context.Printer));\n        builder.AddTypeParser(new ItemIdParser(_itemDatabase));\n    }\n}\n```\n\n**`ShellBootstrapContext` properties:**\n\n| Property | Type | Description |\n|---|---|---|\n| `Printer` | `IConsolePrinter` | Route-safe printer for output from profiles. |\n| `RegistryProxy` | `ICommandRegistry` | A deferred proxy to the final registry — safe to pass to profiles that need to enumerate commands. |\n| `History` | `ICommandHistory` | The shared input history instance. |\n| `SessionState` | `ISessionState` | The shared macro/variable store. |\n| `Controller` | `IShellController` | Allows profiles to request `clear` / `close` lifecycle actions. |\n| `ActiveEnvironment` | `EnvironmentTag` | The environment flag the shell was booted with. |\n\n---\n\n### Option C: Pure Code Setup (Zenject / VContainer)\n\nUse `CoreShellBootstrapper` when you want to wire UShell entirely in code — ideal for DI container installers.\n\n```csharp\nusing UShell.Runtime.Core.Commands;\nusing UShell.Runtime.Unity.Bootstrapping;\n\npublic class TerminalInstaller : MonoInstaller\n{\n    [SerializeField] private UShellManager _manager;\n\n    public override void InstallBindings()\n    {\n        // 1. Instantiate the bootstrapper for the current environment\n        var bootstrapper = new CoreShellBootstrapper(\n            EnvironmentTag.Development,\n            mirrorLogsToUnityConsole: true);\n\n        // 2. Register custom type parsers before profiles\n        bootstrapper.AddTypeParser(new PlayerIdParser());\n\n        // 3. Register profiles via a factory delegate — context is injected by the bootstrapper\n        bootstrapper.AddProfile(context =\u003e\n            new NetworkAdminProfile(context.Printer, Container.Resolve\u003cINetworkService\u003e()));\n\n        bootstrapper.AddProfile(context =\u003e\n            new PlayerCheatsProfile(context.Printer, Container.Resolve\u003cIPlayerService\u003e()));\n\n        // 4. Build the core and hand the result to the manager\n        BootstrapResult result = bootstrapper.Build();\n        _manager.Initialize(result);\n    }\n}\n```\n\n`CoreShellBootstrapper` API:\n\n| Method | Description |\n|---|---|\n| `AddProfile(IShellProfile)` | Registers a pre-constructed profile instance. |\n| `AddProfile(Func\u003cShellBootstrapContext, IShellProfile\u003e)` | Registers a factory that receives the bootstrap context. Preferred for profiles with DI dependencies. |\n| `AddTypeParser\u003cT\u003e(ITypeParser\u003cT\u003e)` | Registers a custom type parser globally. |\n| `AddConfigurator(IShellConfigurator)` | Attaches a full `IShellConfigurator` instance (advanced). |\n| `Build()` | Wires all dependencies and returns a `BootstrapResult`. |\n\n\u003e **⚠️ ARCHITECTURE RULE:** Type parsers must be registered **before** profiles that use them. Registration order matters: parsers first, then profiles.\n\n---\n\n## 🔄 Dynamic Runtime Reconfiguration\n\nUShell supports dynamic runtime modification of the console configuration. Rather than completely tearing down and rebuilding the shell core (which would wipe command history, active session variables, and UI log layouts), UShell utilizes **Configuration Transactions** to mutate the command and parser registries *in-place*.\n\n```\n [User API Request] \n         │\n         ▼\n 1. Stage Changes (IConfigurationTransaction)\n         │\n         ▼\n 2. Concurrency Safety: Wait for all running commands to finish\n         │\n         ▼\n 3. Lock System \u0026 Close UI (StartSession Lock)\n         │\n         ▼\n 4. Perform In-Place Mutations (Command/Parser Registries Updated)\n         │\n         ▼\n 5. Unlock System \u0026 Complete Transaction\n```\n\nThis ensures full system stability while allowing you to inject or revoke developer tools as the game context changes (e.g., loading DLCs, entering specific gameplay zones, or changing network states).\n\n### The Configuration Transaction Pattern\n\nDynamic reconfigurations are structured as transactions using the **Unit of Work** pattern. This guarantees that all changes are pre-validated and applied atomically. If any command name or alias collision occurs during staging, the transaction throws an exception and rolls back without corrupting the active console state.\n\n```csharp\n// 1. Begin the configuration transaction\nIConfigurationTransaction transaction = UShellManager.API.BeginConfiguration();\n\n// 2. Stage multiple operations fluently\ntransaction\n    .AddProfile(new DungeonCheatProfile(UShellManager.API))\n    .AddTypeParser(new EnemyTargetParser())\n    .RemoveProfile\u003cMainMenuProfile\u003e(); // Remove existing profile by its compile-time Type\n\n// 3. Apply the changes atomically\nawait transaction.ApplyAsync();\n```\n\n---\n\n### Concurrency \u0026 Execution Safety Guarantees\n\nMutating command dictionaries while the user is executing a command would result in severe runtime exceptions. UShell prevents this using a multi-step safety routine:\n\n1. **Spin-Lock on Active Commands:** Calling `ApplyAsync` executes a non-blocking spin-lock that checks `IInteractiveSession.IsBusy`. It yields execution back to the engine until all currently running asynchronous, interactive, or multi-step commands are fully completed.\n2. **System Lock and UI Hiding:** Once active tasks finish, the transaction calls `IShellController.RequestClose()` to hide the console UI. It then calls `IInteractiveSession.StartSession()` to acquire an exclusive system-level lock. This blocks any new command execution and prevents the user from typing.\n3. **In-Place Mutation:** The staged additions and removals are processed synchronously within the registers.\n4. **Unlocking:** The transaction releases the interactive session lock, returning the console to its standard operating mode.\n\n---\n\n### Transactional Fluent API Reference\n\nThe `IConfigurationTransaction` interface provides the following chainable methods:\n\n| Method | Description |\n|---|---|\n| `AddProfile(IShellProfile profile)` | Stages a profile instance to be added to the console. |\n| `AddTypeParser\u003cT\u003e(ITypeParser\u003cT\u003e parser)` | Stages a custom type parser to be registered. |\n| `RemoveProfile\u003cTProfile\u003e()` | Stages all active profiles of the specified type `TProfile` to be removed. |\n| `RemoveProfile(Type profileType)` | Stages all active profiles of the specified `Type` to be removed. |\n| `RemoveProfile(IShellProfile profile)` | Stages a specific, exact profile instance to be removed. |\n| `RemoveTypeParser\u003cTTarget\u003e()` | Stages the parser mapped to type `TTarget` to be unregistered. |\n| `RemoveTypeParser(Type targetType)` | Stages the parser mapped to `targetType` to be unregistered. |\n| `ApplyAsync(CancellationToken token)` | Executes the safety checks, locks the shell, applies all staged mutations, and unlocks. |\n\n---\n\n### Complete Reconfiguration Example\n\nHere is a practical Unity controller managing state transitions when a player enters a specific game zone:\n\n```csharp\nusing System;\nusing System.Threading.Tasks;\nusing UnityEngine;\nusing UShell.Runtime.Unity.API;\nusing UShell.Runtime.Core.Configuration;\n\npublic class ZoneManager : MonoBehaviour\n{\n    private IShellProfile? _activeZoneProfile;\n\n    // Called when the player enters a dungeon area\n    public async void OnPlayerEnteredDungeon()\n    {\n        Debug.Log(\"Loading Dungeon Cheat System...\");\n        \n        _activeZoneProfile = new DungeonCheatProfile(UShellManager.API);\n\n        try\n        {\n            // Begin transactional update\n            IConfigurationTransaction transaction = UShellManager.API.BeginConfiguration();\n\n            transaction\n                .AddProfile(_activeZoneProfile)\n                .AddTypeParser(new EnemyConfigParser())\n                .RemoveProfile\u003cMainMenuProfile\u003e(); // Safely remove using generic Type argument\n\n            // Waits for running commands, closes UI, locks the terminal, mutates, and unlocks\n            await transaction.ApplyAsync();\n            \n            Debug.Log(\"Console updated successfully for Dungeon mode.\");\n        }\n        catch (Exception ex)\n        {\n            Debug.LogError($\"Failed to reconfigure console: {ex.Message}\");\n        }\n    }\n\n    // Called when the player exits the dungeon back to main menu\n    public async void OnPlayerExitedDungeon()\n    {\n        Debug.Log(\"Restoring Main Menu Console System...\");\n\n        if (_activeZoneProfile == null) return;\n\n        try\n        {\n            IConfigurationTransaction transaction = UShellManager.API.BeginConfiguration();\n\n            transaction\n                .RemoveProfile(_activeZoneProfile)                // Remove by concrete instance\n                .RemoveProfile(typeof(DungeonCheatProfile))        // (Alternative) Remove by Type object\n                .RemoveTypeParser\u003cEnemyConfig\u003e()                   // Unregister custom parser\n                .AddProfile(new MainMenuProfile(UShellManager.API));\n\n            await transaction.ApplyAsync();\n        }\n        catch (Exception ex)\n        {\n            Debug.LogError($\"Failed to restore console: {ex.Message}\");\n        }\n    }\n}\n```\n\n---\n\n## 🛠️ Profiles \u0026 the Fluent API\n\nA **Profile** is a cohesive grouping of related commands — analogous to a controller in MVC. Each profile is responsible for one concern: `AudioProfile`, `NetworkAdminProfile`, `CheatProfile`, etc.\n\n### Creating a Profile\n\nInherit from `ShellProfile` (not `IShellProfile` directly). The base class provides a protected `Printer` field and a suite of output helper methods. Implement the `Configure(ICommandBuilder builder)` abstract method.\n\n```csharp\nusing UShell.Runtime.Core.Abstractions;\nusing UShell.Runtime.Core.Commands.Fluent;\nusing UShell.Runtime.Core.Output;\n\npublic class AudioProfile : ShellProfile\n{\n    private readonly IAudioService _audio;\n\n    public AudioProfile(IConsolePrinter printer, IAudioService audio) : base(printer)\n    {\n        _audio = audio;\n    }\n\n    protected override void Configure(ICommandBuilder builder)\n    {\n        builder.WithName(\"audio.volume\")\n            .WithDescription(\"Gets or sets the master volume (0.0 – 1.0).\")\n            .AddOptionalParameter\u003cfloat\u003e(\"value\", -1f)\n            .Executes\u003cfloat\u003e(SetOrGetVolume);\n\n        builder.WithName(\"audio.mute\")\n            .WithDescription(\"Toggles the master mute state.\")\n            .Executes(ToggleMute);\n    }\n\n    private void SetOrGetVolume(float value)\n    {\n        if (value \u003c 0f)\n        {\n            PrintSuccess($\"Master volume: {_audio.MasterVolume:F2}\");\n            return;\n        }\n        _audio.MasterVolume = Mathf.Clamp01(value);\n        PrintSuccess($\"Master volume set to {_audio.MasterVolume:F2}.\");\n    }\n\n    private void ToggleMute()\n    {\n        _audio.IsMuted = !_audio.IsMuted;\n        PrintWarning(_audio.IsMuted ? \"Audio muted.\" : \"Audio unmuted.\");\n    }\n}\n```\n\n\u003e **⚠️ ARCHITECTURE RULE:** `ShellProfile` must not reference any `UnityEngine.UI` or scene-graph types directly. The profile's only output channel is `Printer`. Inject all game-service dependencies through the constructor.\n\n---\n\n### `ICommandBuilder`\n\nThe entry point for every command declaration. Accessed as the `builder` parameter inside `Configure`.\n\n| Method | Returns | Description |\n|---|---|---|\n| `WithName(string name)` | `ICommandConfigurator` | Starts a new command chain. Names are case-insensitive, no spaces/quotes/brackets/commas allowed. |\n\n---\n\n### `ICommandConfigurator` — Metadata\n\n| Method | Returns | Description |\n|---|---|---|\n| `.WithDescription(string desc)` | `ICommandConfigurator` | Sets the `help` summary. |\n| `.WithAlias(string alias)` | `ICommandConfigurator` | Adds an alternative trigger name. Chainable — call multiple times for multiple aliases. |\n| `.RestrictedTo(EnvironmentTag tags)` | `ICommandConfigurator` | Bitmask filter. Commands outside the active environment are silently excluded at bootstrap. `EnvironmentTag.Any` (default) appears in all environments. |\n\n`EnvironmentTag` values:\n\n| Value | Bitmask | Intended use |\n|---|---|---|\n| `Editor` | `1` | Unity Editor only |\n| `Development` | `2` | Development builds |\n| `Release` | `4` | Production builds |\n| `Any` | `7` | Always available (default) |\n\n```csharp\n// Strip cheat commands from Release builds automatically\nbuilder.WithName(\"give.item\")\n    .RestrictedTo(EnvironmentTag.Editor | EnvironmentTag.Development)\n    ...\n```\n\n---\n\n### `ICommandConfigurator` — Parameter Binding\n\n\u003e **⚠️ ARCHITECTURE RULE:** Required parameters **must** be declared before optional parameters. Violating this order throws a `ShellConfigurationException` at startup. Positional argument binding follows declaration order exactly.\n\n| Method | Description |\n|---|---|\n| `.AddParameter\u003cT\u003e(string name)` | Declares a **required** positional parameter of type `T`. `name` is used for named argument syntax (e.g., `-count 5`). `T` must have a registered `ITypeParser\u003cT\u003e`. |\n| `.AddOptionalParameter\u003cT\u003e(string name, T defaultValue)` | Declares an **optional** parameter. If the user omits it, `defaultValue` is injected silently. |\n| `.WithSuggestions(IEnumerable\u003cstring\u003e list)` | Attaches a static autocomplete list to the **last declared parameter**. |\n| `.WithSuggestions(Func\u003cSuggestionContext, IEnumerable\u003cstring\u003e\u003e provider)` | Attaches a dynamic suggestion provider to the **last declared parameter**. |\n| `.WithSuggestions(ISuggestionProvider provider)` | Attaches a reusable `ISuggestionProvider` class to the **last declared parameter**. |\n| `.WithTimeout(TimeSpan timeout)` | **Required** before `.ExecutesInteractiveAsync(...)`. Sets the maximum wall-clock duration before the interactive session is force-cancelled. |\n\n**Supported parameter types out of the box:**\n\n| Category | Types |\n|---|---|\n| Primitives | `int`, `uint`, `long`, `ulong`, `short`, `ushort`, `byte`, `sbyte`, `float`, `double`, `decimal`, `bool`, `char`, `string` |\n| System | `Guid`, `DateTime`, `TimeSpan` |\n| Unity structs | `Vector2`, `Vector3`, `Vector4`, `Vector2Int`, `Vector3Int`, `Quaternion`, `Color`, `Rect`, `Bounds` |\n| Unity objects | `GameObject` (resolved via `GameObject.Find`), `Transform` |\n| Special | Any `enum` (case-insensitive, suggestions auto-generated), `T[]`, `List\u003cT\u003e`, `HashSet\u003cT\u003e`, `Stack\u003cT\u003e`, `Queue\u003cT\u003e` |\n\n**Argument syntax examples in the console:**\n\n```\n# Positional\n\u003e spawn Orc 3\n\n# Named (any order)\n\u003e spawn -name Orc -count 3\n\n# Array literal\n\u003e batch.exec [kill, respawn, heal]\n\n# Session variable\n\u003e $boss = spawn Dragon\n\u003e set_health $boss 9999\n```\n\n---\n\n### `ICommandConfigurator` — Execution Delegates\n\nEvery configuration chain **must** terminate with exactly one `Executes*` call. The type parameters of the delegate **must** match the parameter types declared via `AddParameter` / `AddOptionalParameter` in the same order.\n\n| Overload family | Signature pattern | Notes |\n|---|---|---|\n| `Executes(Action)` | No parameters | |\n| `Executes\u003cT1…T5\u003e(Action\u003cT1…T5\u003e)` | Up to 5 typed params | Synchronous, no return value |\n| `ExecutesReturning\u003cTResult\u003e(Func\u003cTResult\u003e)` | No params, has return | Return value is printed as a success log |\n| `ExecutesReturning\u003cT1…T5, TResult\u003e(Func\u003cT1…T5, TResult\u003e)` | Up to 5 params + return | Return value is printed as a success log |\n| `ExecutesAsync(Func\u003cTask\u003e)` | No params, async | Exception is caught and printed gracefully |\n| `ExecutesAsync\u003cT1…T5\u003e(Func\u003cT1…T5, Task\u003e)` | Up to 5 params, async | |\n| `ExecutesInteractiveAsync(Func\u003cICommandContext, Task\u003e)` | Context only | Locks the shell. Requires `.WithTimeout()`. |\n| `ExecutesInteractiveAsync\u003cT1…T5\u003e(Func\u003cICommandContext, T1…T5, Task\u003e)` | Context + up to 5 params | `ICommandContext` is always the first argument. |\n\n\u003e **⚠️ ARCHITECTURE RULE:** If the count or types of the generic arguments on `Executes*` do not match the declared parameters, a `ShellConfigurationException` is thrown immediately at startup — not at runtime when a user types the command.\n\n---\n\n### Complete Profile Example\n\n```csharp\nusing System;\nusing System.Collections.Generic;\nusing UShell.Runtime.Core.Abstractions;\nusing UShell.Runtime.Core.Commands;\nusing UShell.Runtime.Core.Commands.Fluent;\nusing UShell.Runtime.Core.Output;\nusing UShell.Runtime.Core.Suggestions;\n\npublic class ItemProfile : ShellProfile\n{\n    private readonly IItemDatabase _db;\n\n    public ItemProfile(IConsolePrinter printer, IItemDatabase db) : base(printer)\n    {\n        _db = db;\n    }\n\n    protected override void Configure(ICommandBuilder builder)\n    {\n        builder.WithName(\"item.give\")\n            .WithDescription(\"Adds an item to the player's inventory.\")\n            .WithAlias(\"give\")\n            .RestrictedTo(EnvironmentTag.Editor | EnvironmentTag.Development)\n            .AddParameter\u003cstring\u003e(\"itemId\")\n                .WithSuggestions(GetItemSuggestions)          // dynamic\n            .AddOptionalParameter\u003cint\u003e(\"amount\", 1)\n            .AddOptionalParameter\u003cbool\u003e(\"equip\", false)\n            .Executes\u003cstring, int, bool\u003e(GiveItem);\n\n        builder.WithName(\"item.list\")\n            .WithDescription(\"Prints all items in the player's inventory.\")\n            .Executes(ListItems);\n    }\n\n    private void GiveItem(string id, int amount, bool equip)\n    {\n        if (!_db.TryGet(id, out var item))\n        {\n            PrintError($\"Unknown item id '{id}'.\");\n            return;\n        }\n        Inventory.Local.Add(item, amount, equip);\n        PrintSuccess($\"Gave {amount}× {item.DisplayName}. Equipped: {equip}\");\n    }\n\n    private void ListItems()\n    {\n        var items = Inventory.Local.GetAll();\n        var headers = new[] { \"ID\", \"Name\", \"Qty\" };\n        var rows = new List\u003cIReadOnlyList\u003cstring\u003e\u003e();\n        foreach (var entry in items)\n            rows.Add(new[] { entry.Id, entry.DisplayName, entry.Count.ToString() });\n        PrintTable(headers, rows);\n    }\n\n    private IEnumerable\u003cstring\u003e GetItemSuggestions(SuggestionContext ctx)\n        =\u003e _db.GetAllIds();\n}\n```\n\n**Console usage:**\n\n```\n\u003e item.give sword 2 true\n  Gave 2× Iron Sword. Equipped: True\n\n\u003e give potion 5\n  Gave 5× Health Potion. Equipped: False\n\n\u003e item.list\n  ID        | Name           | Qty\n  ----------+----------------+----\n  sword     | Iron Sword     | 2\n  potion    | Health Potion  | 5\n```\n\n---\n\n## ⏳ Interactive Commands (`ICommandContext`)\n\nInteractive commands can suspend their execution to collect input, stream live diagnostics, or confirm destructive actions — all without blocking the main thread.\n\n**Three hard rules for interactive commands:**\n\n1. Call `.WithTimeout(TimeSpan)` before `.ExecutesInteractiveAsync(...)` — without it, a `ShellConfigurationException` is thrown at startup.\n2. The first argument of the delegate is always `ICommandContext`. Do not declare it as a parameter via `AddParameter`.\n3. Always respect `ctx.Token` — pass it to all awaitable calls. Pressing `Escape` or hitting the timeout triggers cancellation.\n\n### `ICommandContext` API\n\n| Member | Type | Description |\n|---|---|---|\n| `Token` | `CancellationToken` | Triggered on timeout or user Escape. Pass to all awaitable calls. |\n| `ConfirmAsync(string msg)` | `Task\u003cbool\u003e` | Suspends execution. Displays `msg (y/n)` and awaits user input. Returns `true` for `y`/`yes`. |\n| `PromptAsync(string msg)` | `Task\u003cstring\u003e` | Suspends execution. Displays `msg` and awaits a free-form string. |\n| `CreateProgressBar(string taskName)` | `IProgressReporter` | Spawns a live progress bar in the log stream. Dispose it to mark 100% complete. |\n| `Print(string)` | `void` | Standard log. Thread-safe during async execution. |\n| `PrintSuccess(string)` | `void` | Success (green) log. |\n| `PrintWarning(string)` | `void` | Warning (amber) log. |\n| `PrintError(string)` | `void` | Error (red) log. |\n\n### `IProgressReporter` API\n\n| Member | Description |\n|---|---|\n| `Report(float progress, string status)` | Updates the bar. `progress` is `0.0–1.0`. Disposing the reporter completes the bar automatically. |\n\n### Interactive Example\n\n```csharp\nbuilder.WithName(\"db.rebuild\")\n    .WithDescription(\"Wipes and rebuilds the local player database.\")\n    .WithTimeout(TimeSpan.FromMinutes(2))\n    .ExecutesInteractiveAsync(RebuildDatabaseAsync);\n\nprivate async Task RebuildDatabaseAsync(ICommandContext ctx)\n{\n    bool confirmed = await ctx.ConfirmAsync(\"This will erase all local data. Proceed?\");\n    if (!confirmed)\n    {\n        ctx.PrintWarning(\"Operation cancelled.\");\n        return;\n    }\n\n    string env = await ctx.PromptAsync(\"Target environment (dev/staging/prod):\");\n    ctx.PrintWarning($\"Targeting: {env}\");\n\n    using (IProgressReporter bar = ctx.CreateProgressBar(\"Rebuilding database\"))\n    {\n        for (int step = 0; step \u003c= 10; step++)\n        {\n            ctx.Token.ThrowIfCancellationRequested();\n            bar.Report(step / 10f, $\"Step {step}/10\");\n            await Task.Delay(500, ctx.Token);\n        }\n    }\n\n    ctx.PrintSuccess(\"Database rebuilt successfully.\");\n}\n```\n\n**Console interaction:**\n\n```\n\u003e db.rebuild\n  This will erase all local data. Proceed? (y/n)\n\u003e y\n  Target environment (dev/staging/prod):\n\u003e staging\n  ⚠ Targeting: staging\n  [Rebuilding database] ████████████████████ 100%\n  ✓ Database rebuilt successfully.\n```\n\n\u003e **⚠️ ARCHITECTURE RULE:** When an interactive command is running, the shell is **locked**. The user cannot type or execute other commands. The only allowed input is text for pending `PromptAsync` / `ConfirmAsync` calls. Pressing `Escape` calls `IInteractiveSession.Cancel()`, which triggers the `CancellationToken`.\n\n---\n\n## 🔍 Autocomplete \u0026 Suggestions\n\nUShell uses a **Fuzzy Matcher** backed by a Trie for O(k) prefix lookup and a scoring heuristic for ranking results. Tab-completion fills in the top-scored suggestion. Up to 5 ranked signatures are displayed below the input field as ghost tooltips.\n\n### Static Suggestions\n\nProvide a fixed list attached to the last declared parameter:\n\n```csharp\n.AddParameter\u003cstring\u003e(\"biome\")\n.WithSuggestions(new[] { \"forest\", \"desert\", \"tundra\", \"ocean\" })\n```\n\n### Dynamic Suggestions\n\nCompute the list at suggestion-request time. Receives a `SuggestionContext` with the partial text already typed:\n\n```csharp\n.AddParameter\u003cstring\u003e(\"playerName\")\n.WithSuggestions(ctx =\u003e\n    ServerState.GetConnectedPlayers()\n               .Where(p =\u003e p.Name.StartsWith(ctx.PartialValue, StringComparison.OrdinalIgnoreCase))\n               .Select(p =\u003e p.Name))\n```\n\n### Reusable Provider Class\n\nFor complex logic shared across multiple commands, implement `ISuggestionProvider`:\n\n```csharp\npublic class SceneNameProvider : ISuggestionProvider\n{\n    public IEnumerable\u003cstring\u003e GetSuggestions(SuggestionContext context)\n    {\n        int count = SceneManager.sceneCountInBuildSettings;\n        for (int i = 0; i \u003c count; i++)\n        {\n            string path = SceneUtility.GetScenePathByBuildIndex(i);\n            string name = System.IO.Path.GetFileNameWithoutExtension(path);\n            if (name.StartsWith(context.PartialValue, StringComparison.OrdinalIgnoreCase))\n                yield return name;\n        }\n    }\n}\n\n// Registration:\n.AddParameter\u003cstring\u003e(\"sceneName\")\n.WithSuggestions(new SceneNameProvider())\n```\n\n\u003e **Note:** `enum` and `bool` parameters automatically generate their own suggestions (`Enum.GetNames` and `[\"true\",\"false\",\"1\",\"0\"]` respectively). You do not need to configure them manually.\n\n---\n\n## 🔠 Custom Type Parsers (`ITypeParser\u003cT\u003e`)\n\nThe `ArgumentBinder` converts raw string tokens into C# types. To teach UShell how to interpret a custom type, inherit from `TypeParser\u003cT\u003e` and implement `ParseTyped`.\n\n### Implementation Contract\n\n```csharp\nusing UShell.Runtime.Core.Execution;\nusing UShell.Runtime.Core.Diagnostics;\nusing UShell.Runtime.Core.Parsing.Types;\n\n// Custom type: public readonly struct PlayerId { public int Value; }\n\npublic class PlayerIdParser : TypeParser\u003cPlayerId\u003e\n{\n    private readonly IPlayerRegistry _registry;\n\n    public PlayerIdParser(IPlayerRegistry registry)\n    {\n        _registry = registry;\n    }\n\n    public override ExecutionResult\u003cPlayerId\u003e ParseTyped(string input)\n    {\n        if (!int.TryParse(input, out int id))\n        {\n            return ExecutionResult\u003cPlayerId\u003e.Failure(\n                ShellError.Create(ShellErrorCode.Bind_CustomError, -1,\n                    $\"'{input}' is not a valid integer.\"));\n        }\n\n        if (!_registry.Exists(id))\n        {\n            return ExecutionResult\u003cPlayerId\u003e.Failure(\n                ShellError.Create(ShellErrorCode.Bind_CustomError, -1,\n                    $\"No player with ID {id} found.\"));\n        }\n\n        return ExecutionResult\u003cPlayerId\u003e.Success(new PlayerId { Value = id });\n    }\n}\n```\n\nUse `ShellError.Create(ShellErrorCode.Bind_CustomError, position, message)` for failures. The `position` argument is used by the `ErrorVisualizer` to point a caret at the offending token in the error output.\n\n### Registration\n\nIn your `IShellConfigurator.Configure` or `CoreShellBootstrapper`:\n\n```csharp\nbuilder.AddTypeParser(new PlayerIdParser(playerRegistry));\n```\n\nNow `AddParameter\u003cPlayerId\u003e(\"target\")` works in any profile.\n\n### ScriptableObject Lookup Example\n\n```csharp\npublic class ItemDefinitionParser : TypeParser\u003cItemDefinition\u003e\n{\n    private readonly ItemDatabase _db;\n\n    public ItemDefinitionParser(ItemDatabase db) { _db = db; }\n\n    public override ExecutionResult\u003cItemDefinition\u003e ParseTyped(string input)\n    {\n        var item = _db.FindById(input);\n        if (item == null)\n            return ExecutionResult\u003cItemDefinition\u003e.Failure(\n                ShellError.Create(ShellErrorCode.Bind_CustomError, -1,\n                    $\"Item '{input}' not found in database.\"));\n\n        return ExecutionResult\u003cItemDefinition\u003e.Success(item);\n    }\n}\n```\n\n---\n\n## 💾 Session State \u0026 Macros\n\n`ISessionState` is a runtime key-value store that survives across command calls within the same play session.\n\n### Variable Syntax in the Console\n\n```\n# Assign a literal\n\u003e $speed = 5.5\n\n# Assign the return value of a command (ExecutesReturning)\n\u003e $boss = spawn Dragon\n\n# Use variables as arguments\n\u003e set_speed $boss $speed\n\n# Math eval with variables\n\u003e eval $speed * 2 + 10\n  21.0\n```\n\n### Built-In Macro Commands\n\n| Command | Description |\n|---|---|\n| `macro.list` | Prints all currently stored variables and their values. |\n| `macro.delete \u003cname\u003e` | Deletes a specific variable from the session. |\n| `macro.clear` | Wipes all variables. |\n\n### Accessing Session State from C#\n\n`ISessionState` is available in `ShellBootstrapContext.SessionState` during bootstrapping and can be injected into profiles that need to read or write variables programmatically:\n\n| Method | Description |\n|---|---|\n| `TryGetValue(string name, out object value)` | Returns `true` and sets `value` if the variable exists. |\n| `SetValue(string name, object value)` | Programmatically writes a variable. |\n| `Clear()` | Removes all stored variables. |\n\n---\n\n## 🎨 Output, Formatting \u0026 Theming\n\n### `ShellProfile` Output Methods\n\nUse these instead of `Debug.Log` inside any `ShellProfile`. They route through `IConsolePrinter`, ensuring theme-aware colour application and correct mirroring to the Unity Console if enabled.\n\n| Method | Log colour | Use for |\n|---|---|---|\n| `Print(string)` | Default (warm grey) | Neutral informational text |\n| `PrintSuccess(string)` | Green | Confirmations, results |\n| `PrintWarning(string)` | Amber | Non-fatal issues, user hints |\n| `PrintError(string)` | Red | Failures, validation errors |\n| `PrintList(string title, IReadOnlyList\u003cstring\u003e items, int limit)` | Mixed | Numbered lists with truncation |\n| `PrintTable(headers, rows, TableStyle)` | Mixed | ASCII grid tables |\n\n### `PrintTable` and `TableStyle`\n\n```csharp\nvar headers = new[] { \"Command\", \"Alias\", \"Description\" };\nvar rows = new List\u003cIReadOnlyList\u003cstring\u003e\u003e\n{\n    new[] { \"scene.load\", \"sl\",  \"Loads a scene by name or index.\" },\n    new[] { \"scene.list\", \"\",    \"Lists all scenes in the build.\" },\n};\n\n// TableStyle.Standard — separator only under header row\nPrintTable(headers, rows, TableStyle.Standard);\n\n// TableStyle.Grid — separator between every row\nPrintTable(headers, rows, TableStyle.Grid);\n```\n\n### `ShellPalette` — Design Tokens\n\nNever hardcode hex colours in profile output. `ShellPalette` provides semantic constants that adapt if the theme changes.\n\n| Category | Constants |\n|---|---|\n| Text | `TextPrimary`, `TextSecondary`, `TextMuted`, `TextDim`, `TextHint` |\n| Semantic | `Success`, `Warning`, `Error`, `ErrorMuted` |\n| Syntax highlighting | `SyntaxCommand`, `SyntaxParam`, `SyntaxType`, `SyntaxValue`, `SyntaxNumber`, `SyntaxKeyword`, `SyntaxAlias`, `SyntaxUsage` |\n| Decoration | `HeaderRule`, `TableHeader`, `TableSeparator`, `TableCell`, `Ruler` |\n| Environment badges | `BadgeEditor`, `BadgeDev`, `BadgeRelease` |\n| Metrics | `StatGood`, `StatWarn`, `StatCritical`, `StatLabel`, `StatUnit` |\n\n`ShellPalette` also exposes two helpers:\n\n```csharp\n// Returns StatGood / StatWarn / StatCritical — lower is worse (e.g., frame time)\nstring color = ShellPalette.MetricColor(frameTimeMs, warnThreshold: 16f, critThreshold: 33f);\n\n// Higher is worse inverted — lower is worse (e.g., FPS)\nstring color = ShellPalette.MetricColorInverted(fps, goodThreshold: 60f, warnThreshold: 30f);\n```\n\n### `RichText` Utility\n\n`RichText` wraps TextMeshPro rich text tags with a concise API:\n\n```csharp\nusing UShell.Runtime.Core.Output.Formatting;\n\nstring colored  = RichText.Color(\"text\", ShellPalette.SyntaxValue);\nstring bold     = RichText.Bold(\"important\");\nstring italic   = RichText.Italic(\"note\");\nstring combined = RichText.Bold(RichText.Color(\"5000 HP\", ShellPalette.StatCritical));\n\nPrint($\"Spawned {RichText.Color(\"Dragon\", ShellPalette.SyntaxValue)} with {combined}.\");\n```\n\n### `ProfileFormatter` Layout Helpers\n\n```csharp\nusing UShell.Runtime.Core.Output.Formatting;\n\n// Produces: ── my section ─────────────────\nstring header = ProfileFormatter.FormatSectionHeader(\"my section\");\n\n// Appends \"  Key    : Value\\n\" with alignment padding\nvar sb = new StringBuilder();\nProfileFormatter.AppendKeyValue(sb, \"Scene\", \"Level_01\");\nProfileFormatter.AppendKeyValue(sb, \"Build Index\", \"3\");\nPrint(sb.ToString());\n```\n\n\u003e **⚠️ ARCHITECTURE RULE:** Never hardcode hex colour literals directly in `Print` calls. Always use `ShellPalette` constants. This is the only way to guarantee that your custom profile output remains visually coherent if the theme asset is swapped.\n\n---\n\n## 🖥️ UI Customization \u0026 Input Adapters\n\n### `UShellUIConfiguration` ScriptableObject\n\nLocated at `Assets/Plugins/UShell/UShell_UI_Configuration.asset`. Assign a modified copy to the `ConsoleView` component or create a new one via `Assets → Create → UShell → UI Configuration`.\n\n| Field | Description |\n|---|---|\n| `MaxLogs` | Maximum number of log entries kept alive in the scroll view before oldest are destroyed. |\n| `MainFont` | `TMP_FontAsset` used across all console elements. |\n| `InputFontSize` | Font size for the command input field. |\n| `LogFontSize` | Font size for all log entry lines. |\n| `ForceGlobalMonospace` | Forces all TextMeshPro text to use a fixed character width. **Enable this if your font is not monospaced**, otherwise ASCII table columns will misalign. |\n| `GlobalMonospaceWidth` | Character width in `em` units when `ForceGlobalMonospace` is on. `0.6` works for most proportional fonts. |\n| `GhostTextColor` | Colour of the faded autocomplete overlay text that appears over the user's input. |\n| `PromptColor` | Colour of the `\u003e` prompt prefix. |\n| `SuggestionBackgroundColor` | Background tint for the command signature tooltip panel. |\n| `StandardLogColor` | Default text colour. |\n| `SuccessLogColor` | Green text colour. |\n| `WarningLogColor` | Amber text colour. |\n| `ErrorLogColor` | Red text colour. |\n| `StandardIcon` / `SuccessIcon` / `WarningIcon` / `ErrorIcon` | Per-severity icon sprites rendered next to each log line. |\n\n### Custom Input Provider (`IInputProvider`)\n\nUShell ships with `InputSystemProvider` (Unity New Input System). To replace it — for Rewired, legacy `Input`, or any other system — implement `IInputProvider` on a `MonoBehaviour` and attach it to the `UShellManager` GameObject. `ConsoleRuntimeEngine` will detect and use it automatically.\n\n```csharp\nusing System;\nusing UnityEngine;\nusing UShell.Runtime.Unity.Inputs;\n\npublic class RewiredInputProvider : MonoBehaviour, IInputProvider\n{\n    public event Action OnToggleConsole;\n    public event Action OnSubmit;\n    public event Action OnHistoryUp;\n    public event Action OnHistoryDown;\n    public event Action OnAutocomplete;\n    public event Action OnEscape;\n\n    private bool _uiActive;\n\n    private void Update()\n    {\n        // Replace with your Rewired player.GetButtonDown(...) calls\n        if (Input.GetKeyDown(KeyCode.BackQuote))  OnToggleConsole?.Invoke();\n        if (!_uiActive) return;\n        if (Input.GetKeyDown(KeyCode.Return))     OnSubmit?.Invoke();\n        if (Input.GetKeyDown(KeyCode.UpArrow))    OnHistoryUp?.Invoke();\n        if (Input.GetKeyDown(KeyCode.DownArrow))  OnHistoryDown?.Invoke();\n        if (Input.GetKeyDown(KeyCode.Tab))        OnAutocomplete?.Invoke();\n        if (Input.GetKeyDown(KeyCode.Escape))     OnEscape?.Invoke();\n    }\n\n    public void SetUIInputActive(bool active) =\u003e _uiActive = active;\n}\n```\n\n**Default key bindings** (`InputSystemProvider`):\n\n| Action | Key |\n|---|---|\n| Toggle console | Backquote / Tilde |\n| Submit command | Enter / Numpad Enter |\n| History up | Up Arrow |\n| History down | Down Arrow |\n| Autocomplete | Tab |\n| Abort / Close | Escape |\n\n---\n\n## 🔌 Programmatic API (`IUShellAPI`)\n\n`UShellManager.API` is a globally accessible, statically typed facade that lets you interact with the console programmatically from any game system — without direct coupling to internal shell dependencies or low-level UI implementations.\n\n### Quick Examples\n\n```csharp\nusing UnityEngine;\nusing UShell.Runtime.Unity.API;\nusing UShell.Runtime.Core.Configuration;\n\npublic class GameManager : MonoBehaviour\n{\n    private void OnEnable()\n    {\n        // React to visibility changes (e.g., disabling player controls)\n        UShellManager.API.OnConsoleOpened += LockPlayerControls;\n        UShellManager.API.OnConsoleClosed += UnlockPlayerControls;\n\n        // Log system messages directly to the terminal from external systems\n        UShellManager.API.OnLogAdded += LogToExternalCloud;\n    }\n\n    private void OnDisable()\n    {\n        UShellManager.API.OnConsoleOpened -= LockPlayerControls;\n        UShellManager.API.OnConsoleClosed -= UnlockPlayerControls;\n        UShellManager.API.OnLogAdded -= LogToExternalCloud;\n    }\n\n    public void TriggerDiagnostics()\n    {\n        // Force-execute any registered command as if the user typed it\n        UShellManager.API.ExecuteCommand(\"stats\");\n    }\n\n    public async void LoadDeveloperDLC()\n    {\n        // Dynamically alter console commands and type parsers on the fly\n        IConfigurationTransaction transaction = UShellManager.API.BeginConfiguration();\n        \n        transaction\n            .AddProfile(new DebugCheatsProfile(UShellManager.API))\n            .AddTypeParser(new SpawnWeightParser())\n            .RemoveProfile\u003cProductionTelemetryProfile\u003e();\n\n        // Transaction waits for executing tasks, locks input, and mutates in-place\n        await transaction.ApplyAsync();\n    }\n\n    private void LockPlayerControls() =\u003e Debug.Log(\"Controls Locked.\");\n    private void UnlockPlayerControls() =\u003e Debug.Log(\"Controls Unlocked.\");\n    private void LogToExternalCloud(UShell.Runtime.Core.Output.LogEntry entry) { /* ... */ }\n}\n```\n\n---\n\n### `IUShellAPI` Reference\n\n#### Properties\n\n| Property | Type | Description |\n|---|---|---|\n| `Core` | `IShellCore` | Provides raw compile-time read-only access to the shell core engine registers (History, Session, ParserRegistry). |\n| `View` | `ConsoleView` | Provides direct access to the console canvas UI view layout controller. |\n| `Printer` | `IConsolePrinter` | Provides direct access to the active output console printer instance. |\n| `Controller` | `IShellController` | Provides direct access to the lifecycle event aggregation controller. |\n| `IsVisible` | `bool` | Returns `true` if the console overlay canvas is currently active on screen. |\n| `IsExecutingInteractiveCommand` | `bool` | Returns `true` if an interactive command currently holds an exclusive session lock. |\n| `CurrentInputText` | `string` | Retrieves the exact raw string currently entered into the terminal's input field. |\n| `TotalLogsCount` | `int` | Returns the current total number of visual log items rendered in the viewport. |\n\n#### Events\n\n| Event | Signature | Fires When |\n|---|---|---|\n| `OnConsoleOpened` | `Action` | The console canvas is activated and acquires visual/keyboard focus. |\n| `OnConsoleClosed` | `Action` | The console canvas is deactivated and relinquishes focus. |\n| `OnConsoleCleared` | `Action` | All logged visual entries are wiped via a clear request. |\n| `OnInputTextChanged` | `Action\u003cstring\u003e` | The text inside the input field is modified by typing or autocomplete. |\n| `OnCommandExecuting` | `Action\u003cstring\u003e` | A command string has been submitted and is about to enter the parse pipeline. |\n| `OnCommandExecuted` | `Action\u003cstring, ExecutionResult\u003cobject?\u003e\u003e` | A command execution completes, yielding either a result payload or a syntax/binding error. |\n| `OnLogAdded` | `Action\u003cLogEntry\u003e` | A new log entry (Standard, Success, Warning, or Error) is dispatched to the output pipeline. |\n\n#### Methods\n\n| Method | Return Type | Description |\n|---|---|---|\n| `Show()` | `void` | Forces the console overlay UI to open, displaying it on screen and focusing the cursor. |\n| `Hide()` | `void` | Closes the console overlay UI and releases focus. |\n| `Clear()` | `void` | Programmatically triggers a complete clearing of all visual log items. |\n| `ExecuteCommand(string rawCommand)` | `void` | Executes a raw command line string programmatically (e.g. `ExecuteCommand(\"time.scale 0.5\")`). |\n| `BeginConfiguration()` | `IConfigurationTransaction` | Spawns a fluent, transactional Unit of Work to dynamically add/remove profiles and type parsers in-place during runtime. |\n---\n\n## 📚 Built-In Profiles Reference\n\nAll built-in profiles are toggled via the `ComponentShellBootstrapper` Inspector or explicitly registered in `CoreShellBootstrapper`.\n\n### `ConsoleManagementProfile`\n\n| Command | Alias | Description |\n|---|---|---|\n| `help` | — | Lists all available commands. `help \u003ccommand\u003e` shows full signature. |\n| `clear` | `cls` | Clears the console log. |\n| `history` | — | Prints command history. `history -limit N` controls count. |\n| `alias` | — | Lists all registered command aliases. |\n| `macro.list` | — | Prints all active session variables. |\n| `macro.delete \u003cname\u003e` | — | Deletes a specific session variable. |\n| `macro.clear` | — | Clears all session variables. |\n\n### `EnvironmentInfoProfile`\n\n| Command | Description |\n|---|---|\n| `info` | Prints Unity version, platform, and device info. |\n| `platform` | Prints current `RuntimePlatform`. |\n| `game.version` | Prints `Application.version`. |\n| `env` | Prints the active `EnvironmentTag`. |\n\n### `ApplicationSettingsProfile`\n\n| Command | Description |\n|---|---|\n| `screen.resolution \u003cwidth\u003e \u003cheight\u003e` | Changes screen resolution. |\n| `gfx.fps \u003climit\u003e` | Sets `Application.targetFrameRate`. |\n| `time.scale \u003cvalue\u003e` | Sets `Time.timeScale`. |\n\n### `SceneManagementProfile`\n\n| Command | Description |\n|---|---|\n| `scene.load \u003cname\\|index\u003e` | Loads a scene. |\n| `scene.active` | Prints the active scene name and index. |\n| `scene.list` | Lists all scenes in the build. |\n\n### `MathUtilityProfile`\n\n| Command | Description |\n|---|---|\n| `eval \u003cexpression\u003e` | Full math expression evaluator. Supports `+`, `-`, `*`, `/`, `%`, `^`, `sqrt`, `sin`, `cos`, `tan`, `log`, `abs`, `min`, `max`, `round`, `floor`, `ceil`, `fact`, `hex()`, `bin()`, and more. |\n| `random \u003cmin\u003e \u003cmax\u003e` | Returns a random float between min and max. |\n| `convert \u003cvalue\u003e \u003cfrom\u003e \u003cto\u003e` | Converts between units (metric/imperial, hex/bin/dec). |\n\n### `RuntimeDiagnosticsProfile`\n\n| Command | Description |\n|---|---|\n| `stats` | Live FPS and frame time. |\n| `mem` | GC heap size and total reserved memory. |\n| `gc` | Forces `GC.Collect()` and reports freed bytes. |\n| `objects` | Counts all active `UnityEngine.Object` instances by type. |\n| `layer.find \u003clayer\u003e` | Lists all GameObjects on a specified layer. |\n| `prefs.clear` | Calls `PlayerPrefs.DeleteAll()`. |\n\n### `GameObjectProfile`\n\n| Command | Description |\n|---|---|\n| `go.teleport \u003cname\u003e \u003cx\u003e \u003cy\u003e \u003cz\u003e` | Teleports a GameObject by name to a world position. |\n| `go.active \u003cname\u003e \u003cstate\u003e` | Sets `GameObject.SetActive`. |\n| `go.info \u003cname\u003e` | Prints position, rotation, scale, components. |\n\n### `HttpProfile`\n\nInteractive HTTP commands that pause the console while awaiting a network response.\n\n| Command | Description |\n|---|---|\n| `http.get \u003curl\u003e` | Sends a GET request and prints the response body. |\n| `http.post \u003curl\u003e \u003cbody\u003e` | Sends a POST request with a JSON body. |\n| `http.put \u003curl\u003e \u003cbody\u003e` | Sends a PUT request. |\n| `http.delete \u003curl\u003e` | Sends a DELETE request. |\n\n### `FileIOProfile`\n\n| Command | Description |\n|---|---|\n| `screenshot` | Captures a screenshot to `Application.persistentDataPath`. |\n| `file.read \u003cpath\u003e` | Reads and prints a text file. |\n| `file.write \u003cpath\u003e \u003ccontent\u003e` | Writes a string to a file. |\n\n---\n\n## 🔬 Architecture Deep Dive\n\nThis section is for contributors or developers extending the core subsystems.\n\n### Execution Pipeline Detail\n\n```\nUser types: \"item.give sword -amount 3 true\"\n                │\n                ▼\n         ┌─────────────┐\n         │    Lexer    │  Tokenises into: IDENTIFIER(\"item.give\"),\n         └──────┬──────┘  IDENTIFIER(\"sword\"), NAMED_ARG(\"-amount\"),\n                │         LITERAL(\"3\"), IDENTIFIER(\"true\")\n                ▼\n         ┌─────────────┐\n         │   Parser    │  Builds AST:\n         └──────┬──────┘  CommandNode {\n                │           name: \"item.give\",\n                │           args: [LiteralNode(\"sword\"),\n                │                  NamedArgumentNode(\"amount\", LiteralNode(\"3\")),\n                │                  LiteralNode(\"true\")]\n                │         }\n                ▼\n         ┌──────────────────┐\n         │  CommandRegistry │  Resolves \"item.give\" → CommandSignature.\n         └──────┬───────────┘  Filters by active EnvironmentTag.\n                │\n                ▼\n         ┌──────────────────┐\n         │ ArgumentBinder   │  Binds each AST node to its C# type\n         └──────┬───────────┘  via ITypeParser\u003cT\u003e. Handles positional,\n                │              named, optional, array, and variable nodes.\n                ▼\n         ┌──────────────────┐\n         │ ICommandInvoker  │  ActionInvoker / FuncInvoker /\n         └──────┬───────────┘  InteractiveFuncInvoker\n                │\n                ▼\n        Command delegate executes.\n        Result/errors → ReportingCommandExecutor → IConsolePrinter.\n```\n\n### Key Interfaces and Responsibilities\n\n| Interface | Responsibility |\n|---|---|\n| `IShellCore` | Aggregates `Registry`, `Executor`, `History`, `InteractiveSession`. |\n| `ICommandRegistry` | Read-only: `TryGetCommand`, `GetSuggestions`, `GetAllCommands`, `GetCompactSignature`. |\n| `ICommandExecutor` | `Execute(string)` + `OnExecuting` / `OnExecuted` events. |\n| `IConsolePrinter` | `Print(LogEntry)`, `UpdatePrint(Guid, LogEntry)`, `PrintTable(...)`. Abstracts the UI completely. |\n| `IInteractiveSession` | Manages the lock state during interactive command execution (`IsBusy`, `IsWaitingForPrompt`, `StartSession`, `EndSession`, `SubmitInput`, `Cancel`). |\n| `IShellController` | Provides `RequestClear()` / `RequestClose()` for profiles that need to affect shell lifecycle without referencing the view. |\n| `IArgumentBinder` | Maps `IReadOnlyList\u003cSyntaxNode\u003e` to `object[]` using registered parsers. |\n| `ITypeParser` | Non-generic base. `TypeParser\u003cT\u003e` provides the `Parse(string) → ExecutionResult\u003cobject?\u003e` bridge. |\n| `ITypeParser\u003cT\u003e` | `ParseTyped(string) → ExecutionResult\u003cT\u003e`. Implement this for custom types. |\n| `ISuggestionProvider` | `GetSuggestions(SuggestionContext) → IEnumerable\u003cstring\u003e`. |\n| `IInputProvider` | Hardware abstraction: six events + `SetUIInputActive(bool)`. |\n| `IShellProfile` | `RegisterCommands(ICommandBuilder builder)`. Implemented by `ShellProfile`. |\n| `IShellConfigurator` | `Configure(ShellBuilder builder, ShellBootstrapContext context)`. Unity-layer extension point. |\n| `IUShellAPI` | Public facade on `UShellManager.API`. |\n\n### Assembly Layout\n\n| Assembly | `noEngineReferences` | Contains |\n|---|---|---|\n| `UShell.Runtime.Core` | `true` | All engine-agnostic logic: parsing, binding, execution, history, session, output abstractions, formatting. Fully unit-testable without Unity. |\n| `UShell.Runtime.Unity` | `false` | Unity-specific: bootstrappers, `MonoBehaviour` components, Unity type parsers, `UnityConsolePrinter`, `InputSystemProvider`, UI components, built-in profiles. |\n\n---\n\n## 📄 License\n\nThis project is licensed under the **MIT License**. You are free to use, modify, and distribute it in personal or commercial projects. Attribution is appreciated but not mandatory.\n\nSee the [LICENSE](LICENSE) file for full terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frivgoo%2Fushell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frivgoo%2Fushell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frivgoo%2Fushell/lists"}