@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,252 +1,252 @@
1
- /*
2
- * Design inspired by MCP for Unity (CoplayDev/unity-mcp), Copyright (c) Coplay Inc., MIT License.
3
- */
4
-
5
- /*
6
- ┌──────────────────────────────────────────────────────────────────┐
7
- │ Author: Ivan Murzak (https://github.com/IvanMurzak) │
8
- │ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
9
- │ Copyright (c) 2025 Ivan Murzak │
10
- │ Licensed under the Apache License, Version 2.0. │
11
- │ See the LICENSE file in the project root for more information. │
12
- └──────────────────────────────────────────────────────────────────┘
13
- */
14
-
15
- #nullable enable
16
- using System;
17
- using System.Collections.Generic;
18
- using System.ComponentModel;
19
- using System.IO;
20
- using System.Linq;
21
- using com.AtelierAI.Uco.Framework;
22
- using com.IvanMurzak.ReflectorNet.Utils;
23
- using com.AtelierAI.Unity.Copilot.Utils;
24
- using Microsoft.CodeAnalysis;
25
- using Microsoft.CodeAnalysis.CSharp;
26
- using RoslynDiagnostic = Microsoft.CodeAnalysis.Diagnostic;
27
-
28
- namespace com.AtelierAI.Unity.Copilot.Editor.API
29
- {
30
- public static partial class Tool_Script
31
- {
32
- /// <summary>
33
- /// Validation strictness for <see cref="Validate"/>.
34
- /// </summary>
35
- public enum ValidationLevel
36
- {
37
- /// <summary>Syntax-only: parser checks, no compilation. Fastest, no project references resolved.</summary>
38
- Syntax = 0,
39
- /// <summary>Syntax + semantic: type resolution against project assemblies (catches unknown types/methods/namespaces).</summary>
40
- Semantic = 1,
41
- /// <summary>Strict: Semantic + warnings-as-errors + nullable strictness. Catches latent issues but can be noisy.</summary>
42
- Strict = 2,
43
- }
44
-
45
- /// <summary>
46
- /// Diagnostic entry returned by <see cref="Validate"/>.
47
- /// </summary>
48
- public class ValidationDiagnostic
49
- {
50
- public string Severity { get; set; } = string.Empty; // "Error" | "Warning" | "Info" | "Hidden"
51
- public string Code { get; set; } = string.Empty; // e.g. "CS1002"
52
- public string Message { get; set; } = string.Empty;
53
- public int Line { get; set; } // 1-based
54
- public int Column { get; set; } // 1-based
55
- public int EndLine { get; set; } // 1-based
56
- public int EndColumn { get; set; } // 1-based
57
- }
58
-
59
- /// <summary>
60
- /// Aggregated result returned by <see cref="Validate"/>.
61
- /// </summary>
62
- public class ValidationResult
63
- {
64
- public bool Ok { get; set; }
65
- public ValidationLevel Level { get; set; }
66
- public int ErrorCount { get; set; }
67
- public int WarningCount { get; set; }
68
- public ValidationDiagnostic[] Diagnostics { get; set; } = Array.Empty<ValidationDiagnostic>();
69
- }
70
-
71
- public const string ScriptValidateToolId = "script-validate";
72
-
73
- // Hard cap on input size to avoid OOM from runaway inputs. 1 MiB of C# source is already huge.
74
- private const int MaxValidateCodeBytes = 1 * 1024 * 1024;
75
-
76
- [UcoTool
77
- (
78
- ScriptValidateToolId,
79
- Title = "Script / Validate",
80
- ReadOnlyHint = true,
81
- IdempotentHint = true
82
- )]
83
- [UcoSkillDescription("Validate C# source with Roslyn at three strictness levels (Syntax / Semantic / Strict). " +
84
- "Does NOT execute or write to disk — safe pre-flight lint before '" + ScriptUpdateOrCreateToolId + "'. " +
85
- "Strict / Semantic catch references to unknown namespaces, types, and methods against the project's loaded assemblies.")]
86
- [UcoSkillBody("Validates a C# source string with Roslyn and returns structured diagnostics. " +
87
- "Pure read-only — does NOT compile to an assembly, write any file, or invoke any code. " +
88
- "Pair with '" + ScriptUpdateOrCreateToolId + "' as a pre-flight check before writing scripts to disk.\n\n" +
89
- "## Inputs\n\n" +
90
- "- `code` — required C# source text (the entire file contents). Throws if empty or larger than 1 MiB.\n" +
91
- "- `level` — validation strictness:\n" +
92
- " - `Syntax` (default) — parser-only, fastest. Catches malformed C# (`CS1002` missing `;`, `CS1513` unmatched braces, etc.).\n" +
93
- " - `Semantic` — parser + symbol resolution against all loaded project assemblies. Catches `CS0246` (unknown type), `CS0103` (name not in scope), `CS1061` (no such member), unknown namespaces.\n" +
94
- " - `Strict` — Semantic + warnings-as-errors + nullable context enabled. Useful but can be noisy on legacy code.\n" +
95
- "- `fileName` — optional display name shown in `Diagnostic.Code` paths. Defaults to `Submitted.cs`.\n\n" +
96
- "## Output\n\n" +
97
- "Returns a `ValidationResult`:\n" +
98
- "- `Ok` — true iff zero diagnostics with `Severity == Error`.\n" +
99
- "- `Level` — the level that was actually run.\n" +
100
- "- `ErrorCount` / `WarningCount` — totals.\n" +
101
- "- `Diagnostics[]` — each with `Severity`, `Code` (e.g. `CS0246`), `Message`, and 1-based `Line`/`Column`/`EndLine`/`EndColumn`.\n\n" +
102
- "## Notes\n\n" +
103
- "- `Semantic` and `Strict` reuse the assembly reference set used by '" + ScriptExecuteToolId + "' " +
104
- " (all non-dynamic assemblies in the current AppDomain with a non-empty `Location`).\n" +
105
- "- Semantic resolution covers anything visible to your project — Unity APIs, packages, your own scripts " +
106
- " that compiled successfully. If your project has compile errors, those types won't be resolvable here either.\n" +
107
- "- This tool never runs the code. For execution use '" + ScriptExecuteToolId + "'.")]
108
- [Description("Validates C# source via Roslyn at Syntax / Semantic / Strict levels without compiling to an assembly or " +
109
- "writing any file. Use as a pre-flight check before '" + ScriptUpdateOrCreateToolId + "'. Semantic/Strict catch " +
110
- "references to unknown namespaces, types, and methods against the project's loaded assemblies.")]
111
- public static ValidationResult Validate
112
- (
113
- [Description("C# source code to validate (full file text).")]
114
- string code,
115
- [Description("Validation strictness. 'Syntax' (default) = parser only. 'Semantic' = parser + project-assembly symbol " +
116
- "resolution (catches unknown types/methods/namespaces). 'Strict' = Semantic + warnings-as-errors + nullable.")]
117
- ValidationLevel level = ValidationLevel.Syntax,
118
- [Description("Optional file name shown in diagnostic locations. Defaults to 'Submitted.cs'.")]
119
- string? fileName = null
120
- )
121
- {
122
- if (string.IsNullOrEmpty(code))
123
- throw new ArgumentException($"'{nameof(code)}' is null or empty. Please provide C# source to validate.", nameof(code));
124
-
125
- // Defensive size cap. Roslyn handles large inputs but we don't want runaway memory from malformed clients.
126
- // 2 bytes per char overestimates UTF-16 but is the right thing to compare against `byte` budget.
127
- if ((long)code.Length * 2 > MaxValidateCodeBytes)
128
- throw new ArgumentException(
129
- $"'{nameof(code)}' is too large ({code.Length} chars). Limit: {MaxValidateCodeBytes / 1024} KiB.",
130
- nameof(code));
131
-
132
- var effectiveFileName = string.IsNullOrWhiteSpace(fileName) ? "Submitted.cs" : fileName!;
133
-
134
- try
135
- {
136
- var syntaxTree = CSharpSyntaxTree.ParseText(code, path: effectiveFileName);
137
-
138
- IEnumerable<RoslynDiagnostic> diagnostics;
139
-
140
- if (level == ValidationLevel.Syntax)
141
- {
142
- diagnostics = syntaxTree.GetDiagnostics();
143
- }
144
- else
145
- {
146
- // Reused from Script.Execute.cs: aggregate non-dynamic AppDomain assemblies as MetadataReferences.
147
- // Roslyn parsing/compilation is pure CPU and does NOT require Unity main thread.
148
- var references = BuildProjectReferences();
149
-
150
- var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
151
- .WithWarningLevel(4);
152
-
153
- if (level == ValidationLevel.Strict)
154
- {
155
- options = options
156
- .WithGeneralDiagnosticOption(ReportDiagnostic.Error)
157
- .WithNullableContextOptions(NullableContextOptions.Enable);
158
- }
159
-
160
- var compilation = CSharpCompilation.Create(
161
- assemblyName: "ValidationAssembly",
162
- syntaxTrees: new[] { syntaxTree },
163
- references: references,
164
- options: options
165
- );
166
-
167
- // GetDiagnostics runs parse + declaration + method-body binding without emitting an assembly.
168
- diagnostics = compilation.GetDiagnostics();
169
- }
170
-
171
- var diagList = diagnostics.ToList();
172
-
173
- var errorCount = diagList.Count(d => d.Severity == DiagnosticSeverity.Error);
174
- var warningCount = diagList.Count(d => d.Severity == DiagnosticSeverity.Warning);
175
-
176
- return new ValidationResult
177
- {
178
- Ok = errorCount == 0,
179
- Level = level,
180
- ErrorCount = errorCount,
181
- WarningCount = warningCount,
182
- Diagnostics = diagList.Select(MapDiagnostic).ToArray(),
183
- };
184
- }
185
- catch (ArgumentException)
186
- {
187
- // Preserve clean caller errors (size/empty checks above bubble through unchanged).
188
- throw;
189
- }
190
- catch (Exception ex)
191
- {
192
- // Roslyn occasionally throws on pathological inputs; surface as a single synthetic error rather than 500ing.
193
- return new ValidationResult
194
- {
195
- Ok = false,
196
- Level = level,
197
- ErrorCount = 1,
198
- WarningCount = 0,
199
- Diagnostics = new[]
200
- {
201
- new ValidationDiagnostic
202
- {
203
- Severity = "Error",
204
- Code = "MCP_VALIDATE_INTERNAL",
205
- Message = $"Validation failed internally: {ex.GetType().Name}: {ex.Message}",
206
- Line = 1, Column = 1, EndLine = 1, EndColumn = 1,
207
- }
208
- },
209
- };
210
- }
211
- }
212
-
213
- /// <summary>
214
- /// Reused from Script.Execute.cs (ExecuteCSharpCode): build a MetadataReference[] from every loaded,
215
- /// non-dynamic AppDomain assembly with a resolvable on-disk Location. Silently skips assemblies whose
216
- /// files have moved or been unloaded — same forgiving behavior as the executor.
217
- /// </summary>
218
- static List<MetadataReference> BuildProjectReferences()
219
- {
220
- var result = new List<MetadataReference>();
221
- foreach (var assembly in AssemblyUtils.AllAssemblies)
222
- {
223
- if (assembly.IsDynamic) continue;
224
- if (string.IsNullOrEmpty(assembly.Location)) continue;
225
- try
226
- {
227
- result.Add(MetadataReference.CreateFromFile(assembly.Location));
228
- }
229
- catch (DirectoryNotFoundException) { /* assembly unloaded / moved — skip */ }
230
- catch (FileNotFoundException) { /* assembly unloaded / moved — skip */ }
231
- catch (Exception) { /* defensive — never let a single bad assembly kill validation */ }
232
- }
233
- return result;
234
- }
235
-
236
- static ValidationDiagnostic MapDiagnostic(RoslynDiagnostic d)
237
- {
238
- // Roslyn FileLinePositionSpan is 0-based; we surface 1-based to match human/IDE expectations.
239
- var span = d.Location.GetLineSpan();
240
- return new ValidationDiagnostic
241
- {
242
- Severity = d.Severity.ToString(),
243
- Code = d.Id,
244
- Message = d.GetMessage(),
245
- Line = span.StartLinePosition.Line + 1,
246
- Column = span.StartLinePosition.Character + 1,
247
- EndLine = span.EndLinePosition.Line + 1,
248
- EndColumn = span.EndLinePosition.Character + 1,
249
- };
250
- }
251
- }
252
- }
1
+ /*
2
+ * Design inspired by MCP for Unity (CoplayDev/unity-mcp), Copyright (c) Coplay Inc., MIT License.
3
+ */
4
+
5
+ /*
6
+ ┌──────────────────────────────────────────────────────────────────┐
7
+ │ Author: Ivan Murzak (https://github.com/IvanMurzak) │
8
+ │ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
9
+ │ Copyright (c) 2025 Ivan Murzak │
10
+ │ Licensed under the Apache License, Version 2.0. │
11
+ │ See the LICENSE file in the project root for more information. │
12
+ └──────────────────────────────────────────────────────────────────┘
13
+ */
14
+
15
+ #nullable enable
16
+ using System;
17
+ using System.Collections.Generic;
18
+ using System.ComponentModel;
19
+ using System.IO;
20
+ using System.Linq;
21
+ using com.AtelierAI.Uco.Framework;
22
+ using com.IvanMurzak.ReflectorNet.Utils;
23
+ using com.AtelierAI.Unity.Copilot.Utils;
24
+ using Microsoft.CodeAnalysis;
25
+ using Microsoft.CodeAnalysis.CSharp;
26
+ using RoslynDiagnostic = Microsoft.CodeAnalysis.Diagnostic;
27
+
28
+ namespace com.AtelierAI.Unity.Copilot.Editor.API
29
+ {
30
+ public static partial class Tool_Script
31
+ {
32
+ /// <summary>
33
+ /// Validation strictness for <see cref="Validate"/>.
34
+ /// </summary>
35
+ public enum ValidationLevel
36
+ {
37
+ /// <summary>Syntax-only: parser checks, no compilation. Fastest, no project references resolved.</summary>
38
+ Syntax = 0,
39
+ /// <summary>Syntax + semantic: type resolution against project assemblies (catches unknown types/methods/namespaces).</summary>
40
+ Semantic = 1,
41
+ /// <summary>Strict: Semantic + warnings-as-errors + nullable strictness. Catches latent issues but can be noisy.</summary>
42
+ Strict = 2,
43
+ }
44
+
45
+ /// <summary>
46
+ /// Diagnostic entry returned by <see cref="Validate"/>.
47
+ /// </summary>
48
+ public class ValidationDiagnostic
49
+ {
50
+ public string Severity { get; set; } = string.Empty; // "Error" | "Warning" | "Info" | "Hidden"
51
+ public string Code { get; set; } = string.Empty; // e.g. "CS1002"
52
+ public string Message { get; set; } = string.Empty;
53
+ public int Line { get; set; } // 1-based
54
+ public int Column { get; set; } // 1-based
55
+ public int EndLine { get; set; } // 1-based
56
+ public int EndColumn { get; set; } // 1-based
57
+ }
58
+
59
+ /// <summary>
60
+ /// Aggregated result returned by <see cref="Validate"/>.
61
+ /// </summary>
62
+ public class ValidationResult
63
+ {
64
+ public bool Ok { get; set; }
65
+ public ValidationLevel Level { get; set; }
66
+ public int ErrorCount { get; set; }
67
+ public int WarningCount { get; set; }
68
+ public ValidationDiagnostic[] Diagnostics { get; set; } = Array.Empty<ValidationDiagnostic>();
69
+ }
70
+
71
+ public const string ScriptValidateToolId = "script-validate";
72
+
73
+ // Hard cap on input size to avoid OOM from runaway inputs. 1 MiB of C# source is already huge.
74
+ private const int MaxValidateCodeBytes = 1 * 1024 * 1024;
75
+
76
+ [UcoTool
77
+ (
78
+ ScriptValidateToolId,
79
+ Title = "Script / Validate",
80
+ ReadOnlyHint = true,
81
+ IdempotentHint = true
82
+ )]
83
+ [UcoSkillDescription("Validate C# source with Roslyn at three strictness levels (Syntax / Semantic / Strict). " +
84
+ "Does NOT execute or write to disk — safe pre-flight lint before '" + ScriptUpdateOrCreateToolId + "'. " +
85
+ "Strict / Semantic catch references to unknown namespaces, types, and methods against the project's loaded assemblies.")]
86
+ [UcoSkillBody("Validates a C# source string with Roslyn and returns structured diagnostics. " +
87
+ "Pure read-only — does NOT compile to an assembly, write any file, or invoke any code. " +
88
+ "Pair with '" + ScriptUpdateOrCreateToolId + "' as a pre-flight check before writing scripts to disk.\n\n" +
89
+ "## Inputs\n\n" +
90
+ "- `code` — required C# source text (the entire file contents). Throws if empty or larger than 1 MiB.\n" +
91
+ "- `level` — validation strictness:\n" +
92
+ " - `Syntax` (default) — parser-only, fastest. Catches malformed C# (`CS1002` missing `;`, `CS1513` unmatched braces, etc.).\n" +
93
+ " - `Semantic` — parser + symbol resolution against all loaded project assemblies. Catches `CS0246` (unknown type), `CS0103` (name not in scope), `CS1061` (no such member), unknown namespaces.\n" +
94
+ " - `Strict` — Semantic + warnings-as-errors + nullable context enabled. Useful but can be noisy on legacy code.\n" +
95
+ "- `fileName` — optional display name shown in `Diagnostic.Code` paths. Defaults to `Submitted.cs`.\n\n" +
96
+ "## Output\n\n" +
97
+ "Returns a `ValidationResult`:\n" +
98
+ "- `Ok` — true iff zero diagnostics with `Severity == Error`.\n" +
99
+ "- `Level` — the level that was actually run.\n" +
100
+ "- `ErrorCount` / `WarningCount` — totals.\n" +
101
+ "- `Diagnostics[]` — each with `Severity`, `Code` (e.g. `CS0246`), `Message`, and 1-based `Line`/`Column`/`EndLine`/`EndColumn`.\n\n" +
102
+ "## Notes\n\n" +
103
+ "- `Semantic` and `Strict` reuse the assembly reference set used by '" + ScriptExecuteToolId + "' " +
104
+ " (all non-dynamic assemblies in the current AppDomain with a non-empty `Location`).\n" +
105
+ "- Semantic resolution covers anything visible to your project — Unity APIs, packages, your own scripts " +
106
+ " that compiled successfully. If your project has compile errors, those types won't be resolvable here either.\n" +
107
+ "- This tool never runs the code. For execution use '" + ScriptExecuteToolId + "'.")]
108
+ [Description("Validates C# source via Roslyn at Syntax / Semantic / Strict levels without compiling to an assembly or " +
109
+ "writing any file. Use as a pre-flight check before '" + ScriptUpdateOrCreateToolId + "'. Semantic/Strict catch " +
110
+ "references to unknown namespaces, types, and methods against the project's loaded assemblies.")]
111
+ public static ValidationResult Validate
112
+ (
113
+ [Description("C# source code to validate (full file text).")]
114
+ string code,
115
+ [Description("Validation strictness. 'Syntax' (default) = parser only. 'Semantic' = parser + project-assembly symbol " +
116
+ "resolution (catches unknown types/methods/namespaces). 'Strict' = Semantic + warnings-as-errors + nullable.")]
117
+ ValidationLevel level = ValidationLevel.Syntax,
118
+ [Description("Optional file name shown in diagnostic locations. Defaults to 'Submitted.cs'.")]
119
+ string? fileName = null
120
+ )
121
+ {
122
+ if (string.IsNullOrEmpty(code))
123
+ throw new ArgumentException($"'{nameof(code)}' is null or empty. Please provide C# source to validate.", nameof(code));
124
+
125
+ // Defensive size cap. Roslyn handles large inputs but we don't want runaway memory from malformed clients.
126
+ // 2 bytes per char overestimates UTF-16 but is the right thing to compare against `byte` budget.
127
+ if ((long)code.Length * 2 > MaxValidateCodeBytes)
128
+ throw new ArgumentException(
129
+ $"'{nameof(code)}' is too large ({code.Length} chars). Limit: {MaxValidateCodeBytes / 1024} KiB.",
130
+ nameof(code));
131
+
132
+ var effectiveFileName = string.IsNullOrWhiteSpace(fileName) ? "Submitted.cs" : fileName!;
133
+
134
+ try
135
+ {
136
+ var syntaxTree = CSharpSyntaxTree.ParseText(code, path: effectiveFileName);
137
+
138
+ IEnumerable<RoslynDiagnostic> diagnostics;
139
+
140
+ if (level == ValidationLevel.Syntax)
141
+ {
142
+ diagnostics = syntaxTree.GetDiagnostics();
143
+ }
144
+ else
145
+ {
146
+ // Reused from Script.Execute.cs: aggregate non-dynamic AppDomain assemblies as MetadataReferences.
147
+ // Roslyn parsing/compilation is pure CPU and does NOT require Unity main thread.
148
+ var references = BuildProjectReferences();
149
+
150
+ var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
151
+ .WithWarningLevel(4);
152
+
153
+ if (level == ValidationLevel.Strict)
154
+ {
155
+ options = options
156
+ .WithGeneralDiagnosticOption(ReportDiagnostic.Error)
157
+ .WithNullableContextOptions(NullableContextOptions.Enable);
158
+ }
159
+
160
+ var compilation = CSharpCompilation.Create(
161
+ assemblyName: "ValidationAssembly",
162
+ syntaxTrees: new[] { syntaxTree },
163
+ references: references,
164
+ options: options
165
+ );
166
+
167
+ // GetDiagnostics runs parse + declaration + method-body binding without emitting an assembly.
168
+ diagnostics = compilation.GetDiagnostics();
169
+ }
170
+
171
+ var diagList = diagnostics.ToList();
172
+
173
+ var errorCount = diagList.Count(d => d.Severity == DiagnosticSeverity.Error);
174
+ var warningCount = diagList.Count(d => d.Severity == DiagnosticSeverity.Warning);
175
+
176
+ return new ValidationResult
177
+ {
178
+ Ok = errorCount == 0,
179
+ Level = level,
180
+ ErrorCount = errorCount,
181
+ WarningCount = warningCount,
182
+ Diagnostics = diagList.Select(MapDiagnostic).ToArray(),
183
+ };
184
+ }
185
+ catch (ArgumentException)
186
+ {
187
+ // Preserve clean caller errors (size/empty checks above bubble through unchanged).
188
+ throw;
189
+ }
190
+ catch (Exception ex)
191
+ {
192
+ // Roslyn occasionally throws on pathological inputs; surface as a single synthetic error rather than 500ing.
193
+ return new ValidationResult
194
+ {
195
+ Ok = false,
196
+ Level = level,
197
+ ErrorCount = 1,
198
+ WarningCount = 0,
199
+ Diagnostics = new[]
200
+ {
201
+ new ValidationDiagnostic
202
+ {
203
+ Severity = "Error",
204
+ Code = "UCO_VALIDATE_INTERNAL",
205
+ Message = $"Validation failed internally: {ex.GetType().Name}: {ex.Message}",
206
+ Line = 1, Column = 1, EndLine = 1, EndColumn = 1,
207
+ }
208
+ },
209
+ };
210
+ }
211
+ }
212
+
213
+ /// <summary>
214
+ /// Reused from Script.Execute.cs (ExecuteCSharpCode): build a MetadataReference[] from every loaded,
215
+ /// non-dynamic AppDomain assembly with a resolvable on-disk Location. Silently skips assemblies whose
216
+ /// files have moved or been unloaded — same forgiving behavior as the executor.
217
+ /// </summary>
218
+ static List<MetadataReference> BuildProjectReferences()
219
+ {
220
+ var result = new List<MetadataReference>();
221
+ foreach (var assembly in AssemblyUtils.AllAssemblies)
222
+ {
223
+ if (assembly.IsDynamic) continue;
224
+ if (string.IsNullOrEmpty(assembly.Location)) continue;
225
+ try
226
+ {
227
+ result.Add(MetadataReference.CreateFromFile(assembly.Location));
228
+ }
229
+ catch (DirectoryNotFoundException) { /* assembly unloaded / moved — skip */ }
230
+ catch (FileNotFoundException) { /* assembly unloaded / moved — skip */ }
231
+ catch (Exception) { /* defensive — never let a single bad assembly kill validation */ }
232
+ }
233
+ return result;
234
+ }
235
+
236
+ static ValidationDiagnostic MapDiagnostic(RoslynDiagnostic d)
237
+ {
238
+ // Roslyn FileLinePositionSpan is 0-based; we surface 1-based to match human/IDE expectations.
239
+ var span = d.Location.GetLineSpan();
240
+ return new ValidationDiagnostic
241
+ {
242
+ Severity = d.Severity.ToString(),
243
+ Code = d.Id,
244
+ Message = d.GetMessage(),
245
+ Line = span.StartLinePosition.Line + 1,
246
+ Column = span.StartLinePosition.Character + 1,
247
+ EndLine = span.EndLinePosition.Line + 1,
248
+ EndColumn = span.EndLinePosition.Character + 1,
249
+ };
250
+ }
251
+ }
252
+ }