AX Agent ?? ?? ??? MCP ?? ??? ???? ??? ??? ??
- MCP ?? ?????? synthetic skill? ???? McpSkillCatalog? ???? ToolRegistry ?? snapshot ?? ??? ??? - managed/user/additional/project/plugin/mcp/legacy ?? source ??, plugin-only ??, source? inline shell trust boundary? SkillService/AppSettings/Settings UI? ??? - SlashCommandCatalog? ChatWindow?? builtin command? skill? ???? ???? ??? ?? ? ????? dedupe?? MCP ???? ? synthetic skill ?? ??? SkillGallery/AgentSettings? ??? - README.md? docs/DEVELOPMENT.md? 2026-04-14 19:13 (KST) ?? ?? ??? ?? ??? ??? - ??: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_phase4\\ -p:IntermediateOutputPath=obj\\verify_phase4\\ (?? 0, ?? 0) - ??: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "SkillServiceRuntimePolicyTests|SlashCommandCatalogTests|McpSkillCatalogTests" -p:OutputPath=bin\\verify_phase4_tests\\ -p:IntermediateOutputPath=obj\\verify_phase4_tests\\ (?? 17, ?? WorkspaceContextGeneratorTests.cs nullable ?? 1? ??)
This commit is contained in:
@@ -147,6 +147,7 @@ public partial class SkillGalleryWindow : Window
|
||||
"기본 제공" => skills.Where(IsBuiltInSkill).ToList(),
|
||||
"프로젝트" => skills.Where(s => string.Equals(s.SourceScope, "project", StringComparison.OrdinalIgnoreCase)).ToList(),
|
||||
"플러그인" => skills.Where(s => string.Equals(s.SourceScope, "plugin", StringComparison.OrdinalIgnoreCase)).ToList(),
|
||||
"MCP" => skills.Where(s => string.Equals(s.SourceScope, "mcp", StringComparison.OrdinalIgnoreCase)).ToList(),
|
||||
"고급 (런타임)" => skills.Where(s => !string.IsNullOrEmpty(s.Requires)).ToList(),
|
||||
"사용자" => skills.Where(IsUserOwnedSkill).ToList(),
|
||||
_ => skills.ToList(),
|
||||
@@ -164,6 +165,8 @@ public partial class SkillGalleryWindow : Window
|
||||
var isBuiltIn = IsBuiltInSkill(skill);
|
||||
var isProject = string.Equals(skill.SourceScope, "project", StringComparison.OrdinalIgnoreCase);
|
||||
var isPlugin = string.Equals(skill.SourceScope, "plugin", StringComparison.OrdinalIgnoreCase);
|
||||
var isMcp = string.Equals(skill.SourceScope, "mcp", StringComparison.OrdinalIgnoreCase);
|
||||
var hasBackingFile = !string.IsNullOrWhiteSpace(skill.FilePath) && File.Exists(skill.FilePath);
|
||||
|
||||
var card = new Border
|
||||
{
|
||||
@@ -231,6 +234,8 @@ public partial class SkillGalleryWindow : Window
|
||||
nameRow.Children.Add(MakeBadge("프로젝트", "#2563EB"));
|
||||
else if (isPlugin)
|
||||
nameRow.Children.Add(MakeBadge("플러그인", "#EC4899"));
|
||||
else if (isMcp)
|
||||
nameRow.Children.Add(MakeBadge("MCP", "#14B8A6"));
|
||||
else if (isUser)
|
||||
nameRow.Children.Add(MakeBadge("사용자", "#34D399"));
|
||||
else if (isAdvanced)
|
||||
@@ -266,62 +271,68 @@ public partial class SkillGalleryWindow : Window
|
||||
};
|
||||
|
||||
// 편집
|
||||
actions.Children.Add(MakeActionBtn("\uE70F", "#3B82F6", isUser ? "편집 (시각적 편집기)" : "편집 (파일 열기)",
|
||||
() =>
|
||||
{
|
||||
if (isUser)
|
||||
if (isUser || hasBackingFile)
|
||||
{
|
||||
actions.Children.Add(MakeActionBtn("\uE70F", "#3B82F6", isUser ? "편집 (시각적 편집기)" : "편집 (파일 열기)",
|
||||
() =>
|
||||
{
|
||||
var editor = new SkillEditorWindow(skill) { Owner = this };
|
||||
if (editor.ShowDialog() == true)
|
||||
if (isUser)
|
||||
{
|
||||
var editor = new SkillEditorWindow(skill) { Owner = this };
|
||||
if (editor.ShowDialog() == true)
|
||||
{
|
||||
SkillService.ReloadFromCurrentSettings();
|
||||
BuildCategoryFilter();
|
||||
RenderSkills();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(skill.FilePath) { UseShellExecute = true }); }
|
||||
catch (Exception ex) { CustomMessageBox.Show($"파일을 열 수 없습니다: {ex.Message}", "편집"); }
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 복제 (파일 기반 스킬만)
|
||||
if (hasBackingFile)
|
||||
{
|
||||
actions.Children.Add(MakeActionBtn("\uE8C8", "#10B981", "복제",
|
||||
() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var destFolder = Path.Combine(userFolder);
|
||||
if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder);
|
||||
|
||||
var srcName = Path.GetFileNameWithoutExtension(skill.FilePath);
|
||||
var destPath = Path.Combine(destFolder, $"{srcName}_copy.skill.md");
|
||||
var counter = 2;
|
||||
while (File.Exists(destPath))
|
||||
destPath = Path.Combine(destFolder, $"{srcName}_copy{counter++}.skill.md");
|
||||
|
||||
File.Copy(skill.FilePath, destPath);
|
||||
SkillService.ReloadFromCurrentSettings();
|
||||
BuildCategoryFilter();
|
||||
RenderSkills();
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex) { CustomMessageBox.Show($"복제 실패: {ex.Message}", "복제"); }
|
||||
}));
|
||||
|
||||
// 내보내기
|
||||
actions.Children.Add(MakeActionBtn("\uEDE1", "#F59E0B", "내보내기 (.zip)",
|
||||
() =>
|
||||
{
|
||||
try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(skill.FilePath) { UseShellExecute = true }); }
|
||||
catch (Exception ex) { CustomMessageBox.Show($"파일을 열 수 없습니다: {ex.Message}", "편집"); }
|
||||
}
|
||||
}));
|
||||
|
||||
// 복제 (사용자 스킬/폴더 스킬만)
|
||||
actions.Children.Add(MakeActionBtn("\uE8C8", "#10B981", "복제",
|
||||
() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var destFolder = Path.Combine(userFolder);
|
||||
if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder);
|
||||
|
||||
var srcName = Path.GetFileNameWithoutExtension(skill.FilePath);
|
||||
var destPath = Path.Combine(destFolder, $"{srcName}_copy.skill.md");
|
||||
var counter = 2;
|
||||
while (File.Exists(destPath))
|
||||
destPath = Path.Combine(destFolder, $"{srcName}_copy{counter++}.skill.md");
|
||||
|
||||
File.Copy(skill.FilePath, destPath);
|
||||
SkillService.ReloadFromCurrentSettings();
|
||||
BuildCategoryFilter();
|
||||
RenderSkills();
|
||||
}
|
||||
catch (Exception ex) { CustomMessageBox.Show($"복제 실패: {ex.Message}", "복제"); }
|
||||
}));
|
||||
|
||||
// 내보내기
|
||||
actions.Children.Add(MakeActionBtn("\uEDE1", "#F59E0B", "내보내기 (.zip)",
|
||||
() =>
|
||||
{
|
||||
var folderDlg = new System.Windows.Forms.FolderBrowserDialog
|
||||
{ Description = "내보낼 폴더를 선택하세요" };
|
||||
if (folderDlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
|
||||
var result = SkillService.ExportSkill(skill, folderDlg.SelectedPath);
|
||||
if (result != null)
|
||||
CustomMessageBox.Show($"내보내기 완료:\n{result}", "내보내기");
|
||||
else
|
||||
CustomMessageBox.Show("내보내기에 실패했습니다.", "내보내기");
|
||||
}));
|
||||
var folderDlg = new System.Windows.Forms.FolderBrowserDialog
|
||||
{ Description = "내보낼 폴더를 선택하세요" };
|
||||
if (folderDlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
|
||||
var result = SkillService.ExportSkill(skill, folderDlg.SelectedPath);
|
||||
if (result != null)
|
||||
CustomMessageBox.Show($"내보내기 완료:\n{result}", "내보내기");
|
||||
else
|
||||
CustomMessageBox.Show("내보내기에 실패했습니다.", "내보내기");
|
||||
}));
|
||||
}
|
||||
|
||||
// 삭제 (사용자 스킬만)
|
||||
if (isUser)
|
||||
@@ -405,6 +416,8 @@ public partial class SkillGalleryWindow : Window
|
||||
yield return "프로젝트";
|
||||
if (skills.Any(s => string.Equals(s.SourceScope, "plugin", StringComparison.OrdinalIgnoreCase)))
|
||||
yield return "플러그인";
|
||||
if (skills.Any(s => string.Equals(s.SourceScope, "mcp", StringComparison.OrdinalIgnoreCase)))
|
||||
yield return "MCP";
|
||||
if (skills.Any(s => !string.IsNullOrEmpty(s.Requires)))
|
||||
yield return "고급 (런타임)";
|
||||
if (skills.Any(IsUserOwnedSkill))
|
||||
@@ -422,7 +435,8 @@ public partial class SkillGalleryWindow : Window
|
||||
string.Equals(skill.SourceScope, "legacy", StringComparison.OrdinalIgnoreCase) ||
|
||||
(!IsBuiltInSkill(skill)
|
||||
&& !string.Equals(skill.SourceScope, "project", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(skill.SourceScope, "plugin", StringComparison.OrdinalIgnoreCase));
|
||||
&& !string.Equals(skill.SourceScope, "plugin", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(skill.SourceScope, "mcp", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private Border MakeActionBtn(string icon, string colorHex, string tooltip, Action action)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user