using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; namespace AxCopilot.Services.Agent; public class DevEnvDetectTool : IAgentTool { private static (DateTime Time, string Result)? _cache; public string Name => "dev_env_detect"; public string Description => "Detect installed development tools on this machine. Returns: IDEs (VS Code, Visual Studio, IntelliJ, PyCharm), language runtimes (dotnet, python/conda, java, node, gcc/g++), and build tools (MSBuild, Maven, Gradle, CMake, npm/yarn). Use this before running build/test commands to know what's available."; public ToolParameterSchema Parameters { get { ToolParameterSchema toolParameterSchema = new ToolParameterSchema(); Dictionary dictionary = new Dictionary(); ToolProperty obj = new ToolProperty { Type = "string", Description = "Detection category: all (default), ides, runtimes, build_tools" }; int num = 4; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "all"; span[1] = "ides"; span[2] = "runtimes"; span[3] = "build_tools"; obj.Enum = list; dictionary["category"] = obj; toolParameterSchema.Properties = dictionary; toolParameterSchema.Required = new List(); return toolParameterSchema; } } public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct) { JsonElement value; string text = (args.TryGetProperty("category", out value) ? (value.GetString() ?? "all") : "all"); if (_cache.HasValue && (DateTime.UtcNow - _cache.Value.Time).TotalSeconds < 60.0) { return Task.FromResult(ToolResult.Ok(_cache.Value.Result)); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("=== 개발 환경 감지 보고서 ===\n"); if ((text == "all" || text == "ides") ? true : false) { stringBuilder.AppendLine("## IDE"); DetectIde(stringBuilder, "VS Code", DetectVsCode); DetectIde(stringBuilder, "Visual Studio", DetectVisualStudio); DetectIde(stringBuilder, "IntelliJ IDEA", () => DetectJetBrains("IntelliJ")); DetectIde(stringBuilder, "PyCharm", () => DetectJetBrains("PyCharm")); stringBuilder.AppendLine(); } if ((text == "all" || text == "runtimes") ? true : false) { stringBuilder.AppendLine("## Language Runtimes"); DetectCommand(stringBuilder, "dotnet", "dotnet --version", "DOTNET_ROOT"); DetectCommand(stringBuilder, "python", "python --version", "PYTHON_HOME"); DetectCommand(stringBuilder, "conda", "conda --version", "CONDA_PREFIX"); DetectCommand(stringBuilder, "java", "java -version", "JAVA_HOME"); DetectCommand(stringBuilder, "node", "node --version", "NODE_HOME"); DetectCommand(stringBuilder, "npm", "npm --version", null); DetectCommand(stringBuilder, "gcc", "gcc --version", null); DetectCommand(stringBuilder, "g++", "g++ --version", null); stringBuilder.AppendLine(); } if ((text == "all" || text == "build_tools") ? true : false) { stringBuilder.AppendLine("## Build Tools"); DetectCommand(stringBuilder, "MSBuild", "msbuild -version", null); DetectCommand(stringBuilder, "Maven", "mvn --version", "MAVEN_HOME"); DetectCommand(stringBuilder, "Gradle", "gradle --version", "GRADLE_HOME"); DetectCommand(stringBuilder, "CMake", "cmake --version", null); DetectCommand(stringBuilder, "yarn", "yarn --version", null); DetectCommand(stringBuilder, "pip", "pip --version", null); stringBuilder.AppendLine(); } string text2 = stringBuilder.ToString(); _cache = (DateTime.UtcNow, text2); return Task.FromResult(ToolResult.Ok(text2)); } private static void DetectIde(StringBuilder sb, string name, Func detector) { string text = detector(); sb.AppendLine((text != null) ? (" ✅ " + name + ": " + text) : (" ❌ " + name + ": 미설치")); } private static void DetectCommand(StringBuilder sb, string name, string versionCmd, string? envVar) { string text = null; if (envVar != null) { text = Environment.GetEnvironmentVariable(envVar); } string text2 = RunQuick("where.exe", name); string value = ""; if (text2 != null) { string[] array = versionCmd.Split(' ', 2); string text3 = RunQuick(array[0], (array.Length > 1) ? array[1] : ""); if (text3 != null) { value = text3.Split('\n')[0].Trim(); } } if (text2 != null) { StringBuilder stringBuilder = sb; StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 3, stringBuilder); handler.AppendLiteral(" ✅ "); handler.AppendFormatted(name); handler.AppendLiteral(": "); handler.AppendFormatted(value); handler.AppendLiteral(" ("); handler.AppendFormatted(text2.Split('\n')[0].Trim()); handler.AppendLiteral(")"); stringBuilder2.AppendLine(ref handler); } else if (text != null) { StringBuilder stringBuilder = sb; StringBuilder stringBuilder3 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 3, stringBuilder); handler.AppendLiteral(" ⚠ "); handler.AppendFormatted(name); handler.AppendLiteral(": 환경변수만 ("); handler.AppendFormatted(envVar); handler.AppendLiteral("="); handler.AppendFormatted(text); handler.AppendLiteral(")"); stringBuilder3.AppendLine(ref handler); } else { StringBuilder stringBuilder = sb; StringBuilder stringBuilder4 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder); handler.AppendLiteral(" ❌ "); handler.AppendFormatted(name); handler.AppendLiteral(": 미설치"); stringBuilder4.AppendLine(ref handler); } } private static string? RunQuick(string exe, string args) { try { ProcessStartInfo startInfo = new ProcessStartInfo(exe, args) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using Process process = Process.Start(startInfo); if (process == null) { return null; } string text = process.StandardOutput.ReadToEnd(); string text2 = process.StandardError.ReadToEnd(); process.WaitForExit(5000); string text3 = (string.IsNullOrWhiteSpace(text) ? text2 : text); return string.IsNullOrWhiteSpace(text3) ? null : text3.Trim(); } catch { return null; } } private static string? DetectVsCode() { string text = RunQuick("where.exe", "code"); if (text != null) { return text.Split('\n')[0].Trim(); } string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); string text2 = Path.Combine(folderPath, "Programs", "Microsoft VS Code", "Code.exe"); return File.Exists(text2) ? text2 : null; } private static string? DetectVisualStudio() { try { using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7"); if (registryKey != null) { foreach (string item in from n in registryKey.GetValueNames() orderby n descending select n) { string text = registryKey.GetValue(item)?.ToString(); if (!string.IsNullOrEmpty(text) && Directory.Exists(text)) { return $"Visual Studio {item} ({text})"; } } } using RegistryKey registryKey2 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\SxS\\VS7"); if (registryKey2 != null) { foreach (string item2 in from n in registryKey2.GetValueNames() orderby n descending select n) { string text2 = registryKey2.GetValue(item2)?.ToString(); if (!string.IsNullOrEmpty(text2) && Directory.Exists(text2)) { return $"Visual Studio {item2} ({text2})"; } } } } catch { } string text3 = RunQuick("where.exe", "devenv"); return (text3 != null) ? text3.Split('\n')[0].Trim() : null; } private static string? DetectJetBrains(string product) { try { using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\JetBrains\\" + product); if (registryKey != null) { string[] array = (from n in registryKey.GetSubKeyNames() orderby n descending select n).ToArray(); if (array.Length != 0) { using RegistryKey registryKey2 = registryKey.OpenSubKey(array[0]); string value = registryKey2?.GetValue("InstallDir")?.ToString() ?? registryKey2?.GetValue("")?.ToString(); if (!string.IsNullOrEmpty(value)) { return $"{product} {array[0]} ({value})"; } } } } catch { } return null; } }