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;
}
}