@atelierai/uco 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +1 -1
  3. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetConfig.cs +132 -132
  4. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDependencyResolver.cs +237 -237
  5. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDownloader.cs +82 -82
  6. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/MenuItems.cs +247 -247
  7. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/ToolGroups.cs +153 -153
  8. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/SystemTool/Skills.Create.SkillBody.cs +155 -155
  9. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Console.GetLogs.cs +95 -95
  10. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Docs.HttpFetcher.cs +139 -139
  11. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Instance.ListAll.cs +72 -72
  12. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/PathReadHelper.cs +106 -106
  13. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Script.Validate.cs +252 -252
  14. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests/TestResultCollector.cs +537 -537
  15. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests.cs +400 -400
  16. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Branding/ProductInfo.cs +62 -62
  17. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/CopilotServerManager.cs +1034 -1034
  18. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Skills/Skill_InitialSetup.cs +197 -197
  19. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.Editor.cs +201 -201
  20. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.cs +67 -67
  21. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotListWindowBase.cs +393 -393
  22. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotWindowBase.cs +113 -113
  23. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.Connection.cs +3 -3
  24. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotFeatures.cs +195 -195
  25. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotServer.cs +20 -20
  26. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CreateGUI.cs +354 -354
  27. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.cs +78 -78
  28. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowInitializer.cs +47 -47
  29. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/SerializationCheckWindow.cs +190 -190
  30. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Build.cs +91 -91
  31. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs +298 -298
  32. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Static.cs +296 -296
  33. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.cs +69 -69
  34. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PackageUtils.cs +168 -168
  35. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PrefsKeyMigration.cs +119 -119
  36. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/SafeDefaultsGuard.cs +180 -180
  37. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ScriptUtils.cs +229 -229
  38. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolsManifestGenerator.cs +200 -200
  39. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringAdapters.cs +1467 -1467
  40. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringSafetyReloadGuard.cs +65 -65
  41. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityCopilotUpdateProjectSettings.cs +66 -66
  42. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/ReflectorNet.dll +0 -0
  43. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.Common.dll +0 -0
  44. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.dll +0 -0
  45. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Converter/Json/Structures/EntityIdConverter.cs +120 -120
  46. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.cs +110 -110
  47. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.pre-Unity.6.5.cs +110 -110
  48. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Unity/Logs/FileLogStorage.cs +547 -547
  49. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Build.cs +243 -243
  50. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Config.cs +3 -3
  51. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.cs +295 -295
  52. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginBuilder.cs +105 -105
  53. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.Static.cs +155 -155
  54. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.cs +59 -59
  55. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Utils/EnvironmentUtils.cs +11 -11
  56. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/AtomicApiTestFixtures.cs +99 -99
  57. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/TryModifyAtTests.cs +227 -227
  58. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AuthoringSafetyPilotTests.cs +1050 -1050
  59. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetLegacyMigrationTests.cs +304 -304
  60. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Environment/EnvironmentUtilsTests.cs +1 -1
  61. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/BlacklistTypeTests.cs +416 -416
  62. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.cs +172 -172
  63. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.pre-Unity.6.5.cs +169 -169
  64. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.cs +182 -182
  65. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.pre-Unity.6.5.cs +179 -179
  66. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.cs +175 -175
  67. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.pre-Unity.6.5.cs +174 -174
  68. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.cs +171 -171
  69. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.pre-Unity.6.5.cs +170 -170
  70. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsCreateFolderTests.cs +243 -243
  71. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsMaterialTests.cs +101 -101
  72. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.cs +348 -348
  73. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.pre-Unity.6.5.cs +348 -348
  74. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsShaderTests.cs +194 -194
  75. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.cs +573 -573
  76. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.pre-Unity.6.5.cs +513 -513
  77. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.cs +154 -154
  78. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.pre-Unity.6.5.cs +154 -154
  79. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotGameViewTests.cs +164 -164
  80. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotSceneViewTests.cs +155 -155
  81. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolParameterTests.cs +56 -56
  82. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.cs +651 -651
  83. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.pre-Unity.6.5.cs +651 -651
  84. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ToolBatchExecutionIntegrationTests.cs +407 -407
  85. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs → UcoPluginTests.cs} +105 -105
  86. package/vendor/plugin/com.atelierai.unity.copilot/package.json +1 -1
  87. /package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs.meta → UcoPluginTests.cs.meta} +0 -0
@@ -1,237 +1,237 @@
1
- /*
2
- +------------------------------------------------------------------+
3
- | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
- | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
- | Copyright (c) 2025 Ivan Murzak |
6
- | Licensed under the Apache License, Version 2.0. |
7
- | See the LICENSE file in the project root for more information. |
8
- +------------------------------------------------------------------+
9
- */
10
-
11
- #nullable enable
12
- using System;
13
- using System.Collections.Generic;
14
- using UnityEditor;
15
- using UnityEditor.PackageManager;
16
- using UnityEngine;
17
- using PackageManagerEvents = UnityEditor.PackageManager.Events;
18
-
19
- namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
20
- {
21
- /// <summary>
22
- /// Entry point for NuGet dependency management. Runs on every domain reload via [InitializeOnLoad]
23
- /// AND in response to UPM package upgrades via PackageManager.Events.registeredPackages.
24
- ///
25
- /// This assembly has ZERO external dependencies — it always compiles, even when the main plugin
26
- /// fails due to missing or conflicting DLLs. It downloads NuGet packages directly from nuget.org,
27
- /// extracts DLLs, skips assemblies Unity already provides, and sets the UNITY_COPILOT_READY define
28
- /// so the main plugin assemblies can compile.
29
- ///
30
- /// Why both triggers are needed:
31
- /// [InitializeOnLoad] fires after a *successful* project-wide recompile. When a user upgrades
32
- /// the Unity package and the new code references newer NuGet APIs whose DLL is still on disk
33
- /// at the old version, the post-import recompile fails, Unity blocks the domain reload, and
34
- /// the new resolver's static constructor never runs. The previous AppDomain is still alive
35
- /// though — and so are its event subscriptions. registeredPackages fires from the still-alive
36
- /// AppDomain after UPM writes new package files but before the failed recompile, giving us a
37
- /// chance to clean up stale DLLs and unblock the next compile attempt. See Unity-MCP#707.
38
- ///
39
- /// Flow on domain reload:
40
- /// 1. [InitializeOnLoad] fires
41
- /// 2. Deferred via EditorApplication.update (runs without editor focus, unlike delayCall)
42
- /// 3. NuGetPackageRestorer checks if all packages are installed
43
- /// 4. Downloads and installs any missing packages, removes stale-version siblings
44
- /// 5. Sets UNITY_COPILOT_READY scripting define
45
- /// 6. If packages were installed: triggers AssetDatabase.Refresh() → domain reload
46
- /// 7. On next reload: everything is in place, main plugin compiles
47
- ///
48
- /// Flow on UPM package change (no domain reload required to invoke):
49
- /// 1. registeredPackages fires from the previous AppDomain
50
- /// 2. NuGetPackageRestorer.Restore() runs the same install + cleanup logic
51
- /// 3. AssetDatabase.Refresh() prompts Unity to retry compilation with the corrected DLL set
52
- /// 4. On the resulting successful reload, [InitializeOnLoad] resumes the normal path
53
- /// </summary>
54
- [InitializeOnLoad]
55
- static class NuGetDependencyResolver
56
- {
57
- const string Tag = "[Unity-MCP DependencyResolver]";
58
-
59
- static NuGetDependencyResolver()
60
- {
61
- // In CI, skip runtime resolution — DLLs are committed to git
62
- // and UNITY_COPILOT_READY should already be in ProjectSettings.
63
- // Setting defines at runtime in batch mode causes "Error building Player
64
- // because scripts are compiling" when the test runner races the recompilation.
65
- if (IsCi())
66
- {
67
- RecompileGate.EnsureReadyDefine();
68
- return;
69
- }
70
-
71
- EditorApplication.update += ResolveOnce;
72
-
73
- // Subscribe to UPM package-change events so that when the user upgrades a package
74
- // whose new code references a newer NuGet DLL than the one currently on disk, we
75
- // can run the cleanup + restore from the still-alive AppDomain. The compile that
76
- // would otherwise fail (and block [InitializeOnLoad] in the next domain) gets
77
- // retried by Unity once AssetDatabase.Refresh() picks up the corrected DLL set.
78
- // -= before += guards against accidental double-subscription if a hot-reload path
79
- // ever re-runs this constructor without a full domain swap.
80
- PackageManagerEvents.registeredPackages -= OnRegisteredPackages;
81
- PackageManagerEvents.registeredPackages += OnRegisteredPackages;
82
- }
83
-
84
- /// <summary>
85
- /// Tracks whether a UPM-event-triggered restore is currently in flight, so a second
86
- /// event burst (UPM can fire multiple registeredPackages events for a single user
87
- /// action) doesn't kick off a parallel restore.
88
- /// </summary>
89
- static bool isRestoringFromPackageEvent;
90
-
91
- static void OnRegisteredPackages(PackageRegistrationEventArgs args)
92
- {
93
- if (isRestoringFromPackageEvent)
94
- return;
95
-
96
- // Cheap pre-filter: if no packages were added, removed, or upgraded, there is no
97
- // possibility of a NuGet-dep version change. Skip the heavier AllPackagesInstalled()
98
- // probe to avoid touching disk on every UPM ping.
99
- if ((args.added == null || args.added.Count == 0)
100
- && (args.removed == null || args.removed.Count == 0)
101
- && (args.changedFrom == null || args.changedFrom.Count == 0)
102
- && (args.changedTo == null || args.changedTo.Count == 0))
103
- {
104
- return;
105
- }
106
-
107
- isRestoringFromPackageEvent = true;
108
- try
109
- {
110
- Debug.Log($"{Tag} UPM package change detected — running NuGet restore.");
111
- RunRestoreAndRefresh();
112
- }
113
- catch (Exception ex)
114
- {
115
- // Same rationale as ResolveOnce: do NOT set UNITY_COPILOT_READY on a failed restore.
116
- Debug.LogError($"{Tag} Restore from UPM event failed: {ex}");
117
- }
118
- finally
119
- {
120
- isRestoringFromPackageEvent = false;
121
- }
122
- }
123
-
124
- /// <summary>
125
- /// Shared core used by both ResolveOnce (post-domain-reload) and OnRegisteredPackages
126
- /// (no-domain-reload UPM event path). AllPackagesInstalled() short-circuits the common
127
- /// no-op case without touching the network.
128
- /// </summary>
129
- static void RunRestoreAndRefresh()
130
- {
131
- if (NuGetPackageRestorer.AllPackagesInstalled())
132
- {
133
- NuGetPluginConfigurator.ConfigureAll();
134
- RecompileGate.EnsureReadyDefine();
135
- return;
136
- }
137
-
138
- Debug.Log($"{Tag} Restoring NuGet packages...");
139
- var changed = NuGetPackageRestorer.Restore();
140
-
141
- NuGetPluginConfigurator.ConfigureAll();
142
-
143
- RecompileGate.EnsureReadyDefine();
144
-
145
- if (changed)
146
- {
147
- Debug.Log($"{Tag} Packages restored. Refreshing AssetDatabase...");
148
- AssetDatabase.Refresh();
149
- }
150
- }
151
-
152
- static void ResolveOnce()
153
- {
154
- EditorApplication.update -= ResolveOnce;
155
- try
156
- {
157
- RunRestoreAndRefresh();
158
- }
159
- catch (Exception ex)
160
- {
161
- // Do NOT set UNITY_COPILOT_READY here: if restore/configuration failed, the DLL
162
- // layout is unknown/inconsistent, and letting main-plugin assemblies compile
163
- // against a partial/mismatched set (via defineConstraints) produces hard-to-
164
- // diagnose MissingMethodException / TypeLoadException at runtime. Surface the
165
- // failure loud and clear instead, so the user fixes the underlying problem
166
- // and retries (the next domain reload will run Restore again).
167
- Debug.LogError($"{Tag} Failed: {ex}");
168
- }
169
- }
170
-
171
- /// <summary>
172
- /// Checks if the current environment is a CI environment.
173
- /// Mirrors EnvironmentUtils.IsCi() but without external dependencies,
174
- /// since this assembly must compile standalone.
175
- /// Checks both command-line arguments and environment variables for
176
- /// CI, GITHUB_ACTIONS, and TF_BUILD (Azure Pipelines).
177
- /// </summary>
178
- static bool IsCi()
179
- {
180
- var args = ParseCommandLineArguments();
181
-
182
- var ci = GetArgOrEnv(args, "CI");
183
- var gha = GetArgOrEnv(args, "GITHUB_ACTIONS");
184
- var az = GetArgOrEnv(args, "TF_BUILD");
185
-
186
- return IsTrue(ci) || IsTrue(gha) || IsTrue(az);
187
-
188
- static string? GetArgOrEnv(Dictionary<string, string?> args, string key)
189
- => args.TryGetValue(key, out var v) ? v : Environment.GetEnvironmentVariable(key);
190
-
191
- static bool IsTrue(string? value)
192
- => string.Equals(value?.Trim()?.Trim('"'), "true", StringComparison.OrdinalIgnoreCase);
193
- }
194
-
195
- /// <summary>
196
- /// Parses Unity command-line arguments into a dictionary.
197
- /// Handles both "-key value" and "-key=value" forms, plus bare flags like "-batchmode".
198
- /// Keys are stored WITHOUT the leading dash.
199
- /// </summary>
200
- static Dictionary<string, string?> ParseCommandLineArguments()
201
- {
202
- var result = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
203
- var rawArgs = Environment.GetCommandLineArgs();
204
-
205
- for (var i = 0; i < rawArgs.Length; i++)
206
- {
207
- var arg = rawArgs[i];
208
- if (!arg.StartsWith("-"))
209
- continue;
210
-
211
- var key = arg.TrimStart('-');
212
-
213
- // Handle -key=value form
214
- var eqIndex = key.IndexOf('=');
215
- if (eqIndex >= 0)
216
- {
217
- result[key.Substring(0, eqIndex)] = key.Substring(eqIndex + 1);
218
- continue;
219
- }
220
-
221
- // Handle -key value form (next arg is value if it doesn't start with -)
222
- if (i + 1 < rawArgs.Length && !rawArgs[i + 1].StartsWith("-"))
223
- {
224
- result[key] = rawArgs[++i];
225
- }
226
- else
227
- {
228
- // Bare flag like -batchmode
229
- result[key] = null;
230
- }
231
- }
232
-
233
- return result;
234
- }
235
-
236
- }
237
- }
1
+ /*
2
+ +------------------------------------------------------------------+
3
+ | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
+ | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
+ | Copyright (c) 2025 Ivan Murzak |
6
+ | Licensed under the Apache License, Version 2.0. |
7
+ | See the LICENSE file in the project root for more information. |
8
+ +------------------------------------------------------------------+
9
+ */
10
+
11
+ #nullable enable
12
+ using System;
13
+ using System.Collections.Generic;
14
+ using UnityEditor;
15
+ using UnityEditor.PackageManager;
16
+ using UnityEngine;
17
+ using PackageManagerEvents = UnityEditor.PackageManager.Events;
18
+
19
+ namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
20
+ {
21
+ /// <summary>
22
+ /// Entry point for NuGet dependency management. Runs on every domain reload via [InitializeOnLoad]
23
+ /// AND in response to UPM package upgrades via PackageManager.Events.registeredPackages.
24
+ ///
25
+ /// This assembly has ZERO external dependencies — it always compiles, even when the main plugin
26
+ /// fails due to missing or conflicting DLLs. It downloads NuGet packages directly from nuget.org,
27
+ /// extracts DLLs, skips assemblies Unity already provides, and sets the UNITY_COPILOT_READY define
28
+ /// so the main plugin assemblies can compile.
29
+ ///
30
+ /// Why both triggers are needed:
31
+ /// [InitializeOnLoad] fires after a *successful* project-wide recompile. When a user upgrades
32
+ /// the Unity package and the new code references newer NuGet APIs whose DLL is still on disk
33
+ /// at the old version, the post-import recompile fails, Unity blocks the domain reload, and
34
+ /// the new resolver's static constructor never runs. The previous AppDomain is still alive
35
+ /// though — and so are its event subscriptions. registeredPackages fires from the still-alive
36
+ /// AppDomain after UPM writes new package files but before the failed recompile, giving us a
37
+ /// chance to clean up stale DLLs and unblock the next compile attempt. See Unity-Uco#707.
38
+ ///
39
+ /// Flow on domain reload:
40
+ /// 1. [InitializeOnLoad] fires
41
+ /// 2. Deferred via EditorApplication.update (runs without editor focus, unlike delayCall)
42
+ /// 3. NuGetPackageRestorer checks if all packages are installed
43
+ /// 4. Downloads and installs any missing packages, removes stale-version siblings
44
+ /// 5. Sets UNITY_COPILOT_READY scripting define
45
+ /// 6. If packages were installed: triggers AssetDatabase.Refresh() → domain reload
46
+ /// 7. On next reload: everything is in place, main plugin compiles
47
+ ///
48
+ /// Flow on UPM package change (no domain reload required to invoke):
49
+ /// 1. registeredPackages fires from the previous AppDomain
50
+ /// 2. NuGetPackageRestorer.Restore() runs the same install + cleanup logic
51
+ /// 3. AssetDatabase.Refresh() prompts Unity to retry compilation with the corrected DLL set
52
+ /// 4. On the resulting successful reload, [InitializeOnLoad] resumes the normal path
53
+ /// </summary>
54
+ [InitializeOnLoad]
55
+ static class NuGetDependencyResolver
56
+ {
57
+ const string Tag = "[Unity-Uco DependencyResolver]";
58
+
59
+ static NuGetDependencyResolver()
60
+ {
61
+ // In CI, skip runtime resolution — DLLs are committed to git
62
+ // and UNITY_COPILOT_READY should already be in ProjectSettings.
63
+ // Setting defines at runtime in batch mode causes "Error building Player
64
+ // because scripts are compiling" when the test runner races the recompilation.
65
+ if (IsCi())
66
+ {
67
+ RecompileGate.EnsureReadyDefine();
68
+ return;
69
+ }
70
+
71
+ EditorApplication.update += ResolveOnce;
72
+
73
+ // Subscribe to UPM package-change events so that when the user upgrades a package
74
+ // whose new code references a newer NuGet DLL than the one currently on disk, we
75
+ // can run the cleanup + restore from the still-alive AppDomain. The compile that
76
+ // would otherwise fail (and block [InitializeOnLoad] in the next domain) gets
77
+ // retried by Unity once AssetDatabase.Refresh() picks up the corrected DLL set.
78
+ // -= before += guards against accidental double-subscription if a hot-reload path
79
+ // ever re-runs this constructor without a full domain swap.
80
+ PackageManagerEvents.registeredPackages -= OnRegisteredPackages;
81
+ PackageManagerEvents.registeredPackages += OnRegisteredPackages;
82
+ }
83
+
84
+ /// <summary>
85
+ /// Tracks whether a UPM-event-triggered restore is currently in flight, so a second
86
+ /// event burst (UPM can fire multiple registeredPackages events for a single user
87
+ /// action) doesn't kick off a parallel restore.
88
+ /// </summary>
89
+ static bool isRestoringFromPackageEvent;
90
+
91
+ static void OnRegisteredPackages(PackageRegistrationEventArgs args)
92
+ {
93
+ if (isRestoringFromPackageEvent)
94
+ return;
95
+
96
+ // Cheap pre-filter: if no packages were added, removed, or upgraded, there is no
97
+ // possibility of a NuGet-dep version change. Skip the heavier AllPackagesInstalled()
98
+ // probe to avoid touching disk on every UPM ping.
99
+ if ((args.added == null || args.added.Count == 0)
100
+ && (args.removed == null || args.removed.Count == 0)
101
+ && (args.changedFrom == null || args.changedFrom.Count == 0)
102
+ && (args.changedTo == null || args.changedTo.Count == 0))
103
+ {
104
+ return;
105
+ }
106
+
107
+ isRestoringFromPackageEvent = true;
108
+ try
109
+ {
110
+ Debug.Log($"{Tag} UPM package change detected — running NuGet restore.");
111
+ RunRestoreAndRefresh();
112
+ }
113
+ catch (Exception ex)
114
+ {
115
+ // Same rationale as ResolveOnce: do NOT set UNITY_COPILOT_READY on a failed restore.
116
+ Debug.LogError($"{Tag} Restore from UPM event failed: {ex}");
117
+ }
118
+ finally
119
+ {
120
+ isRestoringFromPackageEvent = false;
121
+ }
122
+ }
123
+
124
+ /// <summary>
125
+ /// Shared core used by both ResolveOnce (post-domain-reload) and OnRegisteredPackages
126
+ /// (no-domain-reload UPM event path). AllPackagesInstalled() short-circuits the common
127
+ /// no-op case without touching the network.
128
+ /// </summary>
129
+ static void RunRestoreAndRefresh()
130
+ {
131
+ if (NuGetPackageRestorer.AllPackagesInstalled())
132
+ {
133
+ NuGetPluginConfigurator.ConfigureAll();
134
+ RecompileGate.EnsureReadyDefine();
135
+ return;
136
+ }
137
+
138
+ Debug.Log($"{Tag} Restoring NuGet packages...");
139
+ var changed = NuGetPackageRestorer.Restore();
140
+
141
+ NuGetPluginConfigurator.ConfigureAll();
142
+
143
+ RecompileGate.EnsureReadyDefine();
144
+
145
+ if (changed)
146
+ {
147
+ Debug.Log($"{Tag} Packages restored. Refreshing AssetDatabase...");
148
+ AssetDatabase.Refresh();
149
+ }
150
+ }
151
+
152
+ static void ResolveOnce()
153
+ {
154
+ EditorApplication.update -= ResolveOnce;
155
+ try
156
+ {
157
+ RunRestoreAndRefresh();
158
+ }
159
+ catch (Exception ex)
160
+ {
161
+ // Do NOT set UNITY_COPILOT_READY here: if restore/configuration failed, the DLL
162
+ // layout is unknown/inconsistent, and letting main-plugin assemblies compile
163
+ // against a partial/mismatched set (via defineConstraints) produces hard-to-
164
+ // diagnose MissingMethodException / TypeLoadException at runtime. Surface the
165
+ // failure loud and clear instead, so the user fixes the underlying problem
166
+ // and retries (the next domain reload will run Restore again).
167
+ Debug.LogError($"{Tag} Failed: {ex}");
168
+ }
169
+ }
170
+
171
+ /// <summary>
172
+ /// Checks if the current environment is a CI environment.
173
+ /// Mirrors EnvironmentUtils.IsCi() but without external dependencies,
174
+ /// since this assembly must compile standalone.
175
+ /// Checks both command-line arguments and environment variables for
176
+ /// CI, GITHUB_ACTIONS, and TF_BUILD (Azure Pipelines).
177
+ /// </summary>
178
+ static bool IsCi()
179
+ {
180
+ var args = ParseCommandLineArguments();
181
+
182
+ var ci = GetArgOrEnv(args, "CI");
183
+ var gha = GetArgOrEnv(args, "GITHUB_ACTIONS");
184
+ var az = GetArgOrEnv(args, "TF_BUILD");
185
+
186
+ return IsTrue(ci) || IsTrue(gha) || IsTrue(az);
187
+
188
+ static string? GetArgOrEnv(Dictionary<string, string?> args, string key)
189
+ => args.TryGetValue(key, out var v) ? v : Environment.GetEnvironmentVariable(key);
190
+
191
+ static bool IsTrue(string? value)
192
+ => string.Equals(value?.Trim()?.Trim('"'), "true", StringComparison.OrdinalIgnoreCase);
193
+ }
194
+
195
+ /// <summary>
196
+ /// Parses Unity command-line arguments into a dictionary.
197
+ /// Handles both "-key value" and "-key=value" forms, plus bare flags like "-batchmode".
198
+ /// Keys are stored WITHOUT the leading dash.
199
+ /// </summary>
200
+ static Dictionary<string, string?> ParseCommandLineArguments()
201
+ {
202
+ var result = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
203
+ var rawArgs = Environment.GetCommandLineArgs();
204
+
205
+ for (var i = 0; i < rawArgs.Length; i++)
206
+ {
207
+ var arg = rawArgs[i];
208
+ if (!arg.StartsWith("-"))
209
+ continue;
210
+
211
+ var key = arg.TrimStart('-');
212
+
213
+ // Handle -key=value form
214
+ var eqIndex = key.IndexOf('=');
215
+ if (eqIndex >= 0)
216
+ {
217
+ result[key.Substring(0, eqIndex)] = key.Substring(eqIndex + 1);
218
+ continue;
219
+ }
220
+
221
+ // Handle -key value form (next arg is value if it doesn't start with -)
222
+ if (i + 1 < rawArgs.Length && !rawArgs[i + 1].StartsWith("-"))
223
+ {
224
+ result[key] = rawArgs[++i];
225
+ }
226
+ else
227
+ {
228
+ // Bare flag like -batchmode
229
+ result[key] = null;
230
+ }
231
+ }
232
+
233
+ return result;
234
+ }
235
+
236
+ }
237
+ }
@@ -1,82 +1,82 @@
1
- /*
2
- +------------------------------------------------------------------+
3
- | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
- | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
- | Copyright (c) 2025 Ivan Murzak |
6
- | Licensed under the Apache License, Version 2.0. |
7
- | See the LICENSE file in the project root for more information. |
8
- +------------------------------------------------------------------+
9
- */
10
-
11
- #nullable enable
12
- using System;
13
- using System.IO;
14
- using System.Net.Http;
15
- using UnityEngine;
16
-
17
- namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
18
- {
19
- /// <summary>
20
- /// Downloads .nupkg files from the NuGet v3 flat container API.
21
- /// Uses the flat container URL pattern:
22
- /// https://api.nuget.org/v3-flatcontainer/{id}/{version}/{id}.{version}.nupkg
23
- /// </summary>
24
- static class NuGetDownloader
25
- {
26
- const string Tag = NuGetConfig.LogTag;
27
-
28
- static readonly HttpClient httpClient = CreateHttpClient();
29
-
30
- static HttpClient CreateHttpClient()
31
- {
32
- var handler = new HttpClientHandler
33
- {
34
- AutomaticDecompression = System.Net.DecompressionMethods.GZip
35
- | System.Net.DecompressionMethods.Deflate
36
- };
37
- var client = new HttpClient(handler);
38
- client.DefaultRequestHeaders.Add("User-Agent", "Unity-MCP-NuGetResolver");
39
- client.Timeout = TimeSpan.FromMinutes(5);
40
- return client;
41
- }
42
-
43
- /// <summary>
44
- /// Downloads a .nupkg file to the cache. Returns the cached file path.
45
- /// If the file is already cached, returns immediately without downloading.
46
- /// </summary>
47
- public static string Download(NuGetPackage package)
48
- {
49
- NuGetCache.EnsureCacheDirectory();
50
- var cachedPath = NuGetCache.GetCachedPath(package);
51
-
52
- if (File.Exists(cachedPath))
53
- return cachedPath;
54
-
55
- var url = package.DownloadUrl;
56
- Debug.Log($"{Tag} Downloading {package.Id} {package.Version} from {url}");
57
-
58
- try
59
- {
60
- using (var response = httpClient.GetAsync(url).GetAwaiter().GetResult())
61
- {
62
- response.EnsureSuccessStatusCode();
63
- using (var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
64
- using (var fileStream = File.Create(cachedPath))
65
- {
66
- stream.CopyTo(fileStream);
67
- }
68
- }
69
- Debug.Log($"{Tag} Downloaded {package.Id} {package.Version} ({new FileInfo(cachedPath).Length / 1024} KB)");
70
- }
71
- catch (Exception ex)
72
- {
73
- Debug.LogError($"{Tag} Failed to download {package}: {ex.Message}");
74
- if (File.Exists(cachedPath))
75
- File.Delete(cachedPath);
76
- throw;
77
- }
78
-
79
- return cachedPath;
80
- }
81
- }
82
- }
1
+ /*
2
+ +------------------------------------------------------------------+
3
+ | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
+ | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
+ | Copyright (c) 2025 Ivan Murzak |
6
+ | Licensed under the Apache License, Version 2.0. |
7
+ | See the LICENSE file in the project root for more information. |
8
+ +------------------------------------------------------------------+
9
+ */
10
+
11
+ #nullable enable
12
+ using System;
13
+ using System.IO;
14
+ using System.Net.Http;
15
+ using UnityEngine;
16
+
17
+ namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
18
+ {
19
+ /// <summary>
20
+ /// Downloads .nupkg files from the NuGet v3 flat container API.
21
+ /// Uses the flat container URL pattern:
22
+ /// https://api.nuget.org/v3-flatcontainer/{id}/{version}/{id}.{version}.nupkg
23
+ /// </summary>
24
+ static class NuGetDownloader
25
+ {
26
+ const string Tag = NuGetConfig.LogTag;
27
+
28
+ static readonly HttpClient httpClient = CreateHttpClient();
29
+
30
+ static HttpClient CreateHttpClient()
31
+ {
32
+ var handler = new HttpClientHandler
33
+ {
34
+ AutomaticDecompression = System.Net.DecompressionMethods.GZip
35
+ | System.Net.DecompressionMethods.Deflate
36
+ };
37
+ var client = new HttpClient(handler);
38
+ client.DefaultRequestHeaders.Add("User-Agent", "Unity-Uco-NuGetResolver");
39
+ client.Timeout = TimeSpan.FromMinutes(5);
40
+ return client;
41
+ }
42
+
43
+ /// <summary>
44
+ /// Downloads a .nupkg file to the cache. Returns the cached file path.
45
+ /// If the file is already cached, returns immediately without downloading.
46
+ /// </summary>
47
+ public static string Download(NuGetPackage package)
48
+ {
49
+ NuGetCache.EnsureCacheDirectory();
50
+ var cachedPath = NuGetCache.GetCachedPath(package);
51
+
52
+ if (File.Exists(cachedPath))
53
+ return cachedPath;
54
+
55
+ var url = package.DownloadUrl;
56
+ Debug.Log($"{Tag} Downloading {package.Id} {package.Version} from {url}");
57
+
58
+ try
59
+ {
60
+ using (var response = httpClient.GetAsync(url).GetAwaiter().GetResult())
61
+ {
62
+ response.EnsureSuccessStatusCode();
63
+ using (var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
64
+ using (var fileStream = File.Create(cachedPath))
65
+ {
66
+ stream.CopyTo(fileStream);
67
+ }
68
+ }
69
+ Debug.Log($"{Tag} Downloaded {package.Id} {package.Version} ({new FileInfo(cachedPath).Length / 1024} KB)");
70
+ }
71
+ catch (Exception ex)
72
+ {
73
+ Debug.LogError($"{Tag} Failed to download {package}: {ex.Message}");
74
+ if (File.Exists(cachedPath))
75
+ File.Delete(cachedPath);
76
+ throw;
77
+ }
78
+
79
+ return cachedPath;
80
+ }
81
+ }
82
+ }