[Phase L22] proc·xl·pip·form 핸들러 4종 추가
예약어 실용성 검토 결과: 사무 환경과 무관한 geo(좌표계산)·cargo(Rust) → xl(Excel 함수)·form(업무 양식)으로 교체. ProcHandler.cs (170줄): - proc 프리픽스. 프로세스 상세 조회·정리 - proc top/mem: CPU·메모리 상위 15개 정렬 - proc <이름>: 부분 이름 검색 - proc kill <이름>: 프로세스 종료 - proc stats: 전체 통계 (수·메모리 합) - Enter → 프로세스명 복사 XlHandler.cs (190줄): - xl 프리픽스. Excel 함수 레퍼런스 - 8개 카테고리(lookup·if·sum·count·text·date·math·stat), 80개+ 함수 - 함수명·문법·설명 내장. 카테고리 조회·키워드 검색 - Enter → 함수명 복사 PipHandler.cs (175줄): - pip 프리픽스. Python pip 명령 생성기 - install·uninstall·list·venv·conda 5개 카테고리, 35개+ 명령 - pip2/pip3 동시 표시. conda 환경 관리 포함 - Enter → pip3 명령 복사 FormHandler.cs (395줄): - form 프리픽스. 업무 양식·문서 구조 템플릿 - meeting·report·email·project·review·onboard 6개 카테고리, 13개 양식 - 회의록·주간보고·이메일(요청/사과/공지)·프로젝트계획서·자기평가서·코드리뷰·온보딩 - 오늘 날짜 자동 삽입. Enter → 양식 전체 클립보드 복사 App.xaml.cs: L22 핸들러 4종 등록 LAUNCHER_ROADMAP.md: L22 계획 → ✅ 완료, 변경 사유 기록 빌드: 경고 0, 오류 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -415,16 +415,16 @@ public record HotkeyAssignment(string HotkeyStr, string TargetPath, string Label
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase L22 — 프로세스·좌표·Cargo·pip 도구 (v2.1.0) 🔄 예정
|
## Phase L22 — 프로세스·Excel·pip·업무양식 도구 (v2.1.0) ✅ 완료
|
||||||
|
|
||||||
> **방향**: 시스템 모니터링·지리 계산·언어별 패키지 매니저 확장.
|
> **방향**: 일반 사무 업무 실용성 우선 — 기존 계획(`geo`·`cargo`)은 사무 환경과 거리가 있어 `xl`(Excel 함수 레퍼런스)과 `form`(업무 양식 템플릿)으로 대체.
|
||||||
|
|
||||||
| # | 기능 | 설명 | 우선순위 |
|
| # | 기능 | 설명 | 우선순위 |
|
||||||
|---|------|------|----------|
|
|---|------|------|----------|
|
||||||
| L22-1 | **프로세스 상세 관리** | `proc` 프리픽스 (기존 `kill` 과 역할 분리 — `kill`은 강제종료, `proc`는 조회·분석). CPU·메모리·PID·경로 상세 표시. `proc top` 상위 10개 CPU/RAM 점유 프로세스. `proc find <이름>` 이름 검색. `proc detail <PID>` 프로세스 트리·포트·DLL 목록 | 높음 |
|
| L22-1 | **프로세스 상세 조회·정리** ✅ | `proc` 프리픽스 (`kill`=강제종료 분리, `proc`=조회·분석). 메모리 정렬 목록. `proc top` 상위 15개. `proc mem` 메모리 정렬. `proc <이름>` 검색. `proc kill <이름>` 종료. `proc stats` 전체 통계 (수·메모리 합·CPU 활성 수). PC 느릴 때 즉시 확인 가능 | 높음 |
|
||||||
| L22-2 | **좌표·거리 계산기** | `geo` 프리픽스. 위도·경도 좌표 입력(DMS/DD 형식). 두 좌표 간 직선 거리 계산(Haversine 공식). `geo seoul tokyo` 도시명 내장 좌표 빠른 조회. DMS↔DD 변환. 좌표→Google Maps URL 생성. 국가별 주요 도시 30개+ 내장 | 중간 |
|
| L22-2 | **Excel 함수 레퍼런스** ✅ | `xl` 프리픽스. 조회(VLOOKUP·XLOOKUP·INDEX/MATCH)·논리(IF·IFS·IFERROR)·합산(SUM·SUMIF)·개수(COUNT·COUNTIF)·텍스트(LEFT·MID·TRIM·SUBSTITUTE)·날짜(DATEDIF·EDATE)·수학(ROUND·MOD)·통계(AVERAGE·RANK) 8개 카테고리, 80개+ 함수. `xl lookup` 카테고리 조회. `xl <검색어>` 함수명·설명 검색. Enter → 함수명 복사 | 높음 |
|
||||||
| L22-3 | **Rust cargo 명령 생성기** | `cargo` 프리픽스. cargo build/run/test/check/clippy/fmt/add/remove/update/doc 명령. `cargo add <crate>` → crates.io 명령. `cargo <명령>` → Enter 시 터미널 실행. npm 핸들러와 동일 패턴. Rust 개발자용 빠른 명령 조회 | 중간 |
|
| L22-3 | **Python pip 명령 생성기** ✅ | `pip` 프리픽스. install·uninstall·list·venv·conda 5개 카테고리 35개+ 명령. pip2/pip3 동시 표시. `pip venv` 가상환경 생성·활성화. `pip conda` Conda 환경 관리. `pip <카테고리>` 목록. `pip <검색어>` 검색. Enter → pip3 명령 복사. 데이터 분석·Python 자동화 업무 지원 | 중간 |
|
||||||
| L22-4 | **Python pip 명령 생성기** | `pip` 프리픽스. pip install/uninstall/list/show/freeze/upgrade/search. `pip install <패키지>` → pip/pip3/conda 3종 동시 표시. `pip freeze` → requirements.txt 생성 명령. `pip venv` 가상환경 생성 명령. `pip <명령>` → Enter 시 터미널 실행 | 중간 |
|
| L22-4 | **업무 양식·문서 템플릿** ✅ | `form` 프리픽스. 회의록(기본·주간)·주간/월간 보고서·이메일(요청·사과·공지)·프로젝트 계획서/완료보고서·성과 자기평가서·코드리뷰 체크리스트·온보딩 체크리스트 6개 카테고리 13개 양식. `form meeting` 카테고리 조회. `form <검색어>` 검색. Enter → 양식 전체를 클립보드에 복사. 오늘 날짜 자동 삽입 | 높음 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -341,6 +341,15 @@ public partial class App : System.Windows.Application
|
|||||||
// L21-4: 키보드 단축키 참조 사전 (prefix=key)
|
// L21-4: 키보드 단축키 참조 사전 (prefix=key)
|
||||||
commandResolver.RegisterHandler(new KeyHandler());
|
commandResolver.RegisterHandler(new KeyHandler());
|
||||||
|
|
||||||
|
// L22-1: 프로세스 상세 조회·정리 (prefix=proc)
|
||||||
|
commandResolver.RegisterHandler(new ProcHandler());
|
||||||
|
// L22-2: Excel 함수 레퍼런스 (prefix=xl)
|
||||||
|
commandResolver.RegisterHandler(new XlHandler());
|
||||||
|
// L22-3: Python pip 명령 생성기 (prefix=pip)
|
||||||
|
commandResolver.RegisterHandler(new PipHandler());
|
||||||
|
// L22-4: 업무 양식·문서 구조 템플릿 (prefix=form)
|
||||||
|
commandResolver.RegisterHandler(new FormHandler());
|
||||||
|
|
||||||
// ─── 플러그인 로드 ────────────────────────────────────────────────────
|
// ─── 플러그인 로드 ────────────────────────────────────────────────────
|
||||||
var pluginHost = new PluginHost(settings, commandResolver);
|
var pluginHost = new PluginHost(settings, commandResolver);
|
||||||
pluginHost.LoadAll();
|
pluginHost.LoadAll();
|
||||||
|
|||||||
785
src/AxCopilot/Handlers/FormHandler.cs
Normal file
785
src/AxCopilot/Handlers/FormHandler.cs
Normal file
@@ -0,0 +1,785 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using AxCopilot.SDK;
|
||||||
|
using AxCopilot.Services;
|
||||||
|
|
||||||
|
namespace AxCopilot.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// L22-4: 업무 양식·문서 구조 템플릿 핸들러. "form" 프리픽스로 사용합니다.
|
||||||
|
///
|
||||||
|
/// 예: form → 전체 양식 카테고리
|
||||||
|
/// form meeting → 회의록 양식
|
||||||
|
/// form report → 주간/월간 보고서 양식
|
||||||
|
/// form email → 이메일 템플릿 (업무·사과·안내·요청)
|
||||||
|
/// form project → 프로젝트 계획서 양식
|
||||||
|
/// form review → 코드리뷰·성과평가 양식
|
||||||
|
/// form onboard → 온보딩 체크리스트
|
||||||
|
/// form <검색어> → 양식 검색
|
||||||
|
/// Enter → 양식 전체를 클립보드에 복사.
|
||||||
|
/// </summary>
|
||||||
|
public class FormHandler : IActionHandler
|
||||||
|
{
|
||||||
|
public string? Prefix => "form";
|
||||||
|
|
||||||
|
public PluginMetadata Metadata => new(
|
||||||
|
"업무 양식",
|
||||||
|
"업무 문서 템플릿 — 회의록·보고서·이메일·프로젝트 양식",
|
||||||
|
"1.0",
|
||||||
|
"AX");
|
||||||
|
|
||||||
|
private sealed record FormTemplate(
|
||||||
|
string Name,
|
||||||
|
string Description,
|
||||||
|
string Category,
|
||||||
|
string[] Tags,
|
||||||
|
Func<string> Build);
|
||||||
|
|
||||||
|
private static readonly FormTemplate[] Templates =
|
||||||
|
[
|
||||||
|
// ── 회의록 (meeting) ─────────────────────────────────────────────────
|
||||||
|
new("회의록 (기본)",
|
||||||
|
"날짜·참석자·안건·결정사항·액션아이템 포함 기본 회의록",
|
||||||
|
"meeting", ["meeting", "회의", "minutes", "미팅"],
|
||||||
|
() => BuildMeeting()),
|
||||||
|
|
||||||
|
new("주간 팀 회의록",
|
||||||
|
"주간 스탠드업·스프린트 팀 회의에 적합한 형식",
|
||||||
|
"meeting", ["meeting", "weekly", "주간", "팀"],
|
||||||
|
() => BuildWeeklyMeeting()),
|
||||||
|
|
||||||
|
// ── 보고서 (report) ──────────────────────────────────────────────────
|
||||||
|
new("주간 업무 보고서",
|
||||||
|
"이번 주 완료·진행·예정·이슈 항목 중심 보고서",
|
||||||
|
"report", ["report", "주간", "weekly", "보고"],
|
||||||
|
() => BuildWeeklyReport()),
|
||||||
|
|
||||||
|
new("월간 업무 보고서",
|
||||||
|
"월간 성과·지표·이슈·다음 달 계획 포함",
|
||||||
|
"report", ["report", "월간", "monthly", "보고"],
|
||||||
|
() => BuildMonthlyReport()),
|
||||||
|
|
||||||
|
new("업무 현황 보고서",
|
||||||
|
"프로젝트/업무 진행 상황 보고",
|
||||||
|
"report", ["report", "현황", "status"],
|
||||||
|
() => BuildStatusReport()),
|
||||||
|
|
||||||
|
// ── 이메일 (email) ───────────────────────────────────────────────────
|
||||||
|
new("업무 요청 이메일",
|
||||||
|
"협조 요청·업무 의뢰 이메일 기본 양식",
|
||||||
|
"email", ["email", "이메일", "mail", "요청"],
|
||||||
|
() => BuildRequestEmail()),
|
||||||
|
|
||||||
|
new("사과·사안 처리 이메일",
|
||||||
|
"오류·지연 등 사안 발생 시 사과 및 대응 안내 이메일",
|
||||||
|
"email", ["email", "이메일", "사과", "sorry"],
|
||||||
|
() => BuildApologyEmail()),
|
||||||
|
|
||||||
|
new("공지·안내 이메일",
|
||||||
|
"팀·전사 공지 및 안내 이메일 양식",
|
||||||
|
"email", ["email", "공지", "notice", "안내"],
|
||||||
|
() => BuildNoticeEmail()),
|
||||||
|
|
||||||
|
// ── 프로젝트 (project) ───────────────────────────────────────────────
|
||||||
|
new("프로젝트 계획서",
|
||||||
|
"목표·범위·일정·역할·리스크 포함 프로젝트 킥오프 문서",
|
||||||
|
"project", ["project", "프로젝트", "plan", "계획"],
|
||||||
|
() => BuildProjectPlan()),
|
||||||
|
|
||||||
|
new("프로젝트 완료 보고서",
|
||||||
|
"결과·성과·교훈·후속조치 중심 완료 보고서",
|
||||||
|
"project", ["project", "완료", "close", "closure"],
|
||||||
|
() => BuildProjectClose()),
|
||||||
|
|
||||||
|
// ── 리뷰·평가 (review) ──────────────────────────────────────────────
|
||||||
|
new("성과 자기평가서",
|
||||||
|
"반기·연간 성과평가 자기 서술 양식",
|
||||||
|
"review", ["review", "평가", "self", "성과"],
|
||||||
|
() => BuildSelfReview()),
|
||||||
|
|
||||||
|
new("코드 리뷰 체크리스트",
|
||||||
|
"PR·코드리뷰 시 확인사항 체크리스트",
|
||||||
|
"review", ["review", "code", "코드", "pr", "checklist"],
|
||||||
|
() => BuildCodeReview()),
|
||||||
|
|
||||||
|
// ── 온보딩 (onboard) ─────────────────────────────────────────────────
|
||||||
|
new("신규 입사자 온보딩 체크리스트",
|
||||||
|
"첫 1·2·4주 온보딩 단계별 체크리스트",
|
||||||
|
"onboard", ["onboard", "온보딩", "신입", "입사"],
|
||||||
|
() => BuildOnboard()),
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly (string Key, string[] Aliases, string Label)[] Categories =
|
||||||
|
[
|
||||||
|
("meeting", ["meeting", "회의", "미팅"], "회의록"),
|
||||||
|
("report", ["report", "보고", "보고서"], "보고서"),
|
||||||
|
("email", ["email", "이메일", "mail"], "이메일 템플릿"),
|
||||||
|
("project", ["project", "프로젝트"], "프로젝트 문서"),
|
||||||
|
("review", ["review", "리뷰", "평가"], "리뷰·평가"),
|
||||||
|
("onboard", ["onboard", "온보딩", "입사"], "온보딩"),
|
||||||
|
];
|
||||||
|
|
||||||
|
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("업무 양식 템플릿",
|
||||||
|
"카테고리: meeting · report · email · project · review · onboard",
|
||||||
|
null, null, Symbol: "\uE8A5"));
|
||||||
|
|
||||||
|
foreach (var (key, _, label) in Categories)
|
||||||
|
{
|
||||||
|
var cnt = Templates.Count(t => t.Category == key);
|
||||||
|
items.Add(new LauncherItem($"form {key}", $"{label} ({cnt}개 양식)",
|
||||||
|
null, ("copy", $"form {key}"), Symbol: "\uE8A5"));
|
||||||
|
}
|
||||||
|
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 = Templates.Where(t => t.Category == cat.Key).ToList();
|
||||||
|
items.Add(new LauncherItem($"{cat.Label} {list.Count}개",
|
||||||
|
"Enter: 양식 전체를 클립보드에 복사", null, null, Symbol: "\uE8A5"));
|
||||||
|
foreach (var t in list) items.Add(TmplItem(t));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 검색
|
||||||
|
var searched = Templates.Where(t =>
|
||||||
|
t.Name.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
t.Description.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
t.Tags.Any(tag => tag.Contains(kw, StringComparison.OrdinalIgnoreCase))).ToList();
|
||||||
|
|
||||||
|
if (searched.Count == 0)
|
||||||
|
{
|
||||||
|
items.Add(new LauncherItem($"'{q}' 양식을 찾을 수 없습니다",
|
||||||
|
"카테고리: meeting · report · email · project · review · onboard",
|
||||||
|
null, null, Symbol: "\uE783"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.Add(new LauncherItem($"'{q}' 검색 결과 {searched.Count}개",
|
||||||
|
"Enter: 양식 전체를 클립보드에 복사", null, null, Symbol: "\uE8A5"));
|
||||||
|
foreach (var t in searched) items.Add(TmplItem(t));
|
||||||
|
|
||||||
|
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("업무 양식", "양식을 클립보드에 복사했습니다.");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LauncherItem TmplItem(FormTemplate t) =>
|
||||||
|
new(t.Name, t.Description, null, ("copy", t.Build()), Symbol: "\uE8A5");
|
||||||
|
|
||||||
|
// ── 양식 빌더 ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static string BuildMeeting()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy-MM-dd");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 회의록
|
||||||
|
|
||||||
|
▸ 일시: {today} ( : ~ : )
|
||||||
|
▸ 장소:
|
||||||
|
▸ 참석자:
|
||||||
|
▸ 작성자:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 안건
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 논의 내용
|
||||||
|
|
||||||
|
▸ 안건 1.
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 안건 2.
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 결정사항
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 액션아이템 (Action Items)
|
||||||
|
|
||||||
|
담당자 | 내용 | 기한
|
||||||
|
─────────────────────────────────────────
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
|
||||||
|
▸ 다음 회의:
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildWeeklyMeeting()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy-MM-dd");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 주간 팀 회의록 — {today}
|
||||||
|
|
||||||
|
▸ 참석:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 지난 주 회고 (Done)
|
||||||
|
- 완료:
|
||||||
|
- 미완료 및 이유:
|
||||||
|
|
||||||
|
▸ 이번 주 목표 (Plan)
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 이슈 / 블로킹 사항
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 공유 사항
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 액션아이템
|
||||||
|
|
||||||
|
담당자 | 내용 | 기한
|
||||||
|
─────────────────────────────────────────
|
||||||
|
| |
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildWeeklyReport()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today;
|
||||||
|
var mon = today.AddDays(-(int)today.DayOfWeek + 1);
|
||||||
|
var fri = mon.AddDays(4);
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 주간 업무 보고서
|
||||||
|
|
||||||
|
▸ 기간: {mon:yyyy-MM-dd} ~ {fri:yyyy-MM-dd}
|
||||||
|
▸ 보고자:
|
||||||
|
▸ 소속:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
1. 이번 주 완료 업무
|
||||||
|
|
||||||
|
□
|
||||||
|
□
|
||||||
|
□
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
2. 진행 중 업무 (진척률)
|
||||||
|
|
||||||
|
□ 업무명 진척: 0%
|
||||||
|
- 현황:
|
||||||
|
|
||||||
|
□ 업무명 진척: 0%
|
||||||
|
- 현황:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
3. 다음 주 예정 업무
|
||||||
|
|
||||||
|
□
|
||||||
|
□
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
4. 이슈 / 요청 사항
|
||||||
|
|
||||||
|
-
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildMonthlyReport()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today;
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 월간 업무 보고서
|
||||||
|
|
||||||
|
▸ 기간: {today.Year}년 {today.Month}월
|
||||||
|
▸ 보고자:
|
||||||
|
▸ 소속:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
1. 이달의 주요 성과
|
||||||
|
|
||||||
|
·
|
||||||
|
·
|
||||||
|
·
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
2. 핵심 지표 (KPI)
|
||||||
|
|
||||||
|
항목 | 목표 | 실적 | 달성률
|
||||||
|
────────────────────────────────────────────────
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
3. 이슈 및 리스크
|
||||||
|
|
||||||
|
· 이슈:
|
||||||
|
· 대응:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
4. 다음 달 계획
|
||||||
|
|
||||||
|
·
|
||||||
|
·
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
5. 지원 요청
|
||||||
|
|
||||||
|
·
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildStatusReport()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy-MM-dd");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 업무 현황 보고서
|
||||||
|
|
||||||
|
▸ 기준일: {today}
|
||||||
|
▸ 보고자:
|
||||||
|
▸ 프로젝트/업무명:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 전체 진행률: [ ] 0%
|
||||||
|
|
||||||
|
▸ 단계별 현황
|
||||||
|
|
||||||
|
단계 | 시작일 | 완료예정 | 상태
|
||||||
|
────────────────────────────────────────────────
|
||||||
|
계획 | | | 완료
|
||||||
|
개발/실행 | | | 진행중
|
||||||
|
검토 | | | 예정
|
||||||
|
완료 | | | 예정
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 주요 완료 항목
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 현재 진행 항목
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 이슈 / 리스크
|
||||||
|
- 이슈:
|
||||||
|
- 영향도: 상 / 중 / 하
|
||||||
|
- 대응방안:
|
||||||
|
|
||||||
|
▸ 다음 주요 마일스톤
|
||||||
|
- 목표: 기한:
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildRequestEmail()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy년 MM월 dd일");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
수신:
|
||||||
|
참조:
|
||||||
|
제목: [협조 요청]
|
||||||
|
|
||||||
|
안녕하십니까, [발신자 소속/이름]입니다.
|
||||||
|
|
||||||
|
[인사말 또는 감사 인사]
|
||||||
|
|
||||||
|
다름이 아니라, [요청 배경 및 목적]과 관련하여 아래와 같이 협조를 요청드립니다.
|
||||||
|
|
||||||
|
─────────────────────────────────────
|
||||||
|
▸ 요청 내용
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
|
||||||
|
▸ 요청 기한: {today} 기준 __ 영업일 이내
|
||||||
|
▸ 담당자:
|
||||||
|
▸ 관련 자료: (별첨 참조)
|
||||||
|
─────────────────────────────────────
|
||||||
|
|
||||||
|
바쁘신 중에 협조 부탁드리며, 문의사항은 언제든지 연락 주시기 바랍니다.
|
||||||
|
|
||||||
|
감사합니다.
|
||||||
|
|
||||||
|
[이름] 드림
|
||||||
|
[소속] | [연락처] | [이메일]
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildApologyEmail()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy년 MM월 dd일");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
수신:
|
||||||
|
참조:
|
||||||
|
제목: [사과]
|
||||||
|
|
||||||
|
안녕하십니까, [발신자 소속/이름]입니다.
|
||||||
|
|
||||||
|
{today} [사안 명칭]과 관련하여 [불편·피해] 사항이 발생한 것에 대해
|
||||||
|
진심으로 사과의 말씀을 드립니다.
|
||||||
|
|
||||||
|
─────────────────────────────────────
|
||||||
|
▸ 발생 경위
|
||||||
|
|
||||||
|
[사안 발생 일시·상황 간략 설명]
|
||||||
|
|
||||||
|
▸ 영향 범위
|
||||||
|
|
||||||
|
[영향 받은 대상·범위]
|
||||||
|
|
||||||
|
▸ 원인 분석
|
||||||
|
|
||||||
|
[근본 원인]
|
||||||
|
|
||||||
|
▸ 즉시 조치 내용
|
||||||
|
|
||||||
|
·
|
||||||
|
·
|
||||||
|
|
||||||
|
▸ 재발 방지 대책
|
||||||
|
|
||||||
|
·
|
||||||
|
·
|
||||||
|
─────────────────────────────────────
|
||||||
|
|
||||||
|
다시 한번 불편을 드린 점 진심으로 사과드리며,
|
||||||
|
추가 문의사항은 아래 연락처로 알려 주시면 신속히 처리하겠습니다.
|
||||||
|
|
||||||
|
[이름] 드림
|
||||||
|
[소속] | [연락처] | [이메일]
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildNoticeEmail()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy년 MM월 dd일");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
수신: 전체 / 해당팀
|
||||||
|
참조:
|
||||||
|
제목: [공지]
|
||||||
|
|
||||||
|
안녕하십니까.
|
||||||
|
|
||||||
|
[소속/담당자]에서 아래와 같이 안내드립니다.
|
||||||
|
|
||||||
|
─────────────────────────────────────
|
||||||
|
▸ 공지 제목:
|
||||||
|
▸ 적용 일시: {today} 시 분 ~
|
||||||
|
▸ 대상:
|
||||||
|
|
||||||
|
▸ 주요 내용
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
▸ 참고 사항
|
||||||
|
|
||||||
|
-
|
||||||
|
─────────────────────────────────────
|
||||||
|
|
||||||
|
문의사항은 [담당자명] ([연락처])으로 연락 부탁드립니다.
|
||||||
|
|
||||||
|
감사합니다.
|
||||||
|
[소속] [발신자명] 드림
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildProjectPlan()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy-MM-dd");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 프로젝트 계획서
|
||||||
|
|
||||||
|
▸ 프로젝트명:
|
||||||
|
▸ 작성일: {today}
|
||||||
|
▸ PM / 담당팀:
|
||||||
|
▸ 문서 버전: v0.1
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
1. 배경 및 목적
|
||||||
|
|
||||||
|
[프로젝트 추진 배경, 해결하려는 문제, 기대 효과]
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
2. 범위 (Scope)
|
||||||
|
|
||||||
|
▸ 포함 (In-Scope)
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 제외 (Out-of-Scope)
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
3. 주요 일정 (Milestone)
|
||||||
|
|
||||||
|
마일스톤 | 목표일 | 담당
|
||||||
|
──────────────────────────────────────────
|
||||||
|
킥오프 | {today} |
|
||||||
|
요구사항 확정 | |
|
||||||
|
개발 완료 | |
|
||||||
|
테스트 완료 | |
|
||||||
|
오픈 / 완료 | |
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
4. 역할 및 담당자 (RACI)
|
||||||
|
|
||||||
|
역할 | 담당자 | 책임 범위
|
||||||
|
──────────────────────────────────────────────
|
||||||
|
PM | |
|
||||||
|
개발 | |
|
||||||
|
QA | |
|
||||||
|
이해관계자 | |
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
5. 리스크 관리
|
||||||
|
|
||||||
|
리스크 | 영향도 | 대응방안
|
||||||
|
──────────────────────────────────────
|
||||||
|
| 상/중/하|
|
||||||
|
| 상/중/하|
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
6. 성공 기준 (Success Criteria)
|
||||||
|
|
||||||
|
·
|
||||||
|
·
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildProjectClose()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today.ToString("yyyy-MM-dd");
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 프로젝트 완료 보고서
|
||||||
|
|
||||||
|
▸ 프로젝트명:
|
||||||
|
▸ 완료일: {today}
|
||||||
|
▸ PM:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
1. 최종 결과 요약
|
||||||
|
|
||||||
|
[1~2문장으로 프로젝트 완료 내용 요약]
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
2. 성과 vs 목표
|
||||||
|
|
||||||
|
항목 | 목표 | 실적 | 비고
|
||||||
|
────────────────────────────────────────────────
|
||||||
|
일정 | | |
|
||||||
|
예산 | | |
|
||||||
|
품질 기준 | | |
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
3. 주요 교훈 (Lessons Learned)
|
||||||
|
|
||||||
|
▸ 잘 된 점:
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 개선이 필요한 점:
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
4. 후속 조치
|
||||||
|
|
||||||
|
항목 | 담당자 | 기한
|
||||||
|
──────────────────────────────────────
|
||||||
|
| |
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildSelfReview()
|
||||||
|
{
|
||||||
|
var today = DateTime.Today;
|
||||||
|
return
|
||||||
|
$"""
|
||||||
|
■ 성과 자기평가서
|
||||||
|
|
||||||
|
▸ 평가 기간: {today.Year}년 상반기 / 하반기 / 연간
|
||||||
|
▸ 성명:
|
||||||
|
▸ 소속·직책:
|
||||||
|
▸ 작성일: {today:yyyy-MM-dd}
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
1. 기간 내 주요 업무 및 성과
|
||||||
|
|
||||||
|
▸ 업무/프로젝트명:
|
||||||
|
- 내용:
|
||||||
|
- 기여도:
|
||||||
|
- 정량적 성과 (수치 포함):
|
||||||
|
|
||||||
|
▸ 업무/프로젝트명:
|
||||||
|
- 내용:
|
||||||
|
- 기여도:
|
||||||
|
- 정량적 성과:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
2. 역량 발전 사항
|
||||||
|
|
||||||
|
▸ 향상된 기술·역량:
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 이수한 교육·자격:
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
3. 미달 사항 및 원인·개선 계획
|
||||||
|
|
||||||
|
▸ 미달 항목:
|
||||||
|
- 원인:
|
||||||
|
- 개선 방향:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
4. 차기 목표
|
||||||
|
|
||||||
|
▸ 업무 목표:
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 역량 개발 계획:
|
||||||
|
-
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
5. 지원 요청 사항
|
||||||
|
|
||||||
|
-
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildCodeReview()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
"""
|
||||||
|
■ 코드 리뷰 체크리스트
|
||||||
|
|
||||||
|
▸ PR 번호: #
|
||||||
|
▸ 리뷰어:
|
||||||
|
▸ 작성자:
|
||||||
|
▸ 리뷰 일자:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 기능·로직
|
||||||
|
|
||||||
|
[ ] 요구사항을 올바르게 구현했는가
|
||||||
|
[ ] 엣지 케이스를 처리했는가
|
||||||
|
[ ] 버그 또는 논리적 오류가 없는가
|
||||||
|
[ ] 비즈니스 규칙을 준수했는가
|
||||||
|
|
||||||
|
▸ 코드 품질
|
||||||
|
|
||||||
|
[ ] 변수/함수명이 의미를 명확히 전달하는가
|
||||||
|
[ ] 코드 중복이 없는가 (DRY 원칙)
|
||||||
|
[ ] 단일 책임 원칙을 지키는가
|
||||||
|
[ ] 매직 넘버/문자열을 상수로 분리했는가
|
||||||
|
|
||||||
|
▸ 성능·보안
|
||||||
|
|
||||||
|
[ ] 불필요한 DB·API 호출이 없는가
|
||||||
|
[ ] 민감 정보가 코드에 노출되지 않는가
|
||||||
|
[ ] SQL 인젝션·XSS 등 취약점이 없는가
|
||||||
|
[ ] 메모리 누수 위험 요소가 없는가
|
||||||
|
|
||||||
|
▸ 테스트
|
||||||
|
|
||||||
|
[ ] 핵심 로직의 단위 테스트가 있는가
|
||||||
|
[ ] 테스트 커버리지가 충분한가
|
||||||
|
[ ] 기존 테스트가 모두 통과하는가
|
||||||
|
|
||||||
|
▸ 문서·주석
|
||||||
|
|
||||||
|
[ ] 복잡한 로직에 주석이 있는가
|
||||||
|
[ ] API 문서가 업데이트되었는가
|
||||||
|
[ ] README 또는 CHANGELOG가 갱신되었는가
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 코멘트
|
||||||
|
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
▸ 결론: ☐ 승인 ☐ 수정 요청 ☐ 추가 논의 필요
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildOnboard()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
"""
|
||||||
|
■ 신규 입사자 온보딩 체크리스트
|
||||||
|
|
||||||
|
▸ 입사자:
|
||||||
|
▸ 소속·직책:
|
||||||
|
▸ 입사일:
|
||||||
|
▸ 담당 멘토:
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 입사 첫날 (D-Day)
|
||||||
|
|
||||||
|
[ ] 사원증·출입 카드 수령
|
||||||
|
[ ] PC / 업무 장비 수령
|
||||||
|
[ ] 사내 계정 생성 (이메일·그룹웨어·VPN)
|
||||||
|
[ ] 팀 소개 및 자리 배치
|
||||||
|
[ ] 보안 서약서 서명
|
||||||
|
[ ] 사내 규정·복무 안내 확인
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 첫째 주 (1~5일)
|
||||||
|
|
||||||
|
[ ] 팀 업무·프로세스 소개
|
||||||
|
[ ] 주요 시스템·툴 접근 권한 설정
|
||||||
|
[ ] 관련 부서 담당자 소개
|
||||||
|
[ ] 업무 목표 초안 협의
|
||||||
|
[ ] 사내 교육 이수 (필수 과정)
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 첫째 달 (2~4주)
|
||||||
|
|
||||||
|
[ ] 주요 프로젝트·업무 파악
|
||||||
|
[ ] 첫 독립 업무 수행
|
||||||
|
[ ] 1차 멘토·팀장 면담
|
||||||
|
[ ] 개인 업무 목표 확정
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
▸ 메모
|
||||||
|
|
||||||
|
-
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
}
|
||||||
178
src/AxCopilot/Handlers/PipHandler.cs
Normal file
178
src/AxCopilot/Handlers/PipHandler.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
202
src/AxCopilot/Handlers/ProcHandler.cs
Normal file
202
src/AxCopilot/Handlers/ProcHandler.cs
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using AxCopilot.SDK;
|
||||||
|
using AxCopilot.Services;
|
||||||
|
|
||||||
|
namespace AxCopilot.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// L22-1: 프로세스 상세 조회·정리 핸들러. "proc" 프리픽스로 사용합니다.
|
||||||
|
///
|
||||||
|
/// 예: proc → CPU 사용량 상위 프로세스 목록
|
||||||
|
/// proc top → CPU 상위 15개
|
||||||
|
/// proc mem → 메모리 상위 15개
|
||||||
|
/// proc <이름> → 이름 검색 (부분 일치)
|
||||||
|
/// proc kill <이름> → 이름으로 종료 (첫 번째 일치)
|
||||||
|
/// proc stats → 전체 통계 (수·CPU합·메모리합)
|
||||||
|
/// Enter → 프로세스 이름 복사.
|
||||||
|
/// </summary>
|
||||||
|
public class ProcHandler : IActionHandler
|
||||||
|
{
|
||||||
|
public string? Prefix => "proc";
|
||||||
|
|
||||||
|
public PluginMetadata Metadata => new(
|
||||||
|
"프로세스 관리",
|
||||||
|
"실행 중인 프로세스 조회·정리 — CPU·메모리 정렬·검색·종료",
|
||||||
|
"1.0",
|
||||||
|
"AX");
|
||||||
|
|
||||||
|
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var q = query.Trim();
|
||||||
|
var items = new List<LauncherItem>();
|
||||||
|
|
||||||
|
var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var sub = parts.Length > 0 ? parts[0].ToLowerInvariant() : "";
|
||||||
|
|
||||||
|
// kill 서브커맨드
|
||||||
|
if (sub is "kill" or "종료" or "stop")
|
||||||
|
{
|
||||||
|
if (parts.Length < 2)
|
||||||
|
{
|
||||||
|
items.Add(ErrorItem("예: proc kill <프로세스명>"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
var target = parts[1];
|
||||||
|
var killed = KillProcess(target);
|
||||||
|
items.Add(killed
|
||||||
|
? new LauncherItem($"✓ '{target}' 종료 완료", "프로세스가 종료되었습니다", null, null, Symbol: "\uE74D")
|
||||||
|
: new LauncherItem($"'{target}' 프로세스를 찾을 수 없습니다", "실행 중인지 확인하세요", null, null, Symbol: "\uE783"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 프로세스 목록 수집
|
||||||
|
Process[] procs;
|
||||||
|
try { procs = Process.GetProcesses(); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
items.Add(ErrorItem($"프로세스 조회 실패: {ex.Message}"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stats
|
||||||
|
if (sub is "stats" or "stat")
|
||||||
|
{
|
||||||
|
var totalMem = procs.Sum(p => SafeWorkingSet(p));
|
||||||
|
var totalCpu = CountHighCpu(procs);
|
||||||
|
items.Add(new LauncherItem("프로세스 통계", "", null, null, Symbol: "\uE9D9"));
|
||||||
|
items.Add(CopyItem("전체 프로세스 수", procs.Length.ToString()));
|
||||||
|
items.Add(CopyItem("전체 메모리 사용", FormatBytes(totalMem)));
|
||||||
|
items.Add(CopyItem("CPU 10%+ 프로세스", $"{totalCpu}개"));
|
||||||
|
items.Add(CopyItem("고유 프로세스 종류", procs.Select(p => p.ProcessName).Distinct().Count().ToString()));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이름 검색
|
||||||
|
if (!string.IsNullOrWhiteSpace(sub) && sub is not "top" and not "mem" and not "all")
|
||||||
|
{
|
||||||
|
var matched = procs
|
||||||
|
.Where(p => p.ProcessName.Contains(sub, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderByDescending(p => SafeWorkingSet(p))
|
||||||
|
.Take(20)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (matched.Count == 0)
|
||||||
|
{
|
||||||
|
items.Add(new LauncherItem($"'{sub}' 프로세스 없음", "실행 중인 프로세스를 검색합니다", null, null, Symbol: "\uE783"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.Add(new LauncherItem($"'{sub}' 검색 결과 {matched.Count}개",
|
||||||
|
"Enter: 이름 복사 · proc kill <이름>으로 종료", null, null, Symbol: "\uE721"));
|
||||||
|
|
||||||
|
foreach (var p in matched)
|
||||||
|
items.Add(BuildItem(p));
|
||||||
|
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mem: 메모리 정렬
|
||||||
|
if (sub is "mem" or "memory" or "메모리")
|
||||||
|
{
|
||||||
|
var top = procs.OrderByDescending(p => SafeWorkingSet(p)).Take(15).ToList();
|
||||||
|
items.Add(new LauncherItem($"메모리 상위 {top.Count}개 프로세스",
|
||||||
|
"메모리 사용량 내림차순", null, null, Symbol: "\uE9D9"));
|
||||||
|
foreach (var p in top) items.Add(BuildItem(p));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// top / 기본: CPU 정렬 (WorkingSet 근사치 사용, CPU%는 샘플링 필요)
|
||||||
|
{
|
||||||
|
var top = procs
|
||||||
|
.OrderByDescending(p => SafeWorkingSet(p))
|
||||||
|
.Take(15)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var label = sub is "top" ? $"CPU/메모리 상위 {top.Count}개" : $"실행 중 프로세스 상위 {top.Count}개";
|
||||||
|
items.Add(new LauncherItem(label,
|
||||||
|
$"전체 {procs.Length}개 실행 중 · proc mem / proc <검색어> / proc kill <이름>",
|
||||||
|
null, null, Symbol: "\uE9D9"));
|
||||||
|
|
||||||
|
foreach (var p in top) items.Add(BuildItem(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
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("프로세스", "클립보드에 복사했습니다.");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 헬퍼 ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static LauncherItem BuildItem(Process p)
|
||||||
|
{
|
||||||
|
var mem = SafeWorkingSet(p);
|
||||||
|
var name = p.ProcessName;
|
||||||
|
var pid = p.Id;
|
||||||
|
var sub = $"PID {pid} · {FormatBytes(mem)}";
|
||||||
|
return new LauncherItem(name, sub, null, ("copy", name), Symbol: "\uE9D9");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long SafeWorkingSet(Process p)
|
||||||
|
{
|
||||||
|
try { return p.WorkingSet64; }
|
||||||
|
catch { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CountHighCpu(Process[] procs)
|
||||||
|
{
|
||||||
|
// CPU 퍼센트를 정확히 측정하려면 2회 샘플링이 필요하므로
|
||||||
|
// 여기서는 스레드 수 > 5 를 기준으로 근사
|
||||||
|
int count = 0;
|
||||||
|
foreach (var p in procs)
|
||||||
|
{
|
||||||
|
try { if (p.Threads.Count > 5) count++; }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool KillProcess(string name)
|
||||||
|
{
|
||||||
|
var targets = Process.GetProcessesByName(name);
|
||||||
|
if (targets.Length == 0)
|
||||||
|
{
|
||||||
|
// 확장자 없이 시도
|
||||||
|
var noExt = System.IO.Path.GetFileNameWithoutExtension(name);
|
||||||
|
targets = Process.GetProcessesByName(noExt);
|
||||||
|
}
|
||||||
|
if (targets.Length == 0) return false;
|
||||||
|
foreach (var p in targets)
|
||||||
|
{
|
||||||
|
try { p.Kill(); } catch { }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatBytes(long bytes)
|
||||||
|
{
|
||||||
|
if (bytes >= 1_073_741_824) return $"{bytes / 1_073_741_824.0:F1} GB";
|
||||||
|
if (bytes >= 1_048_576) return $"{bytes / 1_048_576.0:F0} MB";
|
||||||
|
if (bytes >= 1_024) return $"{bytes / 1_024.0:F0} KB";
|
||||||
|
return $"{bytes} B";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LauncherItem CopyItem(string label, string value) =>
|
||||||
|
new(label, value, null, ("copy", value), Symbol: "\uE9D9");
|
||||||
|
|
||||||
|
private static LauncherItem ErrorItem(string msg) =>
|
||||||
|
new(msg, "올바른 입력 형식을 확인하세요", null, null, Symbol: "\uE783");
|
||||||
|
}
|
||||||
227
src/AxCopilot/Handlers/XlHandler.cs
Normal file
227
src/AxCopilot/Handlers/XlHandler.cs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using AxCopilot.SDK;
|
||||||
|
using AxCopilot.Services;
|
||||||
|
|
||||||
|
namespace AxCopilot.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// L22-2: Excel 함수 레퍼런스 핸들러. "xl" 프리픽스로 사용합니다.
|
||||||
|
///
|
||||||
|
/// 예: xl → 카테고리 목록
|
||||||
|
/// xl lookup → 찾기·참조 함수 (VLOOKUP, INDEX, MATCH 등)
|
||||||
|
/// xl if → 논리 함수 (IF, IFS, IFERROR 등)
|
||||||
|
/// xl sum → 합산 함수 (SUM, SUMIF, SUMIFS 등)
|
||||||
|
/// xl count → 개수 함수 (COUNT, COUNTA, COUNTIF 등)
|
||||||
|
/// xl text → 텍스트 함수 (LEFT, RIGHT, MID, TRIM 등)
|
||||||
|
/// xl date → 날짜 함수 (TODAY, DATEDIF, EDATE 등)
|
||||||
|
/// xl math → 수학 함수 (ROUND, INT, MOD, ABS 등)
|
||||||
|
/// xl stat → 통계 함수 (AVERAGE, MAX, MIN, RANK 등)
|
||||||
|
/// xl <검색어> → 함수명·설명 검색
|
||||||
|
/// Enter → 함수 이름 복사.
|
||||||
|
/// </summary>
|
||||||
|
public class XlHandler : IActionHandler
|
||||||
|
{
|
||||||
|
public string? Prefix => "xl";
|
||||||
|
|
||||||
|
public PluginMetadata Metadata => new(
|
||||||
|
"Excel 함수",
|
||||||
|
"Excel 함수 레퍼런스 — 조회·논리·텍스트·날짜·수학·통계",
|
||||||
|
"1.0",
|
||||||
|
"AX");
|
||||||
|
|
||||||
|
private sealed record XlFunc(string Name, string Syntax, string Description, string Category);
|
||||||
|
|
||||||
|
private static readonly XlFunc[] Funcs =
|
||||||
|
[
|
||||||
|
// ── 조회·참조 (lookup) ──────────────────────────────────────────────
|
||||||
|
new("VLOOKUP", "VLOOKUP(값, 범위, 열번호, [일치유형])", "세로 방향 조회 — 열에서 값 검색 후 지정 열 반환", "lookup"),
|
||||||
|
new("HLOOKUP", "HLOOKUP(값, 범위, 행번호, [일치유형])", "가로 방향 조회 — 행에서 값 검색 후 지정 행 반환", "lookup"),
|
||||||
|
new("XLOOKUP", "XLOOKUP(값, 찾기범위, 반환범위, [없을때])", "유연한 조회 — 방향 무관, 정확/근사/와일드카드", "lookup"),
|
||||||
|
new("INDEX", "INDEX(범위, 행번호, [열번호])", "범위에서 특정 위치의 값 반환", "lookup"),
|
||||||
|
new("MATCH", "MATCH(값, 범위, [일치유형])", "범위에서 값의 상대 위치(순번) 반환", "lookup"),
|
||||||
|
new("OFFSET", "OFFSET(기준, 행이동, 열이동, [높이], [너비])","기준 셀에서 이동한 범위 반환", "lookup"),
|
||||||
|
new("INDIRECT", "INDIRECT(참조문자열, [A1형식])", "문자열로 표현된 참조를 실제 참조로 변환", "lookup"),
|
||||||
|
new("CHOOSE", "CHOOSE(인덱스, 값1, 값2, ...)", "인덱스 번호에 해당하는 값 반환", "lookup"),
|
||||||
|
new("ADDRESS", "ADDRESS(행번호, 열번호, [참조형식])", "셀 주소 문자열 생성 (예: \"$A$1\")", "lookup"),
|
||||||
|
|
||||||
|
// ── 논리 (if) ────────────────────────────────────────────────────────
|
||||||
|
new("IF", "IF(조건, 참값, 거짓값)", "조건이 참이면 참값, 거짓이면 거짓값 반환", "if"),
|
||||||
|
new("IFS", "IFS(조건1, 값1, 조건2, 값2, ...)", "여러 조건을 순서대로 검사하여 첫 번째 참 반환", "if"),
|
||||||
|
new("IFERROR", "IFERROR(식, 오류시값)", "식이 오류이면 오류시값, 아니면 식 결과 반환", "if"),
|
||||||
|
new("IFNA", "IFNA(식, NA시값)", "#N/A 오류 시 NA시값 반환", "if"),
|
||||||
|
new("AND", "AND(조건1, 조건2, ...)", "모든 조건이 참이면 TRUE", "if"),
|
||||||
|
new("OR", "OR(조건1, 조건2, ...)", "하나 이상 조건이 참이면 TRUE", "if"),
|
||||||
|
new("NOT", "NOT(조건)", "논리값 반전", "if"),
|
||||||
|
new("SWITCH", "SWITCH(식, 값1, 결과1, [값2, 결과2], ...)", "식을 여러 값과 비교하여 일치하는 결과 반환", "if"),
|
||||||
|
|
||||||
|
// ── 합산 (sum) ────────────────────────────────────────────────────────
|
||||||
|
new("SUM", "SUM(범위1, [범위2], ...)", "합계", "sum"),
|
||||||
|
new("SUMIF", "SUMIF(조건범위, 조건, [합산범위])", "조건에 맞는 셀의 합계", "sum"),
|
||||||
|
new("SUMIFS", "SUMIFS(합산범위, 조건범위1, 조건1, ...)", "여러 조건에 맞는 셀의 합계", "sum"),
|
||||||
|
new("SUMPRODUCT","SUMPRODUCT(배열1, [배열2], ...)", "배열 요소를 곱한 후 합계", "sum"),
|
||||||
|
new("SUBTOTAL", "SUBTOTAL(함수번호, 범위)", "필터링·숨긴 행 제외한 부분합", "sum"),
|
||||||
|
new("AGGREGATE", "AGGREGATE(함수번호, 옵션, 범위)", "오류·숨긴 행 무시하고 집계", "sum"),
|
||||||
|
|
||||||
|
// ── 개수 (count) ──────────────────────────────────────────────────────
|
||||||
|
new("COUNT", "COUNT(값1, [값2], ...)", "숫자가 있는 셀 개수", "count"),
|
||||||
|
new("COUNTA", "COUNTA(값1, [값2], ...)", "비어있지 않은 셀 개수", "count"),
|
||||||
|
new("COUNTBLANK","COUNTBLANK(범위)", "빈 셀 개수", "count"),
|
||||||
|
new("COUNTIF", "COUNTIF(범위, 조건)", "조건에 맞는 셀 개수", "count"),
|
||||||
|
new("COUNTIFS", "COUNTIFS(범위1, 조건1, [범위2, 조건2], ...)", "여러 조건에 맞는 셀 개수", "count"),
|
||||||
|
|
||||||
|
// ── 텍스트 (text) ──────────────────────────────────────────────────────
|
||||||
|
new("LEFT", "LEFT(텍스트, 문자수)", "왼쪽에서 N자 추출", "text"),
|
||||||
|
new("RIGHT", "RIGHT(텍스트, 문자수)", "오른쪽에서 N자 추출", "text"),
|
||||||
|
new("MID", "MID(텍스트, 시작, 문자수)", "중간 N자 추출", "text"),
|
||||||
|
new("LEN", "LEN(텍스트)", "문자 수 반환", "text"),
|
||||||
|
new("FIND", "FIND(찾기, 텍스트, [시작위치])", "대소문자 구분하여 위치 반환", "text"),
|
||||||
|
new("SEARCH", "SEARCH(찾기, 텍스트, [시작위치])", "대소문자 무시하여 위치 반환", "text"),
|
||||||
|
new("SUBSTITUTE","SUBSTITUTE(텍스트, 찾기, 바꾸기, [번째])", "특정 문자열을 다른 문자열로 치환", "text"),
|
||||||
|
new("REPLACE", "REPLACE(텍스트, 시작, 문자수, 새텍스트)", "위치 기반으로 문자열 교체", "text"),
|
||||||
|
new("TRIM", "TRIM(텍스트)", "앞뒤·중복 공백 제거", "text"),
|
||||||
|
new("CLEAN", "CLEAN(텍스트)", "인쇄 불가 문자 제거", "text"),
|
||||||
|
new("UPPER", "UPPER(텍스트)", "대문자로 변환", "text"),
|
||||||
|
new("LOWER", "LOWER(텍스트)", "소문자로 변환", "text"),
|
||||||
|
new("PROPER", "PROPER(텍스트)", "각 단어 첫 글자만 대문자", "text"),
|
||||||
|
new("CONCAT", "CONCAT(텍스트1, 텍스트2, ...)", "텍스트 연결 (CONCATENATE 최신 버전)", "text"),
|
||||||
|
new("TEXTJOIN", "TEXTJOIN(구분자, 빈셀무시, 텍스트1, ...)", "구분자를 포함하여 텍스트 연결", "text"),
|
||||||
|
new("TEXT", "TEXT(값, 서식)", "숫자를 서식 문자열로 변환 (예: \"#,##0\")", "text"),
|
||||||
|
new("VALUE", "VALUE(텍스트)", "텍스트를 숫자로 변환", "text"),
|
||||||
|
new("NUMBERVALUE","NUMBERVALUE(텍스트, 소수점, 그룹구분)", "로캘 독립적 텍스트→숫자 변환", "text"),
|
||||||
|
|
||||||
|
// ── 날짜 (date) ──────────────────────────────────────────────────────
|
||||||
|
new("TODAY", "TODAY()", "오늘 날짜 반환", "date"),
|
||||||
|
new("NOW", "NOW()", "현재 날짜와 시간 반환", "date"),
|
||||||
|
new("DATE", "DATE(년, 월, 일)", "날짜 값 생성", "date"),
|
||||||
|
new("DATEDIF", "DATEDIF(시작일, 종료일, 단위)", "두 날짜 간 차이 — 단위: Y M D YM YD MD", "date"),
|
||||||
|
new("EDATE", "EDATE(시작일, 개월수)", "N개월 후/전 날짜", "date"),
|
||||||
|
new("EOMONTH", "EOMONTH(시작일, 개월수)", "N개월 후 월 말일", "date"),
|
||||||
|
new("WORKDAY", "WORKDAY(시작일, 일수, [공휴일])", "영업일 N일 후 날짜", "date"),
|
||||||
|
new("NETWORKDAYS","NETWORKDAYS(시작일, 종료일, [공휴일])", "두 날짜 사이 영업일 수", "date"),
|
||||||
|
new("YEAR", "YEAR(날짜)", "연도 추출", "date"),
|
||||||
|
new("MONTH", "MONTH(날짜)", "월 추출", "date"),
|
||||||
|
new("DAY", "DAY(날짜)", "일 추출", "date"),
|
||||||
|
new("WEEKDAY", "WEEKDAY(날짜, [반환형식])", "요일 번호 반환 (1=일요일)", "date"),
|
||||||
|
new("WEEKNUM", "WEEKNUM(날짜, [반환형식])", "해당 날짜의 주 번호", "date"),
|
||||||
|
|
||||||
|
// ── 수학 (math) ──────────────────────────────────────────────────────
|
||||||
|
new("ROUND", "ROUND(숫자, 자릿수)", "반올림", "math"),
|
||||||
|
new("ROUNDUP", "ROUNDUP(숫자, 자릿수)", "올림", "math"),
|
||||||
|
new("ROUNDDOWN", "ROUNDDOWN(숫자, 자릿수)", "내림", "math"),
|
||||||
|
new("INT", "INT(숫자)", "정수 부분만 반환 (내림)", "math"),
|
||||||
|
new("MOD", "MOD(숫자, 제수)", "나머지 반환", "math"),
|
||||||
|
new("ABS", "ABS(숫자)", "절대값", "math"),
|
||||||
|
new("POWER", "POWER(숫자, 지수)", "거듭제곱", "math"),
|
||||||
|
new("SQRT", "SQRT(숫자)", "제곱근", "math"),
|
||||||
|
new("CEILING", "CEILING(숫자, 기준)", "기준의 배수로 올림", "math"),
|
||||||
|
new("FLOOR", "FLOOR(숫자, 기준)", "기준의 배수로 내림", "math"),
|
||||||
|
new("RAND", "RAND()", "0~1 사이 난수", "math"),
|
||||||
|
new("RANDBETWEEN","RANDBETWEEN(최소, 최대)", "정수 난수", "math"),
|
||||||
|
new("LARGE", "LARGE(범위, 순위)", "N번째로 큰 값", "math"),
|
||||||
|
new("SMALL", "SMALL(범위, 순위)", "N번째로 작은 값", "math"),
|
||||||
|
|
||||||
|
// ── 통계 (stat) ──────────────────────────────────────────────────────
|
||||||
|
new("AVERAGE", "AVERAGE(값1, [값2], ...)", "평균", "stat"),
|
||||||
|
new("AVERAGEIF", "AVERAGEIF(범위, 조건, [평균범위])", "조건에 맞는 셀의 평균", "stat"),
|
||||||
|
new("AVERAGEIFS","AVERAGEIFS(평균범위, 범위1, 조건1, ...)", "여러 조건에 맞는 셀의 평균", "stat"),
|
||||||
|
new("MAX", "MAX(값1, [값2], ...)", "최대값", "stat"),
|
||||||
|
new("MIN", "MIN(값1, [값2], ...)", "최소값", "stat"),
|
||||||
|
new("MAXIFS", "MAXIFS(최대범위, 조건범위1, 조건1, ...)", "조건에 맞는 최대값", "stat"),
|
||||||
|
new("MINIFS", "MINIFS(최소범위, 조건범위1, 조건1, ...)", "조건에 맞는 최소값", "stat"),
|
||||||
|
new("MEDIAN", "MEDIAN(값1, [값2], ...)", "중앙값", "stat"),
|
||||||
|
new("MODE", "MODE(값1, [값2], ...)", "최빈값", "stat"),
|
||||||
|
new("RANK", "RANK(숫자, 범위, [정렬])", "순위 반환 (0=내림차순)", "stat"),
|
||||||
|
new("STDEV", "STDEV(값1, [값2], ...)", "표준편차 (샘플)", "stat"),
|
||||||
|
new("VAR", "VAR(값1, [값2], ...)", "분산 (샘플)", "stat"),
|
||||||
|
new("CORREL", "CORREL(배열1, 배열2)", "두 배열의 상관계수", "stat"),
|
||||||
|
new("PERCENTILE","PERCENTILE(배열, k)", "백분위수 (k: 0~1)", "stat"),
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly (string Key, string[] Aliases, string Label)[] Categories =
|
||||||
|
[
|
||||||
|
("lookup", ["lookup", "find", "조회", "참조", "찾기"], "조회·참조"),
|
||||||
|
("if", ["if", "logic", "논리", "조건"], "논리·조건"),
|
||||||
|
("sum", ["sum", "합계", "sumif", "더하기"], "합산"),
|
||||||
|
("count", ["count", "개수", "countif"], "개수"),
|
||||||
|
("text", ["text", "텍스트", "문자", "string"], "텍스트"),
|
||||||
|
("date", ["date", "날짜", "time", "시간"], "날짜·시간"),
|
||||||
|
("math", ["math", "수학", "round", "수식"], "수학"),
|
||||||
|
("stat", ["stat", "통계", "average", "max"], "통계"),
|
||||||
|
];
|
||||||
|
|
||||||
|
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("Excel 함수 레퍼런스",
|
||||||
|
"카테고리: lookup · if · sum · count · text · date · math · stat",
|
||||||
|
null, null, Symbol: "\uE9D2"));
|
||||||
|
|
||||||
|
foreach (var (key, _, label) in Categories)
|
||||||
|
{
|
||||||
|
var cnt = Funcs.Count(f => f.Category == key);
|
||||||
|
items.Add(new LauncherItem($"xl {key}", $"{label} ({cnt}개)", null, ("copy", $"xl {key}"), Symbol: "\uE9D2"));
|
||||||
|
}
|
||||||
|
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 = Funcs.Where(f => f.Category == cat.Key).ToList();
|
||||||
|
items.Add(new LauncherItem($"{cat.Label} 함수 {list.Count}개",
|
||||||
|
"Enter: 함수명 복사", null, null, Symbol: "\uE9D2"));
|
||||||
|
foreach (var f in list)
|
||||||
|
items.Add(FuncItem(f));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 검색어 매칭
|
||||||
|
var search = Funcs
|
||||||
|
.Where(f => f.Name.Contains(kw, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| f.Description.Contains(kw, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| f.Syntax.Contains(kw, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (search.Count == 0)
|
||||||
|
{
|
||||||
|
items.Add(new LauncherItem($"'{q}' 함수를 찾을 수 없습니다",
|
||||||
|
"카테고리: lookup · if · sum · count · text · date · math · stat",
|
||||||
|
null, null, Symbol: "\uE783"));
|
||||||
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.Add(new LauncherItem($"'{q}' 검색 결과 {search.Count}개",
|
||||||
|
"Enter: 함수명 복사", null, null, Symbol: "\uE9D2"));
|
||||||
|
foreach (var f in search)
|
||||||
|
items.Add(FuncItem(f));
|
||||||
|
|
||||||
|
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("Excel 함수", "클립보드에 복사했습니다.");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LauncherItem FuncItem(XlFunc f) =>
|
||||||
|
new(f.Name, $"{f.Description} | {f.Syntax}",
|
||||||
|
null, ("copy", f.Name), Symbol: "\uE9D2");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user