using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using LLM.Editor.Commands; using LLM.Editor.Data; using UnityEditor; using UnityEngine; namespace LLM.Editor.Core { /// /// Responsible for finding and executing commands from the session queue. /// [InitializeOnLoad] public static class CommandExecutor { private static List _commandQueue; private static CommandContext _currentContext; public static Action OnQueueUpdated; public static event Action OnContextReadyForNextTurn; static CommandExecutor() { // This will run when the editor loads, including after a recompile. EditorApplication.delayCall += Initialize; } private static void Initialize() { Debug.Log("[CommandExecutor] Initializing..."); if (SessionManager.HasActiveSession()) { _commandQueue = SessionManager.LoadCommandQueue(); if (!_commandQueue.Any()) { SessionManager.EndSession(); return; } Debug.Log($"[CommandExecutor] Resuming session with {_commandQueue.Count} command(s) in queue."); _currentContext = new CommandContext(); OnQueueUpdated?.Invoke(); } else { _commandQueue = new List(); } } public static void SetQueue(List commands) { _commandQueue = commands; _currentContext = new CommandContext(); SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); Debug.Log($"[CommandExecutor] Queue {_commandQueue.Count} contents:"); foreach (var command in commands) { Debug.Log($"[CommandExecutor]: {command.commandName}"); } Debug.Log("[CommandExecutor] Queue set."); } private static void ClearQueue() { _commandQueue.Clear(); SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); } public static bool HasPendingCommands() => _commandQueue != null;// && _commandQueue.Any(); public static CommandData GetNextCommand() => HasPendingCommands() ? _commandQueue.First() : null; public static void ExecuteNextCommand() { if (!HasPendingCommands()) { Debug.LogWarning("[CommandExecutor] No commands to execute."); return; } var commandData = _commandQueue.First(); try { var commandInstance = CreateCommandInstance(commandData); if (commandInstance != null) { Debug.Log($"[CommandExecutor] Executing: {commandData.commandName}"); var outcome = commandInstance.Execute(_currentContext); switch (outcome) { // Decide what to do based on the outcome case CommandOutcome.Success: // The command succeeded, so we can remove it and continue. _commandQueue.RemoveAt(0); break; case CommandOutcome.Error: Debug.LogError($"[CommandExecutor] Command '{commandData.commandName}' failed. Clearing remaining command queue."); ClearQueue(); break; case CommandOutcome.AwaitingNextTurn: { // The command has gathered context and is waiting for the next API call. // The queue is paused. The command remains at the top of the queue. Debug.Log("[CommandExecutor] Pausing queue. Awaiting next turn with LLM."); // Check if the command produced detailed context to send back. if (_currentContext.CurrentSubject is string detailedContextJson) { OnContextReadyForNextTurn?.Invoke(detailedContextJson); } break; } } } else { Debug.LogError($"[CommandExecutor] Could not create instance for command: {commandData.commandName}"); ClearQueue(); // Clear queue on critical error } } catch(Exception e) { Debug.LogError($"[CommandExecutor] Failed to execute command '{commandData.commandName}'"); Debug.LogException(e); ClearQueue(); } // Save the modified queue SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); } private static ICommand CreateCommandInstance(CommandData data) { // Use reflection to find the command class in the Commands namespace // This makes the system extensible without needing a giant switch statement. var commandClassName = data.commandName.EndsWith("Command") ? data.commandName : $"{data.commandName}Command"; var type = Assembly.GetExecutingAssembly().GetTypes() .FirstOrDefault(t => t.Namespace == "LLM.Editor.Commands" && t.Name == commandClassName); if (type != null) { // Assumes commands have a constructor that takes a single string (the JSON parameters) var jsonString = data.jsonData?.ToString() ?? "{}"; return (ICommand)Activator.CreateInstance(type, jsonString); } Debug.LogError($"[CommandExecutor] Command type '{commandClassName}' not found."); return null; } } }