using System.Diagnostics;
using System.IO;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
namespace AxCopilot.Handlers;
///
/// L19-4: npm/yarn/pnpm 패키지 매니저 명령어 생성·실행 핸들러. "npm" 프리픽스로 사용합니다.
///
/// 예: npm → 자주 쓰는 명령어 목록
/// npm init → npm/yarn/pnpm init 명령 비교
/// npm install lodash → npm/yarn/pnpm install lodash 명령 비교
/// npm i lodash → install 단축키
/// npm run dev → npm/yarn/pnpm run dev
/// npm uninstall lodash → npm/yarn/pnpm uninstall
/// npm update → 패키지 업데이트 명령
/// npm list → 패키지 목록 명령
/// npm audit → 보안 감사 명령
/// npm publish → 배포 명령
/// npm scripts → package.json scripts 확인 방법
/// npm global → 전역 패키지 목록 명령
/// npm clean → 캐시·node_modules 정리 명령
/// Enter → 클립보드 복사 (또는 터미널에서 실행).
///
public class NpmHandler : IActionHandler
{
public string? Prefix => "npm";
public PluginMetadata Metadata => new(
"NPM",
"npm/yarn/pnpm 명령어 생성기 — install·run·audit·publish·clean",
"1.0",
"AX");
private record PmCmd(string Manager, string Cmd, string Description);
public Task> GetItemsAsync(string query, CancellationToken ct)
{
var q = query.Trim();
var items = new List();
if (string.IsNullOrWhiteSpace(q))
{
items.Add(new LauncherItem("npm/yarn/pnpm 명령어 생성기",
"npm install / run / init / build / test / audit / publish / clean …",
null, null, Symbol: "\uE756"));
AddCommandGroups(items);
return Task.FromResult>(items);
}
var parts = q.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
var sub = parts[0].ToLowerInvariant();
var arg = parts.Length > 1 ? parts[1].Trim() : "";
// 단축키 정규화
sub = sub switch
{
"i" => "install",
"un" => "uninstall",
"rm" => "uninstall",
"up" => "update",
"ls" => "list",
_ => sub
};
switch (sub)
{
case "init":
AddSection(items, "프로젝트 초기화", [
new("npm", "npm init -y", "기본값으로 package.json 생성"),
new("yarn", "yarn init -y", "기본값으로 package.json 생성"),
new("pnpm", "pnpm init", "기본값으로 package.json 생성"),
]);
break;
case "install" when !string.IsNullOrWhiteSpace(arg):
AddSection(items, $"패키지 설치: {arg}", [
new("npm", $"npm install {arg}", $"npm으로 {arg} 설치"),
new("yarn", $"yarn add {arg}", $"yarn으로 {arg} 설치"),
new("pnpm", $"pnpm add {arg}", $"pnpm으로 {arg} 설치"),
]);
AddSection(items, $"개발 의존성으로 설치: {arg}", [
new("npm", $"npm install {arg} --save-dev", "devDependencies에 추가"),
new("yarn", $"yarn add {arg} --dev", "devDependencies에 추가"),
new("pnpm", $"pnpm add {arg} --save-dev", "devDependencies에 추가"),
]);
AddSection(items, $"전역 설치: {arg}", [
new("npm", $"npm install -g {arg}", "전역 설치"),
new("yarn", $"yarn global add {arg}", "전역 설치"),
new("pnpm", $"pnpm add -g {arg}", "전역 설치"),
]);
break;
case "install":
AddSection(items, "의존성 설치 (node_modules)", [
new("npm", "npm install", "package.json 의존성 모두 설치"),
new("yarn", "yarn install", "package.json 의존성 모두 설치"),
new("pnpm", "pnpm install", "package.json 의존성 모두 설치"),
]);
AddSection(items, "프로덕션 의존성만", [
new("npm", "npm install --production", "--production 플래그"),
new("yarn", "yarn install --production", "--production 플래그"),
new("pnpm", "pnpm install --prod", "--prod 플래그"),
]);
break;
case "uninstall" when !string.IsNullOrWhiteSpace(arg):
AddSection(items, $"패키지 제거: {arg}", [
new("npm", $"npm uninstall {arg}", $"npm으로 {arg} 제거"),
new("yarn", $"yarn remove {arg}", $"yarn으로 {arg} 제거"),
new("pnpm", $"pnpm remove {arg}", $"pnpm으로 {arg} 제거"),
]);
break;
case "run" when !string.IsNullOrWhiteSpace(arg):
AddSection(items, $"스크립트 실행: {arg}", [
new("npm", $"npm run {arg}", $"npm run {arg}"),
new("yarn", $"yarn {arg}", $"yarn {arg}"),
new("pnpm", $"pnpm run {arg}", $"pnpm run {arg}"),
]);
break;
case "start":
case "run":
AddSection(items, "주요 스크립트", [
new("npm", "npm start", "start 스크립트 실행"),
new("npm", "npm run dev", "dev 스크립트 실행"),
new("npm", "npm run build", "build 스크립트 실행"),
new("npm", "npm test", "test 스크립트 실행"),
]);
AddSection(items, "yarn 동등 명령", [
new("yarn", "yarn start", "start"),
new("yarn", "yarn dev", "dev"),
new("yarn", "yarn build", "build"),
new("yarn", "yarn test", "test"),
]);
AddSection(items, "pnpm 동등 명령", [
new("pnpm", "pnpm start", "start"),
new("pnpm", "pnpm run dev", "dev"),
new("pnpm", "pnpm run build", "build"),
new("pnpm", "pnpm test", "test"),
]);
break;
case "build":
AddSection(items, "빌드", [
new("npm", "npm run build", "build 스크립트"),
new("yarn", "yarn build", "build 스크립트"),
new("pnpm", "pnpm run build", "build 스크립트"),
]);
break;
case "test":
AddSection(items, "테스트", [
new("npm", "npm test", "test 스크립트"),
new("npm", "npm run test:watch", "테스트 와처"),
new("yarn", "yarn test", "test 스크립트"),
new("pnpm", "pnpm test", "test 스크립트"),
]);
break;
case "update" or "upgrade":
AddSection(items, "패키지 업데이트", [
new("npm", "npm update", "모든 패키지 업데이트"),
new("npm", "npm outdated", "오래된 패키지 확인"),
new("npm", "npx npm-check-updates", "버전 업그레이드 체크"),
new("yarn", "yarn upgrade", "모든 패키지 업그레이드"),
new("pnpm", "pnpm update", "모든 패키지 업데이트"),
]);
break;
case "list" or "ls":
AddSection(items, "패키지 목록", [
new("npm", "npm list", "설치된 패키지 목록"),
new("npm", "npm list --depth=0", "최상위 패키지만"),
new("npm", "npm list -g --depth=0", "전역 패키지 목록"),
new("yarn", "yarn list", "설치된 패키지 목록"),
new("pnpm", "pnpm list", "설치된 패키지 목록"),
]);
break;
case "audit":
AddSection(items, "보안 감사", [
new("npm", "npm audit", "보안 취약점 스캔"),
new("npm", "npm audit fix", "자동 수정"),
new("yarn", "yarn audit", "보안 취약점 스캔"),
new("pnpm", "pnpm audit", "보안 취약점 스캔"),
]);
break;
case "publish":
AddSection(items, "패키지 배포", [
new("npm", "npm publish", "npm 레지스트리에 배포"),
new("npm", "npm publish --access public","공개 패키지로 배포"),
new("npm", "npm version patch", "패치 버전 올리기"),
new("npm", "npm version minor", "마이너 버전 올리기"),
new("npm", "npm version major", "메이저 버전 올리기"),
new("yarn", "yarn publish", "yarn으로 배포"),
new("pnpm", "pnpm publish", "pnpm으로 배포"),
]);
break;
case "scripts":
AddSection(items, "package.json 스크립트 확인", [
new("npm", "npm run", "스크립트 목록 보기"),
new("npm", "cat package.json", "package.json 확인"),
new("yarn", "yarn run", "스크립트 목록 보기"),
new("pnpm", "pnpm run", "스크립트 목록 보기"),
]);
break;
case "global":
AddSection(items, "전역 패키지", [
new("npm", "npm list -g --depth=0", "전역 패키지 목록"),
new("npm", "npm root -g", "전역 node_modules 경로"),
new("yarn", "yarn global list", "전역 패키지 목록"),
new("pnpm", "pnpm list -g", "전역 패키지 목록"),
]);
break;
case "clean":
AddSection(items, "캐시 및 모듈 정리", [
new("npm", "npm cache clean --force", "npm 캐시 삭제"),
new("npm", "Remove-Item -Recurse -Force node_modules", "node_modules 삭제 (PowerShell)"),
new("npm", "rm -rf node_modules && npm install", "재설치 (bash)"),
new("yarn", "yarn cache clean", "yarn 캐시 삭제"),
new("pnpm", "pnpm store prune", "pnpm 스토어 정리"),
]);
break;
case "ci":
AddSection(items, "CI/CD용 클린 설치", [
new("npm", "npm ci", "package-lock.json 기반 클린 설치"),
new("yarn", "yarn install --frozen-lockfile", "lockfile 기반 클린 설치"),
new("pnpm", "pnpm install --frozen-lockfile", "lockfile 기반 클린 설치"),
]);
break;
case "lock":
AddSection(items, "lockfile 관리", [
new("npm", "npm install --package-lock-only", "lockfile만 갱신"),
new("yarn", "yarn import", "package-lock → yarn.lock 변환"),
new("pnpm", "pnpm import", "다른 lockfile → pnpm-lock.yaml 변환"),
]);
break;
default:
items.Add(new LauncherItem($"알 수 없는 명령: '{sub}'",
"init · install · uninstall · run · build · test · update · list · audit · publish · clean · global",
null, null, Symbol: "\uE783"));
AddCommandGroups(items);
break;
}
return Task.FromResult>(items);
}
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
switch (item.Data)
{
case ("copy", string text):
try
{
System.Windows.Application.Current.Dispatcher.Invoke(
() => Clipboard.SetText(text));
NotificationService.Notify("NPM", "클립보드에 복사했습니다.");
}
catch { }
break;
case ("run", string cmd):
RunInTerminal(cmd);
break;
}
return Task.CompletedTask;
}
// ── 헬퍼 ────────────────────────────────────────────────────────────────
private static void AddCommandGroups(List items)
{
var groups = new (string Title, string[] Keys)[]
{
("초기화·설치", ["npm init", "npm install", "npm install <패키지>"]),
("개발 워크플로우", ["npm start", "npm run dev", "npm run build", "npm test"]),
("패키지 관리", ["npm update", "npm uninstall <패키지>", "npm list"]),
("보안·배포", ["npm audit", "npm audit fix", "npm publish"]),
("캐시·정리", ["npm cache clean --force", "npm ci"]),
};
foreach (var (title, keys) in groups)
items.Add(new LauncherItem(title, string.Join(" / ", keys), null, null, Symbol: "\uE756"));
}
private static void AddSection(List items, string title, PmCmd[] cmds)
{
items.Add(new LauncherItem($"── {title} ──", "", null, null, Symbol: "\uE756"));
foreach (var c in cmds)
{
var icon = c.Manager switch
{
"yarn" => "🧶",
"pnpm" => "📦",
_ => "📦"
};
items.Add(new LauncherItem(c.Cmd, $"{c.Manager} · {c.Description} · Enter 복사",
null, ("copy", c.Cmd), Symbol: "\uE756"));
}
}
private static void RunInTerminal(string cmd)
{
try
{
// Windows Terminal 우선
var wtPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@"Microsoft\WindowsApps\wt.exe");
if (File.Exists(wtPath))
{
Process.Start(new ProcessStartInfo
{
FileName = wtPath,
Arguments = $"cmd /k \"{cmd}\"",
UseShellExecute = true
});
return;
}
// cmd 폴백
Process.Start(new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/k {cmd}",
UseShellExecute = true
});
}
catch { }
}
}