AX Commander 비교본 런처 기능 대량 이식

변경 목적: Agent Compare 아래 비교본의 개발 문서와 런처 소스를 기준으로 현재 AX Commander에 빠져 있던 신규 런처 기능을 동일한 흐름으로 옮겨, 비교본 수준의 기능 폭을 현재 제품에 반영했습니다.

핵심 수정사항: 비교본의 신규 런처 핸들러 다수를 src/AxCopilot/Handlers로 이식하고 App.xaml.cs 등록 흐름에 연결했습니다. 빠른 링크, 파일 태그, 알림 센터, 포모도로, 파일 브라우저, 핫키 관리, OCR, 세션/스케줄/매크로, Git/정규식/네트워크/압축/해시/UUID/JWT/QR 등 AX Commander 기능을 추가했습니다.

핵심 수정사항: 신규 기능이 실제 동작하도록 AppSettings 확장, SchedulerService/FileTagService/NotificationCenterService/IconCacheService/UrlTemplateEngine/PomodoroService 추가, 배치 이름변경/세션/스케줄/매크로 편집 창 추가, NotificationService와 Symbols 보강, QR/OCR용 csproj 의존성과 Windows 타겟 프레임워크를 반영했습니다.

문서 반영: README.md와 docs/DEVELOPMENT.md에 비교본 기반 런처 기능 이식 이력과 검증 결과를 업데이트했습니다.

검증 결과: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 실행 기준 경고 0개, 오류 0개를 확인했습니다.
This commit is contained in:
2026-04-05 00:59:45 +09:00
parent 0929778ca7
commit 0336904258
115 changed files with 30749 additions and 1 deletions

View File

@@ -0,0 +1,178 @@
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
namespace AxCopilot.Handlers;
/// <summary>
/// L22-3: Python pip 명령 생성기 핸들러. "pip" 프리픽스로 사용합니다.
///
/// 예: pip → 자주 쓰는 명령 목록
/// pip install → 패키지 설치 관련 명령
/// pip list → 목록 관련 명령
/// pip venv → 가상환경 관련 명령
/// pip conda → conda 관련 명령
/// pip <검색어> → 명령 검색
/// Enter → 명령어 클립보드 복사.
/// </summary>
public class PipHandler : IActionHandler
{
public string? Prefix => "pip";
public PluginMetadata Metadata => new(
"pip 명령",
"Python pip 명령 생성기 — 설치·관리·가상환경·conda",
"1.0",
"AX");
private sealed record PipCmd(
string Pip2,
string Pip3,
string Description,
string Category);
private static readonly PipCmd[] Commands =
[
// ── 설치 (install) ───────────────────────────────────────────────────
new("pip install {패키지}", "pip3 install {패키지}", "패키지 설치", "install"),
new("pip install {패키지}=={버전}", "pip3 install {패키지}=={버전}", "특정 버전 설치 (예: requests==2.31.0)", "install"),
new("pip install -r requirements.txt","pip3 install -r requirements.txt","requirements.txt 일괄 설치", "install"),
new("pip install --upgrade {패키지}", "pip3 install --upgrade {패키지}","패키지 업그레이드", "install"),
new("pip install --upgrade pip", "pip3 install --upgrade pip", "pip 자체 업그레이드", "install"),
new("pip install --user {패키지}", "pip3 install --user {패키지}", "사용자 홈에 설치 (관리자 권한 불필요)", "install"),
new("pip install -e .", "pip3 install -e .", "현재 폴더 패키지를 개발 모드로 설치", "install"),
new("pip download {패키지}", "pip3 download {패키지}", "오프라인 설치를 위한 패키지 다운로드", "install"),
// ── 제거 (uninstall) ─────────────────────────────────────────────────
new("pip uninstall {패키지}", "pip3 uninstall {패키지}", "패키지 제거", "uninstall"),
new("pip uninstall -y {패키지}", "pip3 uninstall -y {패키지}", "확인 없이 패키지 제거", "uninstall"),
new("pip uninstall -r requirements.txt -y","pip3 uninstall -r requirements.txt -y","requirements.txt 패키지 일괄 제거", "uninstall"),
// ── 목록·정보 (list) ─────────────────────────────────────────────────
new("pip list", "pip3 list", "설치된 패키지 목록", "list"),
new("pip list --outdated", "pip3 list --outdated", "업데이트 가능한 패키지 목록", "list"),
new("pip show {패키지}", "pip3 show {패키지}", "패키지 상세 정보 (버전·위치·의존성)", "list"),
new("pip freeze", "pip3 freeze", "설치 패키지 버전 고정 출력", "list"),
new("pip freeze > requirements.txt", "pip3 freeze > requirements.txt", "requirements.txt 파일 생성", "list"),
new("pip check", "pip3 check", "의존성 충돌 검사", "list"),
// ── 검색·캐시 (search) ───────────────────────────────────────────────
new("pip cache list", "pip3 cache list", "캐시 목록 확인", "search"),
new("pip cache purge", "pip3 cache purge", "캐시 전체 삭제", "search"),
new("pip index versions {패키지}", "pip3 index versions {패키지}", "PyPI에서 사용 가능한 버전 목록 조회", "search"),
new("pip config list", "pip3 config list", "pip 설정 목록", "search"),
new("pip config set global.index-url {URL}","pip3 config set global.index-url {URL}","사내 PyPI 미러 설정", "search"),
// ── 가상환경 (venv) ──────────────────────────────────────────────────
new("python -m venv .venv", "python3 -m venv .venv", "가상환경 생성 (.venv 폴더)", "venv"),
new(".venv\\Scripts\\activate", "source .venv/bin/activate", "가상환경 활성화 (Win / Mac·Linux)", "venv"),
new("deactivate", "deactivate", "가상환경 비활성화", "venv"),
new("python -m venv .venv --clear", "python3 -m venv .venv --clear", "가상환경 초기화 (재생성)", "venv"),
new("pip list --local", "pip3 list --local", "현재 가상환경 패키지만 목록", "venv"),
new("python -m site --user-site", "python3 -m site --user-site", "사용자 패키지 설치 경로 확인", "venv"),
// ── conda ────────────────────────────────────────────────────────────
new("conda create -n {환경명} python={버전}","conda create -n {환경명} python={버전}","Conda 환경 생성 (버전 지정)", "conda"),
new("conda activate {환경명}", "conda activate {환경명}", "Conda 환경 활성화", "conda"),
new("conda deactivate", "conda deactivate", "Conda 환경 비활성화", "conda"),
new("conda install {패키지}", "conda install {패키지}", "Conda로 패키지 설치", "conda"),
new("conda update {패키지}", "conda update {패키지}", "Conda 패키지 업데이트", "conda"),
new("conda list", "conda list", "현재 Conda 환경 패키지 목록", "conda"),
new("conda env list", "conda env list", "전체 Conda 환경 목록", "conda"),
new("conda env remove -n {환경명}", "conda env remove -n {환경명}", "Conda 환경 삭제", "conda"),
new("conda env export > env.yml", "conda env export > env.yml", "환경 설정을 yml 파일로 내보내기", "conda"),
new("conda env create -f env.yml", "conda env create -f env.yml", "yml 파일로 환경 복원", "conda"),
];
private static readonly (string Key, string[] Aliases, string Label)[] Categories =
[
("install", ["install", "설치", "add"], "패키지 설치"),
("uninstall", ["uninstall", "remove", "삭제"], "패키지 제거"),
("list", ["list", "목록", "show", "freeze"],"목록·정보"),
("search", ["search", "cache", "config"], "검색·캐시·설정"),
("venv", ["venv", "env", "가상환경"], "가상환경"),
("conda", ["conda", "anaconda"], "conda"),
];
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
{
var q = query.Trim();
var items = new List<LauncherItem>();
if (string.IsNullOrWhiteSpace(q))
{
items.Add(new LauncherItem("pip 명령 생성기",
"카테고리: install · uninstall · list · venv · conda",
null, null, Symbol: "\uE943"));
foreach (var (key, _, label) in Categories)
{
var cnt = Commands.Count(c => c.Category == key);
items.Add(new LauncherItem($"pip {key}", $"{label} ({cnt}개 명령)",
null, ("copy", $"pip {key}"), Symbol: "\uE943"));
}
return Task.FromResult<IEnumerable<LauncherItem>>(items);
}
var kw = q.ToLowerInvariant();
// 카테고리 일치
var cat = Categories.FirstOrDefault(c =>
c.Aliases.Any(a => a == kw || kw.StartsWith(a + " ")));
if (cat.Key != null)
{
var list = Commands.Where(c => c.Category == cat.Key).ToList();
items.Add(new LauncherItem($"{cat.Label} 명령 {list.Count}개",
"pip2 / pip3 모두 표시 · Enter: pip3 명령 복사", null, null, Symbol: "\uE943"));
foreach (var c in list) items.Add(CmdItem(c));
return Task.FromResult<IEnumerable<LauncherItem>>(items);
}
// 검색
var searched = Commands.Where(c =>
c.Pip2.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
c.Pip3.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
c.Description.Contains(kw, StringComparison.OrdinalIgnoreCase)).ToList();
if (searched.Count == 0)
{
items.Add(new LauncherItem($"'{q}' 명령을 찾을 수 없습니다",
"카테고리: install · uninstall · list · venv · conda",
null, null, Symbol: "\uE783"));
return Task.FromResult<IEnumerable<LauncherItem>>(items);
}
items.Add(new LauncherItem($"'{q}' 검색 결과 {searched.Count}개",
"Enter: pip3 명령 복사", null, null, Symbol: "\uE943"));
foreach (var c in searched) items.Add(CmdItem(c));
return Task.FromResult<IEnumerable<LauncherItem>>(items);
}
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
if (item.Data is ("copy", string text))
{
try
{
System.Windows.Application.Current.Dispatcher.Invoke(
() => Clipboard.SetText(text));
NotificationService.Notify("pip", "클립보드에 복사했습니다.");
}
catch { }
}
return Task.CompletedTask;
}
private static LauncherItem CmdItem(PipCmd c)
{
var title = c.Pip2 == c.Pip3
? c.Pip3
: $"{c.Pip3}";
var sub = c.Pip2 == c.Pip3
? c.Description
: $"{c.Description} | pip2: {c.Pip2}";
return new LauncherItem(title, sub, null, ("copy", c.Pip3), Symbol: "\uE943");
}
}