using AxCopilot.SDK; using AxCopilot.Themes; namespace AxCopilot.Handlers; /// /// 환경변수 조회 핸들러. "env" 프리픽스로 사용합니다. /// 예: env → 자주 쓰는 환경변수 목록 /// env PATH → PATH 값 표시 (Enter로 복사) /// env java → 이름에 "java" 포함된 환경변수 검색 /// public class EnvHandler : IActionHandler { public string? Prefix => "env"; public PluginMetadata Metadata => new( "EnvVars", "환경변수 조회 — env 뒤에 변수명 입력", "1.0", "AX"); // 자주 쓰는 환경변수 우선 표시 순서 private static readonly string[] _priorityKeys = [ "PATH", "JAVA_HOME", "PYTHON_HOME", "NODE_HOME", "GOPATH", "GOROOT", "USERPROFILE", "APPDATA", "LOCALAPPDATA", "TEMP", "TMP", "COMPUTERNAME", "USERNAME", "USERDOMAIN", "OS", "PROCESSOR_ARCHITECTURE", "SYSTEMROOT", "WINDIR", "PROGRAMFILES", "PROGRAMFILES(X86)", "COMMONPROGRAMFILES", "NUMBER_OF_PROCESSORS" ]; public Task> GetItemsAsync(string query, CancellationToken ct) { var q = query.Trim(); var allVars = Environment.GetEnvironmentVariables() .Cast() .Select(e => (Key: e.Key?.ToString() ?? "", Value: e.Value?.ToString() ?? "")) .Where(x => !string.IsNullOrEmpty(x.Key)) .ToList(); IEnumerable<(string Key, string Value)> filtered; if (string.IsNullOrWhiteSpace(q)) { // 우선순위 키를 앞에, 나머지 알파벳 순 var prioritySet = new HashSet(_priorityKeys, StringComparer.OrdinalIgnoreCase); var prioritized = _priorityKeys .Where(k => allVars.Any(v => string.Equals(v.Key, k, StringComparison.OrdinalIgnoreCase))) .Select(k => allVars.First(v => string.Equals(v.Key, k, StringComparison.OrdinalIgnoreCase))); var rest = allVars .Where(v => !prioritySet.Contains(v.Key)) .OrderBy(v => v.Key); filtered = prioritized.Concat(rest).Take(20); } else { // 키 또는 값에 쿼리 포함 검색 filtered = allVars .Where(v => v.Key.Contains(q, StringComparison.OrdinalIgnoreCase) || v.Value.Contains(q, StringComparison.OrdinalIgnoreCase)) .OrderBy(v => v.Key.StartsWith(q, StringComparison.OrdinalIgnoreCase) ? 0 : 1) .ThenBy(v => v.Key) .Take(20); } var items = filtered.Select(v => { // PATH처럼 긴 값은 첫 경로만 미리보기 var preview = v.Value.Length > 80 ? v.Value[..77] + "…" : v.Value; // PATH 변수는 세미콜론으로 분할하여 첫 항목만 표시 if (v.Key.Equals("PATH", StringComparison.OrdinalIgnoreCase) && v.Value.Contains(';')) preview = v.Value.Split(';')[0] + $" (외 {v.Value.Split(';').Length - 1}개)"; return new LauncherItem( v.Key, $"{preview} · Enter로 값 복사", null, v.Value, Symbol: Symbols.EnvVar); }).ToList(); if (!items.Any()) items.Add(new LauncherItem( $"'{q}' — 환경변수 없음", "해당 이름의 환경변수를 찾을 수 없습니다", null, null, Symbol: Symbols.Warning)); return Task.FromResult>(items); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is string value) { try { System.Windows.Clipboard.SetText(value); } catch (Exception) { } } return Task.CompletedTask; } }