빌드 부산물 추적 해제와 AX Agent 대기열·composer UI 정리
Some checks failed
Release Gate / gate (push) Has been cancelled

- .gitignore에 bin/obj/publish 및 IDE/OS/비밀정보 패턴 추가
- Git 인덱스에서 publish 및 src 하위 bin/obj 빌드 부산물 추적을 해제하여 저장소 노이즈를 정리
- DraftQueue를 실행 대기/최근 결과 섹션과 상태 요약 pill 구조로 재정리
- composer 상단 모델/컨텍스트/프리셋 줄과 하단 작업 위치 칩 UI를 더 평평한 시각 언어로 통일
- 워크스페이스·브랜치·워크트리 패널에 공통 row 및 요약 strip을 적용해 panel UX를 정돈
- README.md와 docs/DEVELOPMENT.md, docs/AGENT_ROADMAP.md, AGENTS.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-04 23:03:42 +09:00
parent a027ea4f9a
commit 72a8c0d541
908 changed files with 5661 additions and 77215 deletions

View File

@@ -536,7 +536,11 @@ public partial class App : System.Windows.Application
}
/// <summary>ChatWindow 등 외부에서 설정 창을 여는 공개 메서드.</summary>
public void OpenSettingsFromChat() => Dispatcher.Invoke(OpenSettings);
public void OpenSettingsFromChat() => Dispatcher.Invoke(() =>
{
OpenSettings();
_settingsWindow?.SelectAgentSettingsTab();
});
/// <summary>AX Agent 창을 열고 전용 설정창을 바로 표시합니다.</summary>
public void OpenAgentSettingsInChat()

View File

@@ -577,6 +577,12 @@ public class LlmSettings
[JsonPropertyName("promptTemplates")]
public List<PromptTemplate> PromptTemplates { get; set; } = new();
[JsonPropertyName("favoritePromptTemplates")]
public List<string> FavoritePromptTemplates { get; set; } = new();
[JsonPropertyName("recentPromptTemplates")]
public List<string> RecentPromptTemplates { get; set; } = new();
/// <summary>작업 폴더 경로. 빈 문자열이면 미선택.</summary>
[JsonPropertyName("workFolder")]
public string WorkFolder { get; set; } = "";
@@ -596,7 +602,7 @@ public class LlmSettings
/// <summary>
/// 파일 접근 권한 수준.
/// Default = 매번 확인 | AcceptEdits = 파일 편집 자동 허용 | Plan = 계획/승인 중심
/// BypassPermissions = 모든 확인 생략 | DontAsk = 권한 질문 없이 진행 | Deny = 읽기 전용
/// BypassPermissions = 모든 확인 생략 | Deny = 읽기 전용
/// </summary>
[JsonPropertyName("filePermission")]
public string FilePermission { get; set; } = "Deny";

View File

@@ -173,6 +173,9 @@ public class DraftQueueItem
[JsonPropertyName("priority")]
public string Priority { get; set; } = "next";
[JsonPropertyName("kind")]
public string Kind { get; set; } = "message";
[JsonPropertyName("state")]
public string State { get; set; } = "queued";

View File

@@ -17,12 +17,10 @@ public static class PermissionModeCatalog
public static readonly IReadOnlyList<string> UserSelectableModes = new[]
{
Deny,
Default,
AcceptEdits,
Plan,
BypassPermissions,
DontAsk,
};
/// <summary>
@@ -56,11 +54,11 @@ public static class PermissionModeCatalog
"fullauto" => BypassPermissions,
"완전자동" => BypassPermissions,
"완전 자동" => BypassPermissions,
"dontask" => DontAsk,
"don't ask" => DontAsk,
"silent" => DontAsk,
"질문없이진행" => DontAsk,
"질문 없이 진행" => DontAsk,
"dontask" => BypassPermissions,
"don't ask" => BypassPermissions,
"silent" => BypassPermissions,
"질문없이진행" => BypassPermissions,
"질문 없이 진행" => BypassPermissions,
"allow" => AcceptEdits,
"none" => Deny,
"disabled" => Deny,
@@ -115,7 +113,6 @@ public static class PermissionModeCatalog
var normalized = NormalizeGlobalMode(mode);
return !IsAcceptEdits(normalized)
&& !IsBypassPermissions(normalized)
&& !IsDontAsk(normalized)
&& !string.Equals(normalized, Deny, StringComparison.OrdinalIgnoreCase);
}
@@ -128,7 +125,6 @@ public static class PermissionModeCatalog
AcceptEdits => "편집 자동 승인",
Plan => "계획 모드",
BypassPermissions => "권한 건너뛰기",
DontAsk => "질문 없이 진행",
Deny => "읽기 전용",
_ => normalized,
};

View File

@@ -11,12 +11,6 @@ internal static class PermissionModePresentationCatalog
{
public static readonly IReadOnlyList<PermissionModePresentation> Ordered = new[]
{
new PermissionModePresentation(
PermissionModeCatalog.Deny,
"\uE711",
"읽기 전용",
"파일 읽기만 허용하고 생성/수정/삭제는 차단합니다.",
"#107C10"),
new PermissionModePresentation(
PermissionModeCatalog.Default,
"\uE8D7",
@@ -39,14 +33,8 @@ internal static class PermissionModePresentationCatalog
PermissionModeCatalog.BypassPermissions,
"\uE814",
"권한 건너뛰기",
"모든 권한을 허용합니다.",
"파일 편집과 명령 실행까지 모두 자동 허용합니다.",
"#B45309"),
new PermissionModePresentation(
PermissionModeCatalog.DontAsk,
"\uE8A5",
"질문 없이 진행",
"권한 질문 없이 진행합니다. 자동 실행 범위를 점검하세요.",
"#B91C1C"),
};
public static PermissionModePresentation Resolve(string? mode)
@@ -54,6 +42,6 @@ internal static class PermissionModePresentationCatalog
var normalized = PermissionModeCatalog.NormalizeGlobalMode(mode);
return Ordered.FirstOrDefault(item =>
string.Equals(item.Mode, normalized, StringComparison.OrdinalIgnoreCase))
?? Ordered[1];
?? Ordered[0];
}
}

View File

@@ -506,7 +506,7 @@ public sealed class AppStateService
if (string.IsNullOrWhiteSpace(conversation?.Permission))
effective = defaultMode;
var risk = PermissionModeCatalog.IsBypassPermissions(effective) || PermissionModeCatalog.IsDontAsk(effective)
var risk = PermissionModeCatalog.IsBypassPermissions(effective)
? "critical"
: PermissionModeCatalog.IsAcceptEdits(effective)
? "high"
@@ -522,7 +522,6 @@ public sealed class AppStateService
"Deny" => "파일 읽기만 허용하고 생성/수정/삭제는 차단합니다.",
"Plan" => "계획/승인 흐름을 우선 적용한 뒤 파일 작업을 진행합니다.",
"BypassPermissions" => "모든 권한 확인을 생략합니다. 주의해서 사용해야 합니다.",
"DontAsk" => "권한 질문 없이 진행합니다. 자동 실행 범위를 반드시 점검해야 합니다.",
_ => "파일 작업 전마다 사용자 확인을 요청합니다.",
};

View File

@@ -438,7 +438,7 @@ public sealed class ChatSessionStateService
return conv;
}
public DraftQueueItem? EnqueueDraft(string tab, string text, string priority = "next", ChatStorageService? storage = null)
public DraftQueueItem? EnqueueDraft(string tab, string text, string priority = "next", ChatStorageService? storage = null, string kind = "message")
{
var trimmed = text?.Trim() ?? "";
if (string.IsNullOrWhiteSpace(trimmed))
@@ -446,7 +446,7 @@ public sealed class ChatSessionStateService
var conv = EnsureCurrentConversation(tab);
conv.DraftQueueItems ??= new List<DraftQueueItem>();
var item = _draftQueue.CreateItem(trimmed, priority);
var item = _draftQueue.CreateItem(trimmed, priority, kind);
conv.DraftQueueItems.Add(item);
TouchConversation(storage, tab);
return item;

View File

@@ -20,12 +20,13 @@ public sealed class DraftQueueService
public DateTime? NextReadyAt { get; init; }
}
public DraftQueueItem CreateItem(string text, string priority = "next")
public DraftQueueItem CreateItem(string text, string priority = "next", string kind = "message")
{
return new DraftQueueItem
{
Text = text.Trim(),
Priority = NormalizePriority(priority),
Kind = string.IsNullOrWhiteSpace(kind) ? "message" : kind.Trim().ToLowerInvariant(),
State = "queued",
CreatedAt = DateTime.Now,
};

View File

@@ -67,7 +67,7 @@ public static class TokenEstimator
public static string Format(int count) => count switch
{
>= 1_000_000 => $"{count / 1_000_000.0:0.#}M",
>= 1_000 => $"{count / 1_000.0:0.#}k",
>= 1_000 => $"{count / 1_000.0:0.#}K",
_ => count.ToString(),
};

View File

@@ -1,17 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#1A1714"/>
<SolidColorBrush x:Key="ItemBackground" Color="#211D19"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#2B241E"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#332A22"/>
<SolidColorBrush x:Key="PrimaryText" Color="#F4EEE7"/>
<SolidColorBrush x:Key="SecondaryText" Color="#C5B6A6"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#9F8D7C"/>
<SolidColorBrush x:Key="AccentColor" Color="#D28A54"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#45382D"/>
<SolidColorBrush x:Key="HintBackground" Color="#251E18"/>
<SolidColorBrush x:Key="HintText" Color="#F0BB8A"/>
<SolidColorBrush x:Key="BorderColor" Color="#493B2F"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#6A5848"/>
<SolidColorBrush x:Key="LauncherBackground" Color="#181614"/>
<SolidColorBrush x:Key="ItemBackground" Color="#201D1A"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#2A2622"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#302B27"/>
<SolidColorBrush x:Key="PrimaryText" Color="#F3EEE9"/>
<SolidColorBrush x:Key="SecondaryText" Color="#C4BAB0"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#9C9187"/>
<SolidColorBrush x:Key="AccentColor" Color="#D07A47"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#403932"/>
<SolidColorBrush x:Key="HintBackground" Color="#24201C"/>
<SolidColorBrush x:Key="HintText" Color="#E6B48B"/>
<SolidColorBrush x:Key="BorderColor" Color="#433C35"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#665B52"/>
<SolidColorBrush x:Key="ShadowColor" Color="#99000000"/>
</ResourceDictionary>

View File

@@ -1,17 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#FBF8F1"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFDF8"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#F2EADF"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F6EEE3"/>
<SolidColorBrush x:Key="PrimaryText" Color="#1F1A16"/>
<SolidColorBrush x:Key="SecondaryText" Color="#6F6257"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#8E7E70"/>
<SolidColorBrush x:Key="AccentColor" Color="#C9733D"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E7DCCD"/>
<SolidColorBrush x:Key="HintBackground" Color="#F6EEE5"/>
<SolidColorBrush x:Key="HintText" Color="#9A5528"/>
<SolidColorBrush x:Key="BorderColor" Color="#E5D8C7"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#CDB9A5"/>
<SolidColorBrush x:Key="LauncherBackground" Color="#F7F6F3"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#EEE9E2"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F2EFEA"/>
<SolidColorBrush x:Key="PrimaryText" Color="#1D1B18"/>
<SolidColorBrush x:Key="SecondaryText" Color="#6B645D"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#8B837B"/>
<SolidColorBrush x:Key="AccentColor" Color="#C96A36"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E4DED6"/>
<SolidColorBrush x:Key="HintBackground" Color="#F3EEE8"/>
<SolidColorBrush x:Key="HintText" Color="#8B5637"/>
<SolidColorBrush x:Key="BorderColor" Color="#DDD6CE"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#C7BDB2"/>
<SolidColorBrush x:Key="ShadowColor" Color="#22000000"/>
</ResourceDictionary>

View File

@@ -1,17 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#F6F1E8"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFDF9"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#EFE5D8"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F3EADC"/>
<SolidColorBrush x:Key="PrimaryText" Color="#211B16"/>
<SolidColorBrush x:Key="SecondaryText" Color="#6E5F53"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#907F70"/>
<SolidColorBrush x:Key="AccentColor" Color="#C76B37"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E0D3C3"/>
<SolidColorBrush x:Key="HintBackground" Color="#F4EBDD"/>
<SolidColorBrush x:Key="HintText" Color="#995326"/>
<SolidColorBrush x:Key="BorderColor" Color="#E3D7C8"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#C9B6A2"/>
<SolidColorBrush x:Key="LauncherBackground" Color="#F7F6F3"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#EEE9E2"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#F2EFEA"/>
<SolidColorBrush x:Key="PrimaryText" Color="#1D1B18"/>
<SolidColorBrush x:Key="SecondaryText" Color="#6B645D"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#8B837B"/>
<SolidColorBrush x:Key="AccentColor" Color="#C96A36"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#E4DED6"/>
<SolidColorBrush x:Key="HintBackground" Color="#F3EEE8"/>
<SolidColorBrush x:Key="HintText" Color="#8B5637"/>
<SolidColorBrush x:Key="BorderColor" Color="#DDD6CE"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#C7BDB2"/>
<SolidColorBrush x:Key="ShadowColor" Color="#26000000"/>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="LauncherBackground" Color="#EDF1F7"/>
<SolidColorBrush x:Key="ItemBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ItemSelectedBackground" Color="#E3E8F5"/>
<SolidColorBrush x:Key="ItemHoverBackground" Color="#E9EEF8"/>
<SolidColorBrush x:Key="PrimaryText" Color="#182132"/>
<SolidColorBrush x:Key="SecondaryText" Color="#637083"/>
<SolidColorBrush x:Key="PlaceholderText" Color="#8A96A8"/>
<SolidColorBrush x:Key="AccentColor" Color="#6366F1"/>
<SolidColorBrush x:Key="SeparatorColor" Color="#D5DCEC"/>
<SolidColorBrush x:Key="HintBackground" Color="#EDE9FE"/>
<SolidColorBrush x:Key="HintText" Color="#4C1D95"/>
<SolidColorBrush x:Key="BorderColor" Color="#D5DCEC"/>
<SolidColorBrush x:Key="ScrollbarThumb" Color="#AFB8CA"/>
<SolidColorBrush x:Key="ShadowColor" Color="#22000000"/>
</ResourceDictionary>

View File

@@ -149,6 +149,10 @@ public class SettingsViewModel : INotifyPropertyChanged
private bool _snippetAutoExpand;
private string _language;
private string _indexSpeed;
private bool _aiEnabled = true;
private string _operationMode = "internal";
private string _agentTheme = "system";
private string _agentThemePreset = "claw";
// 기능 토글
private bool _showNumberBadges;
@@ -237,7 +241,7 @@ public class SettingsViewModel : INotifyPropertyChanged
public int LlmMaxContextTokens
{
get => _llmMaxContextTokens;
set { _llmMaxContextTokens = value; OnPropertyChanged(); }
set { _llmMaxContextTokens = Math.Clamp(value, 1024, 1_000_000); OnPropertyChanged(); }
}
public int LlmRetentionDays
{
@@ -688,6 +692,30 @@ public class SettingsViewModel : INotifyPropertyChanged
set { _indexSpeed = value; OnPropertyChanged(); OnPropertyChanged(nameof(IndexSpeedHint)); }
}
public bool AiEnabled
{
get => _aiEnabled;
set { _aiEnabled = value; OnPropertyChanged(); }
}
public string OperationMode
{
get => _operationMode;
set { _operationMode = string.IsNullOrWhiteSpace(value) ? "internal" : value.Trim().ToLowerInvariant(); OnPropertyChanged(); }
}
public string AgentTheme
{
get => _agentTheme;
set { _agentTheme = string.IsNullOrWhiteSpace(value) ? "system" : value.Trim().ToLowerInvariant(); OnPropertyChanged(); }
}
public string AgentThemePreset
{
get => _agentThemePreset;
set { _agentThemePreset = string.IsNullOrWhiteSpace(value) ? "claw" : value.Trim().ToLowerInvariant(); OnPropertyChanged(); }
}
public string IndexSpeedHint => _indexSpeed switch
{
"fast" => "CPU 사용률이 높아질 수 있습니다. 고성능 PC에 권장합니다.",
@@ -1025,7 +1053,7 @@ public class SettingsViewModel : INotifyPropertyChanged
var llm = s.Llm;
_llmService = NormalizeServiceKey(llm.Service);
_llmStreaming = llm.Streaming;
_llmMaxContextTokens = llm.MaxContextTokens;
_llmMaxContextTokens = Math.Clamp(llm.MaxContextTokens, 1024, 1_000_000);
_llmRetentionDays = llm.RetentionDays;
_llmTemperature = llm.Temperature;
_defaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(llm.DefaultAgentPermission);
@@ -1038,6 +1066,10 @@ public class SettingsViewModel : INotifyPropertyChanged
_contextCompactTriggerPercent = llm.ContextCompactTriggerPercent > 0
? Math.Clamp(llm.ContextCompactTriggerPercent, 50, 95)
: 80;
_aiEnabled = _service.Settings.AiEnabled;
_operationMode = string.IsNullOrWhiteSpace(_service.Settings.OperationMode) ? "internal" : _service.Settings.OperationMode;
_agentTheme = string.IsNullOrWhiteSpace(llm.AgentTheme) ? "system" : llm.AgentTheme;
_agentThemePreset = string.IsNullOrWhiteSpace(llm.AgentThemePreset) ? "claw" : llm.AgentThemePreset;
_agentLogLevel = llm.AgentLogLevel;
_agentUiExpressionLevel = (llm.AgentUiExpressionLevel ?? "balanced").Trim().ToLowerInvariant() switch
{
@@ -1463,7 +1495,7 @@ public class SettingsViewModel : INotifyPropertyChanged
// LLM 공통 설정 저장
s.Llm.Service = _llmService;
s.Llm.Streaming = _llmStreaming;
s.Llm.MaxContextTokens = _llmMaxContextTokens;
s.Llm.MaxContextTokens = Math.Clamp(_llmMaxContextTokens, 1024, 1_000_000);
s.Llm.RetentionDays = _llmRetentionDays;
s.Llm.Temperature = _llmTemperature;
s.Llm.DefaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(_defaultAgentPermission);
@@ -1474,6 +1506,10 @@ public class SettingsViewModel : INotifyPropertyChanged
s.Llm.MaxRetryOnError = _maxRetryOnError;
s.Llm.EnableProactiveContextCompact = _enableProactiveContextCompact;
s.Llm.ContextCompactTriggerPercent = _contextCompactTriggerPercent;
s.AiEnabled = _aiEnabled;
s.OperationMode = _operationMode;
s.Llm.AgentTheme = _agentTheme;
s.Llm.AgentThemePreset = _agentThemePreset;
s.Llm.AgentLogLevel = _agentLogLevel;
s.Llm.AgentUiExpressionLevel = _agentUiExpressionLevel;
s.Llm.PlanDiffSeverityMediumCount = _planDiffSeverityMediumCount;

View File

@@ -207,6 +207,7 @@
</Border>
</WrapPanel>
<TextBox x:Name="ModelInput"
Visibility="Collapsed"
Margin="0,6,0,8"
Padding="8,6"
Background="{DynamicResource ItemBackground}"
@@ -215,7 +216,7 @@
Foreground="{DynamicResource PrimaryText}"
FontSize="12"/>
<WrapPanel x:Name="ModelChipPanel" Margin="0,0,0,8"/>
<Grid Margin="0,6,0,0">
<Grid Margin="0,6,0,0" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>

View File

@@ -19,6 +19,7 @@ public partial class AgentSettingsWindow : Window
private string _reasoningMode = "detailed";
private string _folderDataUsage = "active";
private string _operationMode = OperationModePolicy.InternalMode;
private string _selectedModel = string.Empty;
public AgentSettingsWindow(SettingsService settings)
{
@@ -30,14 +31,14 @@ public partial class AgentSettingsWindow : Window
private void LoadFromSettings()
{
ModelInput.Text = _llm.Model ?? "";
_selectedModel = _llm.Model ?? "";
ModelInput.Text = _selectedModel;
_permissionMode = PermissionModeCatalog.NormalizeGlobalMode(_llm.FilePermission);
_planMode = string.IsNullOrWhiteSpace(_llm.PlanMode) ? "off" : _llm.PlanMode;
_reasoningMode = string.IsNullOrWhiteSpace(_llm.AgentDecisionLevel) ? "detailed" : _llm.AgentDecisionLevel;
_folderDataUsage = string.IsNullOrWhiteSpace(_llm.FolderDataUsage) ? "active" : _llm.FolderDataUsage;
_operationMode = OperationModePolicy.Normalize(_settings.Settings.OperationMode);
ChkVllmAllowInsecureTls.IsChecked = _llm.VllmAllowInsecureTls;
ChkEnableProactiveCompact.IsChecked = _llm.EnableProactiveContextCompact;
TxtContextCompactTriggerPercent.Text = Math.Clamp(_llm.ContextCompactTriggerPercent, 10, 95).ToString();
TxtMaxContextTokens.Text = Math.Max(1024, _llm.MaxContextTokens).ToString();
@@ -154,7 +155,13 @@ public partial class AgentSettingsWindow : Window
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White,
},
};
border.MouseLeftButtonUp += (_, _) => ModelInput.Text = captured;
SetCardSelection(border, string.Equals(captured, _selectedModel, StringComparison.OrdinalIgnoreCase));
border.MouseLeftButtonUp += (_, _) =>
{
_selectedModel = captured;
ModelInput.Text = captured;
BuildModelChips();
};
ModelChipPanel.Children.Add(border);
}
}
@@ -328,7 +335,7 @@ public partial class AgentSettingsWindow : Window
PermissionModeCatalog.Default => PermissionModeCatalog.AcceptEdits,
PermissionModeCatalog.AcceptEdits => PermissionModeCatalog.Plan,
PermissionModeCatalog.Plan => PermissionModeCatalog.BypassPermissions,
// 기본 순환에서는 코어 모드만 순환하고 dontAsk는 명시 선택으로만 진입.
// 권한 표면 모드는 코어 4단계만 순환합니다.
PermissionModeCatalog.BypassPermissions => PermissionModeCatalog.Deny,
PermissionModeCatalog.DontAsk => PermissionModeCatalog.Deny,
_ => PermissionModeCatalog.Deny,
@@ -371,14 +378,13 @@ public partial class AgentSettingsWindow : Window
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
_llm.Model = ModelInput.Text.Trim();
_llm.Model = string.IsNullOrWhiteSpace(_selectedModel) ? (_llm.Model ?? "") : _selectedModel.Trim();
_llm.FilePermission = _permissionMode;
_llm.PlanMode = _planMode;
_llm.AgentDecisionLevel = _reasoningMode;
_llm.FolderDataUsage = _folderDataUsage;
_llm.AgentUiExpressionLevel = "rich";
_llm.VllmAllowInsecureTls = ChkVllmAllowInsecureTls.IsChecked == true;
_llm.EnableProactiveContextCompact = ChkEnableProactiveCompact.IsChecked == true;
_llm.ContextCompactTriggerPercent = ParseInt(TxtContextCompactTriggerPercent.Text, 80, 10, 95);
_llm.MaxContextTokens = ParseInt(TxtMaxContextTokens.Text, 4096, 1024, 200000);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -213,6 +213,7 @@ internal sealed class ModelRegistrationDialog : Window
};
stack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _endpointBox });
var showTlsOption = string.Equals(service, "vllm", StringComparison.OrdinalIgnoreCase);
var tlsPanel = new Border
{
Background = Brushes.Transparent,
@@ -253,7 +254,8 @@ internal sealed class ModelRegistrationDialog : Window
Grid.SetColumn(_allowInsecureTlsCheck, 1);
tlsGrid.Children.Add(_allowInsecureTlsCheck);
tlsPanel.Child = tlsGrid;
stack.Children.Add(tlsPanel);
if (showTlsOption)
stack.Children.Add(tlsPanel);
// ── 인증 방식 선택 ──────────────────────────────────────────────────
stack.Children.Add(new TextBlock
@@ -456,3 +458,4 @@ internal sealed class ModelRegistrationDialog : Window
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Windows.Data;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -60,6 +61,7 @@ public partial class SettingsWindow : Window
BuildDockBarSettings();
BuildTextActionCommandsPanel();
MoveBlockSectionToEtc();
BuildServiceModelPanels();
// 스킬이 아직 로드되지 않았으면 백그라운드에서 로드 후 UI 구성
var app = System.Windows.Application.Current as App;
@@ -83,15 +85,62 @@ public partial class SettingsWindow : Window
ApplyAiEnabledState(app?.SettingsService?.Settings.AiEnabled ?? false, init: true);
ApplyOperationModeState(app?.SettingsService?.Settings.OperationMode);
InitializeDisplayModeUi();
SyncAgentSelectionCards();
};
}
private void SyncAgentSelectionCards()
{
var service = (_vm.LlmService ?? "").Trim().ToLowerInvariant();
if (AgentServiceCardOllama != null) AgentServiceCardOllama.IsChecked = service == "ollama";
if (AgentServiceCardVllm != null) AgentServiceCardVllm.IsChecked = service == "vllm";
if (AgentServiceCardGemini != null) AgentServiceCardGemini.IsChecked = service == "gemini";
if (AgentServiceCardClaude != null) AgentServiceCardClaude.IsChecked = service == "claude";
var permission = Services.Agent.PermissionModeCatalog.NormalizeGlobalMode(_vm.DefaultAgentPermission);
if (AgentPermissionCardAsk != null) AgentPermissionCardAsk.IsChecked = permission == "Ask";
if (AgentPermissionCardPlan != null) AgentPermissionCardPlan.IsChecked = permission == "Plan";
if (AgentPermissionCardAuto != null) AgentPermissionCardAuto.IsChecked = permission == "Auto";
if (AgentPermissionCardDeny != null) AgentPermissionCardDeny.IsChecked = permission == "Deny";
var decision = (_vm.AgentDecisionLevel ?? "normal").Trim().ToLowerInvariant();
if (AgentDecisionCardMinimal != null) AgentDecisionCardMinimal.IsChecked = decision == "minimal";
if (AgentDecisionCardNormal != null) AgentDecisionCardNormal.IsChecked = decision == "normal";
if (AgentDecisionCardDetailed != null) AgentDecisionCardDetailed.IsChecked = decision == "detailed";
var planMode = (_vm.PlanMode ?? "off").Trim().ToLowerInvariant();
if (AgentPlanModeCardOff != null) AgentPlanModeCardOff.IsChecked = planMode == "off";
if (AgentPlanModeCardAlways != null) AgentPlanModeCardAlways.IsChecked = planMode == "always";
if (AgentPlanModeCardAuto != null) AgentPlanModeCardAuto.IsChecked = planMode == "auto";
var operationMode = OperationModePolicy.Normalize(_vm.OperationMode);
if (AgentOperationModeInternal != null) AgentOperationModeInternal.IsChecked = operationMode == OperationModePolicy.InternalMode;
if (AgentOperationModeExternal != null) AgentOperationModeExternal.IsChecked = operationMode == OperationModePolicy.ExternalMode;
var maxContextTokens = _vm.LlmMaxContextTokens;
if (AgentContextTokens4K != null) AgentContextTokens4K.IsChecked = maxContextTokens <= 4096;
if (AgentContextTokens16K != null) AgentContextTokens16K.IsChecked = maxContextTokens > 4096 && maxContextTokens <= 16384;
if (AgentContextTokens64K != null) AgentContextTokens64K.IsChecked = maxContextTokens > 16384 && maxContextTokens <= 65536;
if (AgentContextTokens256K != null) AgentContextTokens256K.IsChecked = maxContextTokens > 65536 && maxContextTokens <= 262144;
if (AgentContextTokens1M != null) AgentContextTokens1M.IsChecked = maxContextTokens > 262144;
var retentionDays = _vm.LlmRetentionDays;
if (AgentRetentionDays7 != null) AgentRetentionDays7.IsChecked = retentionDays == 7;
if (AgentRetentionDays30 != null) AgentRetentionDays30.IsChecked = retentionDays == 30;
if (AgentRetentionDays90 != null) AgentRetentionDays90.IsChecked = retentionDays == 90;
if (AgentRetentionDaysUnlimited != null) AgentRetentionDaysUnlimited.IsChecked = retentionDays == 0;
var logLevel = (_vm.AgentLogLevel ?? "simple").Trim().ToLowerInvariant();
if (AgentLogLevelSimple != null) AgentLogLevelSimple.IsChecked = logLevel == "simple";
if (AgentLogLevelDetailed != null) AgentLogLevelDetailed.IsChecked = logLevel == "detailed";
if (AgentLogLevelDebug != null) AgentLogLevelDebug.IsChecked = logLevel == "debug";
}
private void InitializeDisplayModeUi()
{
var app = System.Windows.Application.Current as App;
if (app?.SettingsService?.Settings?.Llm != null)
app.SettingsService.Settings.Llm.AgentUiExpressionLevel = "rich";
SetDisplayMode("rich", persist: false);
var saved = app?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel;
SetDisplayMode(saved ?? "balanced", persist: false);
}
private void DisplayMode_Checked(object sender, RoutedEventArgs e)
@@ -107,6 +156,19 @@ public partial class SettingsWindow : Window
SetDisplayMode(next, persist: true);
}
private void AgentDisplayMode_Checked(object sender, RoutedEventArgs e)
{
if (_isDisplayModeSyncing) return;
var rb = sender as RadioButton;
var next = rb?.Name switch
{
"AgentDisplayModeRich" => "rich",
"AgentDisplayModeSimple" => "simple",
_ => "balanced",
};
SetDisplayMode(next, persist: true);
}
private void SetDisplayMode(string mode, bool persist)
{
mode = NormalizeDisplayMode(mode);
@@ -117,9 +179,15 @@ public partial class SettingsWindow : Window
var rich = GetDisplayModeRadio("DisplayModeRich");
var balanced = GetDisplayModeRadio("DisplayModeBalanced");
var simple = GetDisplayModeRadio("DisplayModeSimple");
var agentRich = GetDisplayModeRadio("AgentDisplayModeRich");
var agentBalanced = GetDisplayModeRadio("AgentDisplayModeBalanced");
var agentSimple = GetDisplayModeRadio("AgentDisplayModeSimple");
if (rich != null) rich.IsChecked = mode == "rich";
if (balanced != null) balanced.IsChecked = mode == "balanced";
if (simple != null) simple.IsChecked = mode == "simple";
if (agentRich != null) agentRich.IsChecked = mode == "rich";
if (agentBalanced != null) agentBalanced.IsChecked = mode == "balanced";
if (agentSimple != null) agentSimple.IsChecked = mode == "simple";
}
finally
{
@@ -183,21 +251,17 @@ public partial class SettingsWindow : Window
if (AgentTabCommon == null) return;
SetSubTabVisible(AgentTabCommon, true);
SetSubTabVisible(AgentTabChat, mode != "simple");
SetSubTabVisible(AgentTabChat, true);
SetSubTabVisible(AgentTabCoworkCode, true);
SetSubTabVisible(AgentTabCowork, mode == "rich");
SetSubTabVisible(AgentTabCode, mode == "rich");
SetSubTabVisible(AgentTabTools, mode != "simple");
SetSubTabVisible(AgentTabEtc, mode != "simple");
SetSubTabVisible(AgentTabDev, mode == "rich");
SetSubTabVisible(AgentTabDev, true);
SetSubTabVisible(AgentTabCowork, false);
SetSubTabVisible(AgentTabCode, false);
SetSubTabVisible(AgentTabTools, false);
SetSubTabVisible(AgentTabEtc, false);
if (AgentTabCommon.IsChecked != true
&& AgentTabChat.IsChecked != true
&& AgentTabCoworkCode.IsChecked != true
&& AgentTabCowork.IsChecked != true
&& AgentTabCode.IsChecked != true
&& AgentTabTools.IsChecked != true
&& AgentTabEtc.IsChecked != true
&& AgentTabDev.IsChecked != true)
{
AgentTabCommon.IsChecked = true;
@@ -327,66 +391,66 @@ public partial class SettingsWindow : Window
{
("파일/검색", "\uE8B7", "#F59E0B", new[]
{
("file_read", "파일 읽기 텍스트/바이너리 파일의 내용을 읽습니다"),
("file_write", "파일 쓰기 새 파일을 생성하거나 기존 파일을 덮어씁니다"),
("file_edit", "파일 편집 줄 번호 기반으로 파일의 특정 부분을 수정합니다"),
("glob", "파일 패턴 검색 글로브 패턴으로 파일을 찾습니다 (예: **/*.cs)"),
("grep_tool", "텍스트 검색 정규식으로 파일 내용을 검색합니다"),
("folder_map", "폴더 구조 프로젝트의 디렉토리 트리를 조회합니다"),
("document_read", "문서 읽기 PDF, DOCX 등 문서 파일의 텍스트를 추출합니다"),
("file_read", "파일 읽기 ? 텍스트/바이너리 파일의 내용을 읽습니다"),
("file_write", "파일 쓰기 ? 새 파일을 생성하거나 기존 파일을 덮어씁니다"),
("file_edit", "파일 편집 ? 줄 번호 기반으로 파일의 특정 부분을 수정합니다"),
("glob", "파일 패턴 검색 ? 글로브 패턴으로 파일을 찾습니다 (예: **/*.cs)"),
("grep_tool", "텍스트 검색 ? 정규식으로 파일 내용을 검색합니다"),
("folder_map", "폴더 구조 ? 프로젝트의 디렉토리 트리를 조회합니다"),
("document_read", "문서 읽기 ? PDF, DOCX 등 문서 파일의 텍스트를 추출합니다"),
}),
("프로세스/빌드", "\uE756", "#06B6D4", new[]
{
("process", "프로세스 실행 외부 명령/프로그램을 실행합니다"),
("build_run", "빌드/테스트 프로젝트 타입을 감지하여 빌드/테스트를 실행합니다"),
("dev_env_detect", "개발 환경 감지 IDE, 런타임, 빌드 도구를 자동으로 인식합니다"),
("process", "프로세스 실행 ? 외부 명령/프로그램을 실행합니다"),
("build_run", "빌드/테스트 ? 프로젝트 타입을 감지하여 빌드/테스트를 실행합니다"),
("dev_env_detect", "개발 환경 감지 ? IDE, 런타임, 빌드 도구를 자동으로 인식합니다"),
}),
("코드 분석", "\uE943", "#818CF8", new[]
{
("search_codebase", "코드 키워드 검색 TF-IDF 기반으로 관련 코드를 찾습니다"),
("code_review", "AI 코드 리뷰 Git diff 분석, 파일 정적 검사, PR 요약"),
("lsp", "LSP 인텔리전스 정의 이동, 참조 검색, 심볼 목록 (C#/TS/Python)"),
("test_loop", "테스트 루프 테스트 생성/실행/분석 자동화"),
("git_tool", "Git 작업 status, log, diff, commit 등 Git 명령 실행"),
("snippet_runner", "코드 실행 C#/Python/JavaScript 스니펫 즉시 실행"),
("search_codebase", "코드 키워드 검색 ? TF-IDF 기반으로 관련 코드를 찾습니다"),
("code_review", "AI 코드 리뷰 ? Git diff 분석, 파일 정적 검사, PR 요약"),
("lsp", "LSP 인텔리전스 ? 정의 이동, 참조 검색, 심볼 목록 (C#/TS/Python)"),
("test_loop", "테스트 루프 ? 테스트 생성/실행/분석 자동화"),
("git_tool", "Git 작업 ? status, log, diff, commit 등 Git 명령 실행"),
("snippet_runner", "코드 실행 ? C#/Python/JavaScript 스니펫 즉시 실행"),
}),
("문서 생성", "\uE8A5", "#34D399", new[]
{
("excel_create", "Excel 생성 .xlsx 스프레드시트를 생성합니다"),
("docx_create", "Word 생성 .docx 문서를 생성합니다"),
("csv_create", "CSV 생성 CSV 파일을 생성합니다"),
("markdown_create", "마크다운 생성 .md 파일을 생성합니다"),
("html_create", "HTML 생성 HTML 파일을 생성합니다"),
("chart_create", "차트 생성 데이터 시각화 차트를 생성합니다"),
("batch_create", "배치 생성 스크립트 파일을 생성합니다"),
("document_review", "문서 검증 문서 품질을 검사합니다"),
("format_convert", "포맷 변환 문서 형식을 변환합니다"),
("excel_create", "Excel 생성 ? .xlsx 스프레드시트를 생성합니다"),
("docx_create", "Word 생성 ? .docx 문서를 생성합니다"),
("csv_create", "CSV 생성 ? CSV 파일을 생성합니다"),
("markdown_create", "마크다운 생성 ? .md 파일을 생성합니다"),
("html_create", "HTML 생성 ? HTML 파일을 생성합니다"),
("chart_create", "차트 생성 ? 데이터 시각화 차트를 생성합니다"),
("batch_create", "배치 생성 ? 스크립트 파일을 생성합니다"),
("document_review", "문서 검증 ? 문서 품질을 검사합니다"),
("format_convert", "포맷 변환 ? 문서 형식을 변환합니다"),
}),
("데이터 처리", "\uE9F5", "#F59E0B", new[]
{
("json_tool", "JSON 처리 JSON 파싱, 변환, 검증, 포맷팅"),
("regex_tool", "정규식 정규식 테스트, 추출, 치환"),
("diff_tool", "텍스트 비교 두 파일/텍스트 비교, 통합 diff 출력"),
("base64_tool", "인코딩 Base64/URL 인코딩, 디코딩"),
("hash_tool", "해시 계산 MD5/SHA256 해시 계산 (파일/텍스트)"),
("datetime_tool", "날짜/시간 날짜 변환, 타임존, 기간 계산"),
("json_tool", "JSON 처리 ? JSON 파싱, 변환, 검증, 포맷팅"),
("regex_tool", "정규식 ? 정규식 테스트, 추출, 치환"),
("diff_tool", "텍스트 비교 ? 두 파일/텍스트 비교, 통합 diff 출력"),
("base64_tool", "인코딩 ? Base64/URL 인코딩, 디코딩"),
("hash_tool", "해시 계산 ? MD5/SHA256 해시 계산 (파일/텍스트)"),
("datetime_tool", "날짜/시간 ? 날짜 변환, 타임존, 기간 계산"),
}),
("시스템/환경", "\uE770", "#06B6D4", new[]
{
("clipboard_tool", "클립보드 Windows 클립보드 읽기/쓰기 (텍스트/이미지)"),
("notify_tool", "알림 Windows 시스템 알림 전송"),
("env_tool", "환경변수 환경변수 읽기/설정 (프로세스 범위)"),
("zip_tool", "압축 파일 압축(zip) / 해제"),
("http_tool", "HTTP 로컬/사내 HTTP API 호출 (GET/POST)"),
("sql_tool", "SQLite SQLite DB 쿼리 실행 (로컬 파일)"),
("clipboard_tool", "클립보드 ? Windows 클립보드 읽기/쓰기 (텍스트/이미지)"),
("notify_tool", "알림 ? Windows 시스템 알림 전송"),
("env_tool", "환경변수 ? 환경변수 읽기/설정 (프로세스 범위)"),
("zip_tool", "압축 ? 파일 압축(zip) / 해제"),
("http_tool", "HTTP ? 로컬/사내 HTTP API 호출 (GET/POST)"),
("sql_tool", "SQLite ? SQLite DB 쿼리 실행 (로컬 파일)"),
}),
("에이전트", "\uE99A", "#F472B6", new[]
{
("spawn_agent", "서브에이전트 하위 작업을 병렬로 실행하는 서브에이전트를 생성합니다"),
("wait_agents", "에이전트 대기 실행 중인 서브에이전트의 결과를 수집합니다"),
("memory", "에이전트 메모리 프로젝트 규칙, 선호도를 저장/검색합니다"),
("skill_manager", "스킬 관리 스킬 목록 조회, 상세 정보, 재로드"),
("project_rules", "프로젝트 지침 AGENTS.md 개발 지침을 읽고 씁니다"),
("spawn_agent", "서브에이전트 ? 하위 작업을 병렬로 실행하는 서브에이전트를 생성합니다"),
("wait_agents", "에이전트 대기 ? 실행 중인 서브에이전트의 결과를 수집합니다"),
("memory", "에이전트 메모리 ? 프로젝트 규칙, 선호도를 저장/검색합니다"),
("skill_manager", "스킬 관리 ? 스킬 목록 조회, 상세 정보, 재로드"),
("project_rules", "프로젝트 지침 ? AGENTS.md 개발 지침을 읽고 씁니다"),
}),
};
@@ -878,7 +942,7 @@ public partial class SettingsWindow : Window
VerticalAlignment = VerticalAlignment.Center,
Child = new TextBlock
{
Text = " 사용 가능",
Text = "? 사용 가능",
FontSize = 9,
Foreground = new SolidColorBrush(Color.FromRgb(0x34, 0xD3, 0x99)),
FontWeight = FontWeights.SemiBold,
@@ -891,7 +955,7 @@ public partial class SettingsWindow : Window
// 아래 줄: 설명 (뱃지와 구분되도록 위 여백 추가)
row.Children.Add(new TextBlock
{
Text = $"{skill.Label} {skill.Description}",
Text = $"{skill.Label} ? {skill.Description}",
FontSize = 11.5,
Foreground = TryFindResource("SecondaryText") as Brush
?? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#6666AA")),
@@ -1061,7 +1125,7 @@ public partial class SettingsWindow : Window
ChkDockAutoShow.Unchecked += (_, _) =>
{
launcher.DockBarAutoShow = false; svc.Save();
// 끄기 시에는 설정만 저장 독 바를 토글하지 않음 (이미 표시 중이면 유지, 다음 재시작 시 안 뜸)
// 끄기 시에는 설정만 저장 ? 독 바를 토글하지 않음 (이미 표시 중이면 유지, 다음 재시작 시 안 뜸)
};
ChkDockRainbowGlow.IsChecked = launcher.DockBarRainbowGlow;
@@ -1229,7 +1293,7 @@ public partial class SettingsWindow : Window
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x22, 0xFF, 0xFF, 0xFF));
var shadowColor = TryFindResource("ShadowColor") is Color sc ? sc : Colors.Black;
// 보관 기간 선택 팝업 커스텀 버튼으로 날짜 선택
// 보관 기간 선택 팝업 ? 커스텀 버튼으로 날짜 선택
var popup = new Window
{
WindowStyle = WindowStyle.None, AllowsTransparency = true, Background = Brushes.Transparent,
@@ -1396,7 +1460,7 @@ public partial class SettingsWindow : Window
// ─── 핫키 (콤보박스 선택 방식) ──────────────────────────────────────────
/// <summary>이전 녹화기에서 호출되던 초기화 콤보박스 전환 후 무연산 (호환용)</summary>
/// <summary>이전 녹화기에서 호출되던 초기화 ? 콤보박스 전환 후 무연산 (호환용)</summary>
private void RefreshHotkeyBadges() { /* 콤보박스 SelectedValue 바인딩으로 대체 */ }
/// <summary>현재 핫키가 콤보박스 목록에 없으면 항목으로 추가합니다.</summary>
@@ -1423,7 +1487,7 @@ public partial class SettingsWindow : Window
HotkeyCombo.SelectedIndex = 0;
}
/// <summary>Window-level PreviewKeyDown 핫키 녹화 제거 후 잔여 호출 보호</summary>
/// <summary>Window-level PreviewKeyDown ? 핫키 녹화 제거 후 잔여 호출 보호</summary>
private void Window_PreviewKeyDown(object sender, KeyEventArgs e) { }
/// <summary>WPF Key → HotkeyParser가 인식하는 문자열 이름.</summary>
@@ -1444,11 +1508,11 @@ public partial class SettingsWindow : Window
Key.Up => "Up",
Key.Down => "Down",
Key.Insert => "Insert",
// AZ
// A?Z
>= Key.A and <= Key.Z => key.ToString(),
// 09 (메인 키보드)
// 0?9 (메인 키보드)
>= Key.D0 and <= Key.D9 => ((int)(key - Key.D0)).ToString(),
// F1F12
// F1?F12
>= Key.F1 and <= Key.F12 => key.ToString(),
// 기호
Key.OemTilde => "`",
@@ -1549,7 +1613,7 @@ public partial class SettingsWindow : Window
var count = Services.Agent.SkillService.ImportSkills(dlg.FileName);
if (count > 0)
CustomMessageBox.Show($"스킬 {count}개를 성공적으로 가져왔습니<EFBFBD><EFBFBD>.\n스킬 목록이 갱신됩니다.", "스킬 가져오기");
CustomMessageBox.Show($"스킬 {count}개를 성공적으로 가져왔습니??.\n스킬 목록이 갱신됩니다.", "스킬 가져오기");
else
CustomMessageBox.Show("스킬 가져오기에 실패했습니다.\nzip 파일에 .skill.md 또는 SKILL.md 파일이 포함되어야 합니다.", "스킬 가져오기");
}
@@ -1607,7 +1671,7 @@ public partial class SettingsWindow : Window
{
var cb = new CheckBox
{
Content = $"/{skill.Name} {skill.Label}",
Content = $"/{skill.Name} ? {skill.Label}",
FontSize = 12.5,
Foreground = fgBrush,
Margin = new Thickness(0, 3, 0, 3),
@@ -1710,6 +1774,7 @@ public partial class SettingsWindow : Window
Cp4dPassword = Services.CryptoService.EncryptIfEnabled(dlg.Cp4dPassword, IsEncryptionEnabled),
});
BuildFallbackModelsPanel();
BuildServiceModelPanels();
}
}
@@ -1738,6 +1803,7 @@ public partial class SettingsWindow : Window
row.Cp4dUsername = dlg.Cp4dUsername;
row.Cp4dPassword = Services.CryptoService.EncryptIfEnabled(dlg.Cp4dPassword, IsEncryptionEnabled);
BuildFallbackModelsPanel();
BuildServiceModelPanels();
}
}
@@ -1750,6 +1816,7 @@ public partial class SettingsWindow : Window
{
_vm.RegisteredModels.Remove(row);
BuildFallbackModelsPanel();
BuildServiceModelPanels();
}
}
@@ -1795,21 +1862,30 @@ public partial class SettingsWindow : Window
private void AgentSubTab_Checked(object sender, RoutedEventArgs e)
{
if (AgentPanelCommon == null) return; // 초기화 전 방어
AgentPanelCommon.Visibility = AgentTabCommon.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelChat.Visibility = AgentTabChat.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelCoworkCode.Visibility = AgentTabCoworkCode.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelCowork.Visibility = AgentTabCowork.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelCode.Visibility = AgentTabCode.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
var showCommon = AgentTabCommon.IsChecked == true;
var showService = AgentTabChat.IsChecked == true;
var showPermission = AgentTabCoworkCode.IsChecked == true;
var showAdvanced = AgentTabDev.IsChecked == true;
AgentPanelCommon.Visibility = showCommon || showService ? Visibility.Visible : Visibility.Collapsed;
if (AgentCommonOverviewSection != null)
AgentCommonOverviewSection.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
if (AgentCommonRuntimeSection != null)
AgentCommonRuntimeSection.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
if (AgentServiceSection != null)
AgentServiceSection.Visibility = showService ? Visibility.Visible : Visibility.Collapsed;
AgentPanelCoworkCode.Visibility = showPermission ? Visibility.Visible : Visibility.Collapsed;
AgentPanelChat.Visibility = Visibility.Collapsed;
AgentPanelCowork.Visibility = Visibility.Collapsed;
AgentPanelCode.Visibility = Visibility.Collapsed;
if (AgentPanelDev != null)
AgentPanelDev.Visibility = AgentTabDev.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelDev.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
if (AgentPanelEtc != null)
AgentPanelEtc.Visibility = AgentTabEtc.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
AgentPanelEtc.Visibility = Visibility.Collapsed;
if (AgentPanelTools != null)
{
var show = AgentTabTools.IsChecked == true;
AgentPanelTools.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
if (show) LoadToolCards();
}
AgentPanelTools.Visibility = Visibility.Collapsed;
}
// ─── 도구 관리 카드 UI ──────────────────────────────────────────────
@@ -2085,6 +2161,320 @@ public partial class SettingsWindow : Window
SvcPanelVllm.Visibility = SvcTabVllm.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
SvcPanelGemini.Visibility = SvcTabGemini.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
SvcPanelSigmoid.Visibility = SvcTabSigmoid.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
BuildServiceModelPanels();
SyncAgentSelectionCards();
}
private void AgentServiceCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
var service = rb.Name switch
{
"AgentServiceCardVllm" => "vllm",
"AgentServiceCardGemini" => "gemini",
"AgentServiceCardClaude" => "claude",
_ => "ollama",
};
_vm.LlmService = service;
if (SvcTabOllama != null) SvcTabOllama.IsChecked = service == "ollama";
if (SvcTabVllm != null) SvcTabVllm.IsChecked = service == "vllm";
if (SvcTabGemini != null) SvcTabGemini.IsChecked = service == "gemini";
if (SvcTabSigmoid != null) SvcTabSigmoid.IsChecked = service == "claude";
BuildServiceModelPanels();
}
private void AgentPermissionCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
_vm.DefaultAgentPermission = rb.Name switch
{
"AgentPermissionCardPlan" => "Plan",
"AgentPermissionCardAuto" => "Auto",
"AgentPermissionCardDeny" => "Deny",
_ => "Ask",
};
}
private void AgentDecisionCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
_vm.AgentDecisionLevel = rb.Name switch
{
"AgentDecisionCardMinimal" => "minimal",
"AgentDecisionCardDetailed" => "detailed",
_ => "normal",
};
}
private void AgentPlanModeCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true) return;
_vm.PlanMode = rb.Name switch
{
"AgentPlanModeCardAlways" => "always",
"AgentPlanModeCardAuto" => "auto",
_ => "off",
};
}
private void AgentOperationModeCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
return;
var next = rb.Name == "AgentOperationModeExternal"
? OperationModePolicy.ExternalMode
: OperationModePolicy.InternalMode;
var app = System.Windows.Application.Current as App;
var settings = app?.SettingsService?.Settings;
if (settings == null)
return;
var current = OperationModePolicy.Normalize(settings.OperationMode);
if (string.Equals(next, current, StringComparison.OrdinalIgnoreCase))
return;
var ok = PromptPasswordDialog(
"운영 모드 변경 ? 비밀번호 확인",
"\U0001f512 사내/사외 모드 변경",
"비밀번호를 입력하세요:");
if (!ok)
{
ApplyOperationModeState(settings.OperationMode);
return;
}
settings.OperationMode = next;
_vm.OperationMode = next;
app?.SettingsService?.Save();
ApplyOperationModeState(next);
}
private void AgentContextTokensCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
return;
_vm.LlmMaxContextTokens = rb.Name switch
{
"AgentContextTokens16K" => 16384,
"AgentContextTokens64K" => 65536,
"AgentContextTokens256K" => 262144,
"AgentContextTokens1M" => 1_000_000,
_ => 4096,
};
}
private void AgentRetentionDaysCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
return;
_vm.LlmRetentionDays = rb.Name switch
{
"AgentRetentionDays30" => 30,
"AgentRetentionDays90" => 90,
"AgentRetentionDaysUnlimited" => 0,
_ => 7,
};
}
private void AgentLogLevelCard_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded || sender is not RadioButton rb || rb.IsChecked != true)
return;
_vm.AgentLogLevel = rb.Name switch
{
"AgentLogLevelDetailed" => "detailed",
"AgentLogLevelDebug" => "debug",
_ => "simple",
};
}
private void BuildServiceModelPanels()
{
BuildRegisteredModelList("ollama", OllamaRegisteredModelsList);
BuildRegisteredModelList("vllm", VllmRegisteredModelsList);
BuildServiceModelChips("ollama", OllamaModelChipPanel, _vm.OllamaModel, value => _vm.OllamaModel = value);
BuildServiceModelChips("vllm", VllmModelChipPanel, _vm.VllmModel, value => _vm.VllmModel = value);
BuildPresetModelChips(
GeminiModelChipPanel,
_vm.GeminiModel,
value => _vm.GeminiModel = value,
new (string Id, string Label, string Hint)[]
{
("gemini-3.1-pro-preview", "Gemini 3.1 Pro", "최고 성능"),
("gemini-3-flash", "Gemini 3 Flash", "빠른 응답"),
("gemini-3.1-flash-lite-preview", "Gemini 3.1 Flash Lite", "경량"),
("gemini-2.5-flash", "Gemini 2.5 Flash", "균형"),
("gemini-2.5-flash-lite", "Gemini 2.5 Flash Lite", "저비용"),
});
BuildPresetModelChips(
ClaudeModelChipPanel,
_vm.ClaudeModel,
value => _vm.ClaudeModel = value,
new (string Id, string Label, string Hint)[]
{
(ProviderModelIds.SigmoidOpus46, "Claude Opus 4.6", "최고 성능"),
(ProviderModelIds.SigmoidSonnet46, "Claude Sonnet 4.6", "균형"),
(ProviderModelIds.SigmoidHaiku45_20251001, "Claude Haiku 4.5", "경량"),
(ProviderModelIds.SigmoidSonnet45_20250929, "Claude Sonnet 4.5", "이전 안정판"),
(ProviderModelIds.SigmoidOpus4_20250514, "Claude Opus 4", "이전 최고급"),
});
}
private void BuildRegisteredModelList(string service, ItemsControl? target)
{
if (target == null) return;
var view = new ListCollectionView(_vm.RegisteredModels);
view.Filter = item =>
{
if (item is not RegisteredModelRow row) return false;
return string.Equals(row.Service, service, StringComparison.OrdinalIgnoreCase);
};
target.ItemsSource = view;
}
private void BuildServiceModelChips(string service, WrapPanel? panel, string selectedValue, Action<string> applySelection)
{
if (panel == null)
return;
panel.Children.Clear();
var models = _vm.RegisteredModels
.Where(row => string.Equals(row.Service, service, StringComparison.OrdinalIgnoreCase))
.ToList();
if (models.Count == 0)
{
panel.Children.Add(new Border
{
Background = TryFindResource("ItemBackground") as Brush ?? Brushes.WhiteSmoke,
BorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.LightGray,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(10),
Padding = new Thickness(12, 8, 12, 8),
Child = new TextBlock
{
Text = "등록된 모델이 없습니다. + 모델 추가로 먼저 등록하세요.",
FontSize = 11.5,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
}
});
return;
}
foreach (var model in models)
{
var isSelected = string.Equals(model.EncryptedModelName, selectedValue, StringComparison.OrdinalIgnoreCase);
var chip = new Border
{
Cursor = Cursors.Hand,
CornerRadius = new CornerRadius(10),
BorderThickness = new Thickness(1),
BorderBrush = isSelected
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
: (TryFindResource("BorderColor") as Brush ?? Brushes.LightGray),
Background = isSelected
? (TryFindResource("ItemHoverBackground") as Brush ?? Brushes.AliceBlue)
: Brushes.Transparent,
Padding = new Thickness(12, 9, 12, 9),
Margin = new Thickness(0, 0, 8, 8),
Child = new StackPanel
{
Children =
{
new TextBlock
{
Text = string.IsNullOrWhiteSpace(model.Alias) ? "(이름 없음)" : model.Alias,
FontSize = 12,
FontWeight = isSelected ? FontWeights.SemiBold : FontWeights.Normal,
Foreground = isSelected
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
: (TryFindResource("PrimaryText") as Brush ?? Brushes.Black),
},
new TextBlock
{
Text = string.IsNullOrWhiteSpace(model.Endpoint) ? "기본 서버 사용" : model.Endpoint,
FontSize = 10.5,
Margin = new Thickness(0, 3, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
}
}
}
};
var captured = model.EncryptedModelName;
chip.MouseLeftButtonUp += (_, _) =>
{
applySelection(captured);
BuildServiceModelPanels();
};
panel.Children.Add(chip);
}
}
private void BuildPresetModelChips(
WrapPanel? panel,
string? selectedValue,
Action<string> applySelection,
IEnumerable<(string Id, string Label, string Hint)> models)
{
if (panel == null)
return;
panel.Children.Clear();
foreach (var model in models)
{
var isSelected = string.Equals(model.Id, selectedValue, StringComparison.OrdinalIgnoreCase);
var chip = new Border
{
Cursor = Cursors.Hand,
CornerRadius = new CornerRadius(12),
BorderThickness = new Thickness(1),
BorderBrush = isSelected
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
: (TryFindResource("BorderColor") as Brush ?? Brushes.LightGray),
Background = isSelected
? (TryFindResource("ItemHoverBackground") as Brush ?? Brushes.AliceBlue)
: Brushes.Transparent,
Padding = new Thickness(12, 9, 12, 9),
Margin = new Thickness(0, 0, 8, 8),
Child = new StackPanel
{
Children =
{
new TextBlock
{
Text = model.Label,
FontSize = 12,
FontWeight = isSelected ? FontWeights.SemiBold : FontWeights.Normal,
Foreground = isSelected
? (TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue)
: (TryFindResource("PrimaryText") as Brush ?? Brushes.Black),
},
new TextBlock
{
Text = model.Hint,
FontSize = 10.5,
Margin = new Thickness(0, 3, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
}
}
}
};
var capturedId = model.Id;
chip.MouseLeftButtonUp += (_, _) =>
{
applySelection(capturedId);
BuildServiceModelPanels();
};
panel.Children.Add(chip);
}
}
private void ThemeCard_Click(object sender, RoutedEventArgs e)
@@ -2115,7 +2505,7 @@ public partial class SettingsWindow : Window
// 비밀번호 확인 다이얼로그
var dlg = new Window
{
Title = "개발자 모드 비밀번호 확인",
Title = "개발자 모드 ? 비밀번호 확인",
Width = 340, SizeToContent = SizeToContent.Height,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this, ResizeMode = ResizeMode.NoResize,
@@ -2169,7 +2559,7 @@ public partial class SettingsWindow : Window
if (dlg.ShowDialog() != true)
{
// 비밀번호 실패/취소 체크 해제 + DevMode 강제 false
// 비밀번호 실패/취소 ? 체크 해제 + DevMode 강제 false
_vm.DevMode = false;
cb.IsChecked = false;
}
@@ -2198,24 +2588,66 @@ public partial class SettingsWindow : Window
{
AiEnabledToggle.IsChecked = enabled;
}
// AX Agent 상세 설정은 전용 창으로 분리 (탭은 숨김)
if (AgentAiEnabledToggle != null && AgentAiEnabledToggle.IsChecked != enabled)
{
AgentAiEnabledToggle.IsChecked = enabled;
}
if (AgentTabItem != null)
AgentTabItem.Visibility = Visibility.Collapsed;
AgentTabItem.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
ApplyMainTabVisibility(NormalizeDisplayMode((System.Windows.Application.Current as App)?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel));
}
public void SelectAgentSettingsTab()
{
if (AgentTabItem == null || MainSettingsTab == null || AgentTabItem.Visibility != Visibility.Visible)
return;
MainSettingsTab.SelectedItem = AgentTabItem;
if (AgentTabCommon != null)
AgentTabCommon.IsChecked = true;
AgentSubTab_Checked(this, new RoutedEventArgs());
Activate();
}
private void BtnAgentSettingsBack_Click(object sender, RoutedEventArgs e)
{
if (MainSettingsTab == null)
return;
var fallback = MainSettingsTab.Items
.OfType<TabItem>()
.FirstOrDefault(t => t != AgentTabItem && t.Visibility == Visibility.Visible);
if (fallback != null)
MainSettingsTab.SelectedItem = fallback;
}
private void BtnAgentShortcut_Click(object sender, RoutedEventArgs e)
{
SelectAgentSettingsTab();
}
private void ApplyOperationModeState(string? mode)
{
if (OperationModeCombo == null)
var normalized = OperationModePolicy.Normalize(mode);
SyncOperationModeCombo(OperationModeCombo, normalized);
if (AgentOperationModeInternal != null) AgentOperationModeInternal.IsChecked = normalized == OperationModePolicy.InternalMode;
if (AgentOperationModeExternal != null) AgentOperationModeExternal.IsChecked = normalized == OperationModePolicy.ExternalMode;
}
private static void SyncOperationModeCombo(ComboBox? combo, string normalized)
{
if (combo == null)
return;
var normalized = OperationModePolicy.Normalize(mode);
foreach (var item in OperationModeCombo.Items.OfType<ComboBoxItem>())
foreach (var item in combo.Items.OfType<ComboBoxItem>())
{
var key = item.Tag as string;
if (string.Equals(key, normalized, StringComparison.OrdinalIgnoreCase))
{
if (!ReferenceEquals(OperationModeCombo.SelectedItem, item))
OperationModeCombo.SelectedItem = item;
if (!ReferenceEquals(combo.SelectedItem, item))
combo.SelectedItem = item;
return;
}
}
@@ -2307,7 +2739,8 @@ public partial class SettingsWindow : Window
private void AiEnabled_Changed(object sender, RoutedEventArgs e)
{
if (!IsLoaded) return;
var tryEnable = AiEnabledToggle?.IsChecked == true;
var sourceToggle = sender as CheckBox;
var tryEnable = sourceToggle?.IsChecked == true;
// 비활성화는 즉시 적용 (비밀번호 불필요)
if (!tryEnable)
@@ -2335,7 +2768,7 @@ public partial class SettingsWindow : Window
var dlg = new Window
{
Title = "AI 기능 활성화 비밀번호 확인",
Title = "AI 기능 활성화 ? 비밀번호 확인",
Width = 340, SizeToContent = SizeToContent.Height,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this, ResizeMode = ResizeMode.NoResize,
@@ -2399,8 +2832,9 @@ public partial class SettingsWindow : Window
}
else
{
// 취소/실패 토글 원상복구
// 취소/실패 ? 토글 원상복구
if (AiEnabledToggle != null) AiEnabledToggle.IsChecked = false;
if (AgentAiEnabledToggle != null) AgentAiEnabledToggle.IsChecked = false;
}
}
@@ -2421,7 +2855,7 @@ public partial class SettingsWindow : Window
return;
var ok = PromptPasswordDialog(
"운영 모드 변경 비밀번호 확인",
"운영 모드 변경 ? 비밀번호 확인",
"\U0001f512 사내/사외 모드 변경",
"비밀번호를 입력하세요:");
if (!ok)
@@ -2449,7 +2883,7 @@ public partial class SettingsWindow : Window
var dlg = new Window
{
Title = "스텝 바이 스텝 승인 비밀번호 확인",
Title = "스텝 바이 스텝 승인 ? 비밀번호 확인",
Width = 340, SizeToContent = SizeToContent.Height,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this, ResizeMode = ResizeMode.NoResize,
@@ -2983,7 +3417,7 @@ public partial class SettingsWindow : Window
if (section.Models != null && !string.IsNullOrEmpty(modelName) && !section.Models.Contains(modelName))
section.Models.Add(modelName);
}
// 2) AppSettings의 RegisteredModels (기존 저장된 것 ViewModel에 없는 경우 보완)
// 2) AppSettings의 RegisteredModels (기존 저장된 것 ? ViewModel에 없는 경우 보완)
foreach (var m in llm.RegisteredModels)
{
var svc = (m.Service ?? "").ToLowerInvariant();
@@ -3013,7 +3447,7 @@ public partial class SettingsWindow : Window
})
if (!sections[3].Models.Contains(cm)) sections[3].Models.Add(cm);
// 렌더링 모델이 없는 섹션도 헤더는 표시
// 렌더링 ? 모델이 없는 섹션도 헤더는 표시
foreach (var (service, svcLabel, svcColor, models) in sections)
{
FallbackModelsPanel.Children.Add(new TextBlock
@@ -3331,7 +3765,7 @@ public partial class SettingsWindow : Window
var result = CustomMessageBox.Show(
"설정을 불러오면 현재 설정이 덮어씌워집니다.\n계속하시겠습니까?",
"AX Copilot 설정 불러오기",
"AX Copilot ? 설정 불러오기",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
@@ -3344,7 +3778,7 @@ public partial class SettingsWindow : Window
var json = CryptoService.PortableDecrypt(fileContent);
if (string.IsNullOrEmpty(json))
{
// 평문 JSON일 수 있음 직접 파싱 시도
// 평문 JSON일 수 있음 ? 직접 파싱 시도
try
{
var test = System.Text.Json.JsonSerializer.Deserialize<Models.AppSettings>(fileContent);
@@ -3395,3 +3829,6 @@ public partial class SettingsWindow : Window
base.OnClosed(e);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,391 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0/win-x64",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/win-x64": {
"AxCopilot/0.7.3": {
"dependencies": {
"AxCopilot.SDK": "1.0.0",
"DocumentFormat.OpenXml": "3.2.0",
"Markdig": "0.37.0",
"Microsoft.Data.Sqlite": "8.0.0",
"Microsoft.Web.WebView2": "1.0.2903.40",
"System.ServiceProcess.ServiceController": "8.0.1",
"UglyToad.PdfPig": "1.7.0-custom-5",
"Microsoft.Web.WebView2.Core": "1.0.2903.40",
"Microsoft.Web.WebView2.WinForms": "1.0.2903.40",
"Microsoft.Web.WebView2.Wpf": "1.0.2903.40"
},
"runtime": {
"AxCopilot.dll": {}
}
},
"DocumentFormat.OpenXml/3.2.0": {
"dependencies": {
"DocumentFormat.OpenXml.Framework": "3.2.0"
},
"runtime": {
"lib/net8.0/DocumentFormat.OpenXml.dll": {
"assemblyVersion": "3.2.0.0",
"fileVersion": "3.2.0.0"
}
}
},
"DocumentFormat.OpenXml.Framework/3.2.0": {
"dependencies": {
"System.IO.Packaging": "8.0.1"
},
"runtime": {
"lib/net8.0/DocumentFormat.OpenXml.Framework.dll": {
"assemblyVersion": "3.2.0.0",
"fileVersion": "3.2.0.0"
}
}
},
"Markdig/0.37.0": {
"runtime": {
"lib/net8.0/Markdig.dll": {
"assemblyVersion": "0.37.0.0",
"fileVersion": "0.37.0.0"
}
}
},
"Microsoft.Data.Sqlite/8.0.0": {
"dependencies": {
"Microsoft.Data.Sqlite.Core": "8.0.0",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
}
},
"Microsoft.Data.Sqlite.Core/8.0.0": {
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
},
"runtime": {
"lib/net8.0/Microsoft.Data.Sqlite.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.23.53103"
}
}
},
"Microsoft.Web.WebView2/1.0.2903.40": {
"native": {
"runtimes/win-x64/native/WebView2Loader.dll": {
"fileVersion": "1.0.2903.40"
}
}
},
"SQLitePCLRaw.bundle_e_sqlite3/2.1.6": {
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.6",
"SQLitePCLRaw.provider.e_sqlite3": "2.1.6"
},
"runtime": {
"lib/netstandard2.0/SQLitePCLRaw.batteries_v2.dll": {
"assemblyVersion": "2.1.6.2060",
"fileVersion": "2.1.6.2060"
}
}
},
"SQLitePCLRaw.core/2.1.6": {
"runtime": {
"lib/netstandard2.0/SQLitePCLRaw.core.dll": {
"assemblyVersion": "2.1.6.2060",
"fileVersion": "2.1.6.2060"
}
}
},
"SQLitePCLRaw.lib.e_sqlite3/2.1.6": {
"native": {
"runtimes/win-x64/native/e_sqlite3.dll": {
"fileVersion": "0.0.0.0"
}
}
},
"SQLitePCLRaw.provider.e_sqlite3/2.1.6": {
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
},
"runtime": {
"lib/net6.0-windows7.0/SQLitePCLRaw.provider.e_sqlite3.dll": {
"assemblyVersion": "2.1.6.2060",
"fileVersion": "2.1.6.2060"
}
}
},
"System.Diagnostics.EventLog/8.0.1": {
"runtime": {
"runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.1024.46610"
}
}
},
"System.IO.Packaging/8.0.1": {
"runtime": {
"lib/net8.0/System.IO.Packaging.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.1024.46610"
}
}
},
"System.ServiceProcess.ServiceController/8.0.1": {
"dependencies": {
"System.Diagnostics.EventLog": "8.0.1"
},
"runtime": {
"runtimes/win/lib/net8.0/System.ServiceProcess.ServiceController.dll": {
"assemblyVersion": "8.0.0.1",
"fileVersion": "8.0.1024.46610"
}
}
},
"UglyToad.PdfPig/1.7.0-custom-5": {
"dependencies": {
"UglyToad.PdfPig.Core": "1.7.0-custom-5",
"UglyToad.PdfPig.Fonts": "1.7.0-custom-5",
"UglyToad.PdfPig.Tokenization": "1.7.0-custom-5",
"UglyToad.PdfPig.Tokens": "1.7.0-custom-5"
},
"runtime": {
"lib/net6.0/UglyToad.PdfPig.dll": {
"assemblyVersion": "0.1.8.0",
"fileVersion": "0.1.8.0"
}
}
},
"UglyToad.PdfPig.Core/1.7.0-custom-5": {
"runtime": {
"lib/net6.0/UglyToad.PdfPig.Core.dll": {
"assemblyVersion": "0.1.8.0",
"fileVersion": "0.1.8.0"
}
}
},
"UglyToad.PdfPig.Fonts/1.7.0-custom-5": {
"dependencies": {
"UglyToad.PdfPig.Core": "1.7.0-custom-5",
"UglyToad.PdfPig.Tokenization": "1.7.0-custom-5",
"UglyToad.PdfPig.Tokens": "1.7.0-custom-5"
},
"runtime": {
"lib/net6.0/UglyToad.PdfPig.Fonts.dll": {
"assemblyVersion": "0.1.8.0",
"fileVersion": "0.1.8.0"
}
}
},
"UglyToad.PdfPig.Tokenization/1.7.0-custom-5": {
"dependencies": {
"UglyToad.PdfPig.Core": "1.7.0-custom-5",
"UglyToad.PdfPig.Tokens": "1.7.0-custom-5"
},
"runtime": {
"lib/net6.0/UglyToad.PdfPig.Tokenization.dll": {
"assemblyVersion": "0.1.8.0",
"fileVersion": "0.1.8.0"
}
}
},
"UglyToad.PdfPig.Tokens/1.7.0-custom-5": {
"dependencies": {
"UglyToad.PdfPig.Core": "1.7.0-custom-5"
},
"runtime": {
"lib/net6.0/UglyToad.PdfPig.Tokens.dll": {
"assemblyVersion": "0.1.8.0",
"fileVersion": "0.1.8.0"
}
}
},
"AxCopilot.SDK/1.0.0": {
"runtime": {
"AxCopilot.SDK.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
},
"Microsoft.Web.WebView2.Core/1.0.2903.40": {
"runtime": {
"Microsoft.Web.WebView2.Core.dll": {
"assemblyVersion": "1.0.2903.40",
"fileVersion": "1.0.2903.40"
}
}
},
"Microsoft.Web.WebView2.WinForms/1.0.2903.40": {
"runtime": {
"Microsoft.Web.WebView2.WinForms.dll": {
"assemblyVersion": "1.0.2903.40",
"fileVersion": "1.0.2903.40"
}
}
},
"Microsoft.Web.WebView2.Wpf/1.0.2903.40": {
"runtime": {
"Microsoft.Web.WebView2.Wpf.dll": {
"assemblyVersion": "1.0.2903.40",
"fileVersion": "1.0.2903.40"
}
}
}
}
},
"libraries": {
"AxCopilot/0.7.3": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"DocumentFormat.OpenXml/3.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eDBT9G0sAWUvjgE8l8E5bGCFXgxCZXIecQ8dqUnj2PyxyMR5eBmLahqRRw3Q7uSKM3cKbysaL2mEY0JJbEEOEA==",
"path": "documentformat.openxml/3.2.0",
"hashPath": "documentformat.openxml.3.2.0.nupkg.sha512"
},
"DocumentFormat.OpenXml.Framework/3.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-e1neOKqRnSHUom4JQEorAoZ67aiJOp6+Xzsu0fc6IYfFcgQn6roo+w6i2w//N2u/5ilEfvLr35bNO9zaIN7r7g==",
"path": "documentformat.openxml.framework/3.2.0",
"hashPath": "documentformat.openxml.framework.3.2.0.nupkg.sha512"
},
"Markdig/0.37.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-biiu4MTPFjW55qw6v5Aphtj0MjDLJ14x8ndZwkJUHIeqvaSGKeqhLY7S7Vu/S3k7/c9KwhhnaCDP9hdFNUhcNA==",
"path": "markdig/0.37.0",
"hashPath": "markdig.0.37.0.nupkg.sha512"
},
"Microsoft.Data.Sqlite/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-H+iC5IvkCCKSNHXzL3JARvDn7VpkvuJM91KVB89sKjeTF/KX/BocNNh93ZJtX5MCQKb/z4yVKgkU2sVIq+xKfg==",
"path": "microsoft.data.sqlite/8.0.0",
"hashPath": "microsoft.data.sqlite.8.0.0.nupkg.sha512"
},
"Microsoft.Data.Sqlite.Core/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pujbzfszX7jAl7oTbHhqx7pxd9jibeyHHl8zy1gd55XMaKWjDtc5XhhNYwQnrwWYCInNdVoArbaaAvLgW7TwuA==",
"path": "microsoft.data.sqlite.core/8.0.0",
"hashPath": "microsoft.data.sqlite.core.8.0.0.nupkg.sha512"
},
"Microsoft.Web.WebView2/1.0.2903.40": {
"type": "package",
"serviceable": true,
"sha512": "sha512-THrzYAnJgE3+cNH+9Epr44XjoZoRELdVpXlWGPs6K9C9G6TqyDfVCeVAR/Er8ljLitIUX5gaSkPsy9wRhD1sgQ==",
"path": "microsoft.web.webview2/1.0.2903.40",
"hashPath": "microsoft.web.webview2.1.0.2903.40.nupkg.sha512"
},
"SQLitePCLRaw.bundle_e_sqlite3/2.1.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==",
"path": "sqlitepclraw.bundle_e_sqlite3/2.1.6",
"hashPath": "sqlitepclraw.bundle_e_sqlite3.2.1.6.nupkg.sha512"
},
"SQLitePCLRaw.core/2.1.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==",
"path": "sqlitepclraw.core/2.1.6",
"hashPath": "sqlitepclraw.core.2.1.6.nupkg.sha512"
},
"SQLitePCLRaw.lib.e_sqlite3/2.1.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==",
"path": "sqlitepclraw.lib.e_sqlite3/2.1.6",
"hashPath": "sqlitepclraw.lib.e_sqlite3.2.1.6.nupkg.sha512"
},
"SQLitePCLRaw.provider.e_sqlite3/2.1.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==",
"path": "sqlitepclraw.provider.e_sqlite3/2.1.6",
"hashPath": "sqlitepclraw.provider.e_sqlite3.2.1.6.nupkg.sha512"
},
"System.Diagnostics.EventLog/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==",
"path": "system.diagnostics.eventlog/8.0.1",
"hashPath": "system.diagnostics.eventlog.8.0.1.nupkg.sha512"
},
"System.IO.Packaging/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KYkIOAvPexQOLDxPO2g0BVoWInnQhPpkFzRqvNrNrMhVT6kqhVr0zEb6KCHlptLFukxnZrjuMVAnxK7pOGUYrw==",
"path": "system.io.packaging/8.0.1",
"hashPath": "system.io.packaging.8.0.1.nupkg.sha512"
},
"System.ServiceProcess.ServiceController/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-02I0BXo1kmMBgw03E8Hu4K6nTqur4wpQdcDZrndczPzY2fEoGvlinE35AWbyzLZ2h2IksEZ6an4tVt3hi9j1oA==",
"path": "system.serviceprocess.servicecontroller/8.0.1",
"hashPath": "system.serviceprocess.servicecontroller.8.0.1.nupkg.sha512"
},
"UglyToad.PdfPig/1.7.0-custom-5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-mddnoBg+XV5YZJg+lp/LlXQ9NY9/oV/MoNjLbbLHw0uTymfyuinVePQB4ff/ELRv3s6n0G7h8q3Ycb3KYg+hgQ==",
"path": "uglytoad.pdfpig/1.7.0-custom-5",
"hashPath": "uglytoad.pdfpig.1.7.0-custom-5.nupkg.sha512"
},
"UglyToad.PdfPig.Core/1.7.0-custom-5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bChQUAYApM6/vgBis0+fBTZyAVqjXdqshjZDCgI3dgwUplfLJxXRrnkCOdNj0a6JNcF32R4aLpnGpTc9QmmVmg==",
"path": "uglytoad.pdfpig.core/1.7.0-custom-5",
"hashPath": "uglytoad.pdfpig.core.1.7.0-custom-5.nupkg.sha512"
},
"UglyToad.PdfPig.Fonts/1.7.0-custom-5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Z6SBBAIL8wRkJNhXGYaz0CrHnNrNeuNtmwRbBtQUA1b3TDhRQppOmHCIuhjb6Vu/Rirp6FIOtzAU1lXsGik90w==",
"path": "uglytoad.pdfpig.fonts/1.7.0-custom-5",
"hashPath": "uglytoad.pdfpig.fonts.1.7.0-custom-5.nupkg.sha512"
},
"UglyToad.PdfPig.Tokenization/1.7.0-custom-5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-U8VVH7VJjv6czP7qWyzDq6CRaiJQe7/sESUCL8H3kiEa3zi0l9TonIKlD/YidQ5DlgTumracii6zjLyKPEFKwA==",
"path": "uglytoad.pdfpig.tokenization/1.7.0-custom-5",
"hashPath": "uglytoad.pdfpig.tokenization.1.7.0-custom-5.nupkg.sha512"
},
"UglyToad.PdfPig.Tokens/1.7.0-custom-5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-m/j5RVfL4eF/OwX6ASprzK+yzD3l7xdgQ7zQPgENhjxfuXD+hj6FSeZlmxSTt9ywvWcTCjGKAILl9XTK9iQgCQ==",
"path": "uglytoad.pdfpig.tokens/1.7.0-custom-5",
"hashPath": "uglytoad.pdfpig.tokens.1.7.0-custom-5.nupkg.sha512"
},
"AxCopilot.SDK/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.Web.WebView2.Core/1.0.2903.40": {
"type": "reference",
"serviceable": false,
"sha512": ""
},
"Microsoft.Web.WebView2.WinForms/1.0.2903.40": {
"type": "reference",
"serviceable": false,
"sha512": ""
},
"Microsoft.Web.WebView2.Wpf/1.0.2903.40": {
"type": "reference",
"serviceable": false,
"sha512": ""
}
}
}

View File

@@ -1,18 +0,0 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "8.0.0"
}
],
"configProperties": {
"CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false
}
}
}

View File

@@ -1,504 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Microsoft.Web.WebView2.WinForms</name>
</assembly>
<members>
<member name="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties">
<summary>
This class is a bundle of the most common parameters used to create <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> and <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/> instances.
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> in order to customize the environment and/or controller used by a <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> during implicit initialization.
</summary>
<remarks>
This class isn't intended to contain all possible environment or controller customization options.
If you need complete control over the environment and/or controller used by a WebView2 control then you'll need to initialize the control explicitly by
creating your own environment (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/>) and/or controller options (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateCoreWebView2ControllerOptions"/>) and passing them to <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/>
*before* you set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property to anything.
See the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> class documentation for an initialization overview.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.#ctor">
<summary>
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties"/> with default data for all properties.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.BrowserExecutableFolder">
<summary>
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.UserDataFolder">
<summary>
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.Language">
<summary>
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.ProfileName">
<summary>
Gets or sets the value to use for the ProfileName property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.AdditionalBrowserArguments">
<summary>
Gets or sets the value to pass as the AdditionalBrowserArguments parameter of <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions"/> which is passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.IsInPrivateModeEnabled">
<summary>
Gets or sets the value to use for the IsInPrivateModeEnabled property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateEnvironmentAsync">
<summary>
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
</summary>
<returns>A task which will provide the created environment on completion, or null if no environment-related options are set.</returns>
<remarks>
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
If some other property is changed then the next call to this method will return a different task/environment.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateCoreWebView2ControllerOptions(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Creates a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> using the current values of this instance's properties.
</summary>
<returns>A <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> object or null if no controller-related properties are set.</returns>
<exception cref="T:System.NullReferenceException">Thrown if the parameter environment is null.</exception>
</member>
<member name="T:Microsoft.Web.WebView2.WinForms.WebView2">
<summary>
Control to embed WebView2 in WinForms.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.#ctor">
<summary>
Create a new WebView2 WinForms control.
After construction the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property is <c>null</c>.
Call <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> to initialize the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
</summary>
<remarks>
This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2
You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property.
Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control.
Upon creation, the control's CoreWebView2 property will be null.
This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes.
There are two ways to cause the CoreWebView2 to be created:
1) Call the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method. This is referred to as explicit initialization.
2) Set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property. This is referred to as implicit initialization.
Either option will start initialization in the background and return back to the caller without waiting for it to finish.
To specify options regarding the initialization process, either pass your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to EnsureCoreWebView2Async or set the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property prior to initialization.
When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order:
1) The control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event.
2) If a Uri has been set to the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish).
3) The Task returned from <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> will complete.
For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation.
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
fire standard key press events such as OnKeyDown. You can suppress the
control's default implementation of an accelerator key press (e.g.
printing, in the case of Ctrl+P) by setting the Handled property of its
EventArgs to true. Also note that the underlying browser process is
blocked while these handlers execute, so:
<list type="number">
<item>
You should avoid doing a lot of work in these handlers.
</item>
<item>
Some of the WebView2 and CoreWebView2 APIs may throw errors if
invoked within these handlers due to being unable to communicate with
the browser process.
</item>
</list>
If you need to do a lot of work and/or invoke WebView2 APIs in response to
accelerator keys then consider kicking off a background task or queuing
the work for later execution on the UI thread.
</remarks>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)">
<summary>
Cleans up any resources being used.
</summary>
<param name="disposing"><c>true</c> if managed resources should be disposed; otherwise, <c>false</c>.</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnPaint(System.Windows.Forms.PaintEventArgs)">
<summary>
Overrides the base OnPaint event to have custom actions
in designer mode
</summary>
<param name="e">The graphics devices which is the source</param>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.WndProc(System.Windows.Forms.Message@)">
<summary>
Overrides the base WndProc events to handle specific window messages.
</summary>
<param name="m">The Message object containing the HWND window message and parameters</param>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties">
<summary>
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if initialization of the control's CoreWebView2 has already started.</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
<summary>
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
If you pass <c>null</c> (the default value) then a default environment will be created and used automatically.
</param>
<param name="controllerOptions">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> that should be used to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
Creating your own controller options gives you control over several options that affect how the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> is initialized.
If you pass a controllerOptions to this method then it will override any settings specified on the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property.
If you pass <c>null</c> (the default value) and no value has been set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> then a default controllerOptions will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
or on exceptions.
</returns>
<remarks>
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
and simply return a Task representing that initialization already in progress.
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
environment, or if you begin initialization with one environment and then call this method with no environment specified.
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
<para>
The following summarizes the possible error values and a description of why these errors occur.
<list type="table">
<listheader>
<description>Error Value</description>
<description>Description</description>
</listheader>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)</c></description>
<description>*\\Edge\\Application* path used in browserExecutableFolder.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_STATE)</c></description>
<description>Specified options do not match the options of the WebViews that are currently running in the shared browser process.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_WINDOW_HANDLE)</c></description>
<description>WebView2 Initialization failed due to an invalid host HWND parentWindow.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_DISK_FULL)</c></description>
<description>WebView2 Initialization failed due to reaching the maximum number of installed runtime versions.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_PRODUCT_UNINSTALLED</c></description>
<description>If the Webview depends upon an installed WebView2 Runtime version and it is uninstalled.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)</c></description>
<description>Could not find Edge installation.</description>
</item>
<item>
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)</c></description>
<description>User data folder cannot be created because a file with the same name already exists.</description>
</item>
<item>
<description><c>E_ACCESSDENIED</c></description>
<description>Unable to create user data folder, Access Denied.</description>
</item>
<item>
<description><c>E_FAIL</c></description>
<description>Edge runtime unable to start.</description>
</item>
</list>
</para>
</remarks>
<exception cref="T:System.ArgumentException">
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
</exception>
<exception cref="T:System.InvalidOperationException">
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
<summary>
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
</summary>
<param name="environment">
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
If you pass <c>null</c> then a default environment will be created and used automatically.
</param>
<returns>
A Task that represents the background initialization process.
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
or on exceptions.
</returns>
<remarks>
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
and simply return a Task representing that initialization already in progress.
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
environment, or if you begin initialization with one environment and then call this method with no environment specified.
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
</remarks>
<exception cref="T:System.ArgumentException">
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
</exception>
<exception cref="T:System.InvalidOperationException">
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
</exception>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
<summary>
This is the private function which implements the actual background initialization task.
Cannot be called if the control is already initialized or has been disposed.
</summary>
<param name="environment">
The environment to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
If that is null then a default environment is created with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and its default parameters.
</param>
<param name="controllerOptions">
The controllerOptions to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
If that is null then a default controllerOptions is created with its default parameters.
</param>
<returns>A task representing the background initialization process.</returns>
<remarks>All the event handlers added here need to be removed in <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)"/>.</remarks>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreateParams">
<summary>
Protected CreateParams property. Used to set custom window styles to the forms HWND.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnVisibleChanged(System.EventArgs)">
<summary>
Protected VisibilityChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnSizeChanged(System.EventArgs)">
<summary>
Protected SizeChanged handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Select(System.Boolean,System.Boolean)">
<summary>
Protected Select method: override this to capture tab direction when WebView control is activated
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnGotFocus(System.EventArgs)">
<summary>
Protected OnGotFocus handler.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnParentChanged(System.EventArgs)">
<summary>
Protected OnParentChanged handler.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.IsInitialized">
<summary>
True if initialization finished successfully and the control is not disposed yet.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GetSitedParentSite(System.Windows.Forms.Control)">
<summary>
Recursive retrieval of the parent control
</summary>
<param name="control">The control to get the parent for</param>
<returns>The root parent control</returns>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2">
<summary>
The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed
on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed.
You can force the underlying CoreWebView2 to
initialize via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method.
</summary>
<exception cref="T:System.InvalidOperationException">Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.</exception>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor">
<summary>
The zoom factor for the WebView.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.AllowExternalDrop">
<summary>
Enable/disable external drop.
</summary>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.Source">
<summary>
The Source property is the URI of the top level document of the
WebView2. Setting the Source is equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>.
Setting the Source will trigger initialization of the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>, if not already initialized.
The default value of Source is <c>null</c>, indicating that the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.
</summary>
<exception cref="T:System.ArgumentException">Specified value is not an absolute <see cref="T:System.Uri"/>.</exception>
<exception cref="T:System.NotImplementedException">Specified value is <c>null</c> and the control is initialized.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoForward">
<summary>
Returns true if the webview can navigate to a next page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoBack">
<summary>
Returns <c>true</c> if the webview can navigate to a previous page in the
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack"/> method.
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
</summary>
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
</member>
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.DefaultBackgroundColor">
<summary>
The default background color for the WebView.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ExecuteScriptAsync(System.String)">
<summary>
Executes the provided script in the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Reload">
<summary>
Reloads the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward">
<summary>
Navigates to the next page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack">
<summary>
Navigates to the previous page in navigation history.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.NavigateToString(System.String)">
<summary>
Renders the provided HTML as the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
</summary>
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Stop">
<summary>
Stops any in progress navigation in the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
</summary>
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted">
<summary>
This event is triggered either 1) when the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything
OR 2) the initialization failed.
You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
</summary>
<remarks>
This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is true.
Unlikely this event can fire second time (after reporting initialization success first)
if the initialization is followed by navigation which fails.
</remarks>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationStarting">
<summary>
NavigationStarting dispatches before a new navigate starts for the top
level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationCompleted">
<summary>
NavigationCompleted dispatches after a navigate of the top level
document completes rendering either successfully or not.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.WebMessageReceived">
<summary>
WebMessageReceived dispatches after web content sends a message to the
app host via <c>chrome.webview.postMessage</c>.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.SourceChanged">
<summary>
SourceChanged dispatches after the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property changes. This may happen
during a navigation or if otherwise the script in the page changes the
URI of the document.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ContentLoading">
<summary>
ContentLoading dispatches after a navigation begins to a new URI and the
content of that URI begins to render.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
</member>
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactorChanged">
<summary>
ZoomFactorChanged dispatches when the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor"/> property changes.
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/> event.
</summary>
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
</member>
<member name="F:Microsoft.Web.WebView2.WinForms.WebView2.components">
<summary>
Required designer variable.
</summary>
</member>
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitializeComponent">
<summary>
Required method for Designer support - do not modify
the contents of this method with the code editor.
</summary>
</member>
</members>
</doc>

View File

@@ -1,95 +0,0 @@
---
name: adr-writer
label: 아키텍처 결정 기록 (ADR)
description: 아키텍처 결정 사항을 표준 ADR 형식으로 문서화합니다.
icon: \uE82D
allowed-tools:
- file_read
- file_write
- folder_map
- grep
- search_codebase
tabs: code
---
아키텍처 결정 사항을 ADR(Architecture Decision Record) 형식으로 문서화하세요.
## ADR이란?
소프트웨어 아키텍처에서 내린 중요한 결정의 배경, 대안, 근거를 기록하는 경량 문서입니다.
미래의 팀원이 "왜 이렇게 결정했는지"를 이해할 수 있게 합니다.
## 워크플로우
1. **결정 사항 확인**: 사용자에게 다음을 파악
- 어떤 결정을 내렸는가 (또는 내려야 하는가)
- 관련 코드/시스템 영역
- 고려한 대안들
2. **코드 분석** (선택): 관련 코드 구조를 읽어 현재 상태 파악
3. **ADR 작성**: 표준 형식으로 문서 생성
4. **파일 저장**: `docs/adr/` 폴더에 번호 형식으로 저장
## ADR 표준 형식
```markdown
# ADR-[번호]: [결정 제목]
**상태**: 제안됨 | 승인됨 | 폐기됨 | 대체됨
**날짜**: YYYY-MM-DD
**결정자**: [이름/팀]
## 맥락 (Context)
어떤 상황에서 이 결정이 필요한가?
- 기술적 배경
- 비즈니스 요구사항
- 제약 조건
## 결정 (Decision)
무엇을 결정했는가?
- 선택한 방안의 구체적 내용
- 적용 범위
## 대안 (Alternatives)
### 대안 1: [이름]
- 장점: ...
- 단점: ...
- 비용/복잡도: ...
### 대안 2: [이름]
- 장점: ...
- 단점: ...
- 비용/복잡도: ...
## 근거 (Rationale)
왜 이 결정을 선택했는가?
- 대안 대비 장점
- 트레이드오프 분석
- 참고 자료/벤치마크
## 결과 (Consequences)
### 긍정적
- ...
### 부정적
- ...
### 리스크
- ...
## 관련 문서
- ADR-[관련번호]: [제목]
- [외부 참고 링크]
```
## 파일 명명 규칙
- 위치: `docs/adr/` (없으면 생성)
- 파일명: `ADR-NNNN-제목-요약.md` (예: `ADR-0001-데이터베이스-선택.md`)
- 번호: 기존 ADR 파일 조회 후 자동 부여
## 규칙
- 결정의 "왜"를 중심으로 작성 (코드를 읽으면 "무엇"은 알 수 있음)
- 대안은 최소 2개 이상 제시
- 트레이드오프를 솔직하게 기록 (완벽한 선택은 없음)
- 짧고 명확하게 (1~2페이지 이내)
- 한국어로 작성

View File

@@ -1,59 +0,0 @@
---
name: api-docs
label: API 문서 생성
description: 코드에서 API 엔드포인트를 분석하여 마크다운/HTML API 문서를 자동 생성합니다.
icon: \uE8A1
allowed-tools:
- folder_map
- grep
- file_read
- file_write
- search_codebase
tabs: code
---
작업 폴더의 소스 코드를 분석하여 API 문서를 생성하세요.
## 분석 대상
- REST API 엔드포인트 (Controller, Route 어노테이션)
- 함수/메서드 시그니처 및 주석
- 요청/응답 모델 (DTO, Schema)
- 인증/권한 요구사항
## 작업 절차
1. `folder_map` — 프로젝트 구조 파악
2. `grep` — API 엔드포인트 패턴 검색 (`[HttpGet]`, `@GetMapping`, `router.get`, `@app.route` 등)
3. `file_read` — 컨트롤러/라우터 파일 분석
4. `grep` — 요청/응답 모델 클래스 검색
5. `file_read` — 모델 구조 분석
6. `file_write` — API 문서 생성
## 출력 형식
마크다운으로 작성하되 다음 구조를 따르세요:
```
# API 문서
## 개요
- Base URL, 인증 방식, 공통 헤더
## 엔드포인트
### [POST] /api/users
- **설명**: 사용자 생성
- **인증**: Bearer Token 필요
- **요청 본문**:
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| name | string | ✓ | 사용자 이름 |
- **응답**: 201 Created
```json
{ "id": 1, "name": "..." }
```
- **에러 코드**: 400, 401, 409
```
## 주의사항
- 코드에서 실제 확인된 내용만 문서화하세요. 추측하지 마세요.
- 주석이나 Swagger/OpenAPI 어노테이션이 있으면 우선 활용하세요.
- 인증, 페이징, 에러 처리 등 공통 패턴은 별도 섹션으로 정리하세요.

View File

@@ -1,70 +0,0 @@
---
name: batch-rename
label: 파일 일괄 이름 변경
description: 패턴 매칭, 번호 붙이기, 날짜 추가 등 파일 이름을 일괄 변경합니다.
icon: \uE8AC
tabs: cowork
allowed-tools:
- folder_map
- file_read
- file_manage
- clipboard_tool
---
파일 이름을 규칙에 따라 일괄 변경하세요. 변경 전 반드시 미리보기를 제공합니다.
## 작업 절차
1. **대상 파일 스캔**: folder_map으로 작업 폴더의 파일 목록을 수집
2. **이름 변경 규칙 확인**: 사용자에게 다음 옵션을 확인
- 대상 파일 필터 (확장자, 이름 패턴)
- 변경 규칙 (아래 지원 규칙 참조)
- 적용 순서 (이름순, 날짜순, 크기순)
3. **변경 미리보기 생성**: 변경 전/후 이름을 표로 표시
```
| # | 현재 이름 | 변경 후 이름 |
|---|----------------------|----------------------|
| 1 | IMG_20260101_001.jpg | 2026-01-01_001.jpg |
| 2 | IMG_20260101_002.jpg | 2026-01-01_002.jpg |
```
4. **사용자 확인**: 미리보기를 보여주고 진행 여부를 확인
5. **일괄 변경 실행**: file_manage로 파일 이름을 순차 변경
6. **결과 보고**: 변경 성공/실패 건수와 상세 내역을 안내
## 지원 이름 변경 규칙
### 패턴 치환
- **문자열 치환**: "IMG_" → "사진_"
- **정규식 치환**: `(\d{4})(\d{2})(\d{2})` → `$1-$2-$3`
- **대소문자 변환**: 소문자, 대문자, 타이틀 케이스
### 번호 붙이기
- **순번 추가**: `문서_001.pdf`, `문서_002.pdf`, ...
- **시작 번호**: 사용자 지정 (기본: 1)
- **자릿수**: 자동 계산 (파일 수 기준)
- **위치**: 접두사 또는 접미사
### 날짜 추가
- **오늘 날짜**: `보고서_2026-03-30.docx`
- **파일 수정일**: 파일의 실제 수정 날짜 사용
- **날짜 형식**: YYYY-MM-DD, YYYYMMDD, YY.MM.DD
### 정리
- **공백 처리**: 공백 → 언더스코어/하이픈
- **특수문자 제거**: 파일명에서 특수문자 제거
- **확장자 변경**: `.jpeg` → `.jpg`
- **접두사/접미사 추가 또는 제거**
## 충돌 처리
- 변경 후 이름이 이미 존재하면 자동으로 번호 추가 (`_1`, `_2`)
- 충돌 건은 미리보기에서 ⚠️ 표시로 경고
- 원본 파일 덮어쓰기 절대 금지
## 규칙
- **미리보기 없이 직접 변경하지 않음** — 반드시 미리보기 후 사용자 확인
- 하위 폴더 포함 여부는 사용자에게 확인
- 숨김 파일(.으로 시작)은 기본 제외
- 변경 실패 시 이미 변경된 파일은 원복하지 않으므로, 중요한 경우 백업 권장
- 한 번에 1,000개 이상의 파일 변경 시 경고
한국어로 안내하세요.

View File

@@ -1,74 +0,0 @@
---
name: changelog
label: 변경 이력 / 릴리즈 노트
description: Git 커밋 이력에서 자동으로 변경 이력과 릴리즈 노트를 생성합니다.
icon: \uE81C
allowed-tools:
- git_tool
- file_read
- file_write
- html_create
- text_summarize
tabs: code
---
Git 커밋 이력을 분석하여 변경 이력(CHANGELOG) 또는 릴리즈 노트를 생성하세요.
## 워크플로우
1. **이력 조회**: git_tool로 커밋 로그 수집
- 지정 기간 또는 태그 간 커밋
- 커밋 메시지 + 변경 파일 목록
2. **분류**: 커밋을 Conventional Commits 기준으로 분류
3. **Breaking Change 감지**: 시그니처 변경, API 삭제 등 감지
4. **문서 생성**: Markdown 또는 HTML로 출력
## Conventional Commits 분류
| 접두사 | 분류 | 설명 |
|--------|------|------|
| feat | ✨ 신기능 | 새로운 기능 추가 |
| fix | 🐛 버그 수정 | 버그 수정 |
| docs | 📝 문서 | 문서 변경 |
| style | 💄 스타일 | 코드 포맷팅 (동작 변경 없음) |
| refactor | ♻️ 리팩토링 | 코드 리팩토링 |
| perf | ⚡ 성능 | 성능 개선 |
| test | ✅ 테스트 | 테스트 추가/수정 |
| chore | 🔧 기타 | 빌드, 설정 변경 |
| BREAKING | 💥 Breaking | 하위 호환성 깨지는 변경 |
## 출력 형식
### CHANGELOG.md
```markdown
# Changelog
## [1.6.0] - 2026-03-30
### ✨ 신기능
- 멀티패스 문서 생성 엔진 (#123)
- PPT 네이티브 생성 도구
### 🐛 버그 수정
- 탭 전환 시 대화 유실 문제 해결
### 💥 Breaking Changes
- 없음
### 📝 문서
- 개발자 가이드 v1.6.0 업데이트
```
### 릴리즈 노트 (HTML)
사용자 친화적인 형식:
- 주요 변경사항 (스크린샷 포함 가능)
- 개선 사항
- 알려진 이슈
- 업그레이드 가이드
## 규칙
- Conventional Commits 형식이 아닌 커밋도 내용 분석으로 분류
- 중복/사소한 커밋은 병합하여 요약
- Breaking Change는 반드시 별도 섹션으로 강조
- 이슈 번호가 있으면 링크 포함
- 한국어로 작성

View File

@@ -1,37 +0,0 @@
---
name: code-scaffold
label: 코드 스캐폴딩
description: 프로젝트 구조를 분석하고 새 기능의 코드 뼈대를 자동 생성합니다.
icon: \uE943
allowed-tools:
- folder_map
- file_read
- grep
- file_write
- search_codebase
tabs: code
---
작업 폴더의 프로젝트 구조를 분석하고 새 기능의 코드 뼈대를 생성하세요.
다음 도구를 사용하세요:
1. folder_map — 프로젝트 구조 파악
2. file_read — 기존 코드 패턴 분석
3. grep — 코딩 컨벤션 확인
4. file_write — 새 파일 생성
작업 순서:
1. 프로젝트 타입 감지 (언어, 프레임워크, 빌드 시스템)
2. 기존 코드 패턴 분석 (네이밍, 폴더 구조, 임포트 스타일)
3. 사용자 요청에 맞는 코드 뼈대 생성
생성 항목:
- 클래스/모듈 파일 (프로젝트 컨벤션에 맞춰)
- 인터페이스/타입 정의
- 단위 테스트 파일
- 필요한 설정/구성 파일
규칙:
- 기존 프로젝트의 코딩 스타일을 따르세요
- TODO 주석으로 구현이 필요한 부분을 표시하세요
- 한국어 주석을 추가하세요

View File

@@ -1,72 +0,0 @@
---
name: commit-review
label: 커밋 메시지 리뷰
description: Git 커밋 메시지를 Conventional Commits 기준으로 검토하고 개선을 제안합니다.
icon: \uE8CB
allowed-tools:
- process
- git_tool
- file_read
- text_summarize
tabs: code
---
작업 폴더의 최근 Git 커밋 메시지를 검토하고 개선안을 제시하세요.
## 작업 절차
1. `process``git log --oneline -20` 으로 최근 커밋 목록 확인
2. `process``git log --format="%H%n%s%n%b%n---" -10` 으로 상세 메시지 확인
3. `process``git diff HEAD~1` 으로 최신 커밋 변경 내용 확인 (필요 시)
4. 각 커밋을 Conventional Commits 기준으로 분석
5. 결과를 정리하여 출력
## Conventional Commits 규칙
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
### type 종류
- `feat`: 새 기능
- `fix`: 버그 수정
- `docs`: 문서 변경
- `style`: 포맷팅 (코드 동작 변경 없음)
- `refactor`: 리팩토링
- `perf`: 성능 개선
- `test`: 테스트 추가/수정
- `chore`: 빌드/도구 설정
- `ci`: CI 설정 변경
## 검토 항목
1. **type 적절성**: 변경 내용과 type이 일치하는가
2. **설명 품질**: 50자 이내, 명령형, 명확한 내용
3. **본문 유무**: 복잡한 변경에 "왜" 설명이 있는가
4. **일관성**: 팀 내 커밋 스타일이 통일되어 있는가
5. **Breaking Change**: BREAKING CHANGE 푸터 또는 ! 표기
## 출력 형식
```
## 커밋 메시지 리뷰 결과
### 전체 요약
- 검토 커밋: N개
- 규칙 준수: N개 ✓ / 위반: N개 ✗
### 개별 리뷰
#### abc1234 "Fix login bug"
- ❌ type 없음 → `fix: resolve login authentication failure`
- ❌ 본문 없음 → 원인과 해결 방법 추가 권장
#### def5678 "feat: add user profile page"
- ✅ Conventional Commits 준수
- 💡 scope 추가 권장: `feat(profile): add user profile page`
```
## 주의사항
- 비판이 아닌 건설적 제안을 하세요.
- 팀의 기존 컨벤션이 있으면 그것을 우선 존중하세요.
- 개선된 메시지 예시를 항상 함께 제시하세요.

View File

@@ -1,51 +0,0 @@
---
name: compare
label: 비교 분석표
description: 2개 이상 항목의 비교 분석 매트릭스를 생성합니다.
icon: \uE9D5
allowed-tools:
- html_create
- excel_create
- file_read
- document_read
- chart_create
tabs: all
---
사용자가 요청한 항목들을 체계적으로 비교 분석하는 매트릭스를 생성하세요.
## 워크플로우
1. **비교 대상 확인**: 어떤 항목들을 비교할지 파악
- 제품, 기술, 방안, 서비스, 도구, 프레임워크 등
2. **비교 기준 설정**: 적절한 비교 축을 설계
- 기능, 가격, 성능, 사용성, 확장성, 지원, 보안 등
3. **데이터 수집**: 참고 파일이 있으면 읽어서 반영
4. **분석표 생성**: HTML 또는 Excel로 비교 매트릭스 생성
5. **종합 평가**: 총평 + 추천 의견 제시
## 비교표 구성
### 기본 매트릭스
| 기준 | 항목 A | 항목 B | 항목 C |
|------|--------|--------|--------|
| 기능1 | ✅ 지원 | ⚠ 일부 | ❌ 미지원 |
| 기능2 | ... | ... | ... |
### 점수 비교 (radar 차트 활용)
- 각 항목을 1~10점으로 정량화
- chart_create로 레이더 차트 또는 바 차트 시각화
### 종합 평가
- 장점/단점 요약
- 상황별 추천 (예: "예산이 제한적이면 A, 확장성이 중요하면 B")
## 출력 형식
- **HTML** (권장): 색상 배지, 차트 포함 비주얼 보고서
- **Excel**: 정량 데이터 + 수식 기반 점수표
## 규칙
- 객관적 사실 기반으로 비교 (주관적 판단은 별도 섹션)
- 각 항목의 강점과 약점을 균형 있게 서술
- 출처/근거가 있으면 명시
- 한국어로 작성

View File

@@ -1,137 +0,0 @@
---
name: csv-to-xlsx
label: CSV → Excel 변환
description: CSV 파일을 서식이 완성된 Excel(.xlsx)로 변환합니다. 헤더 고정, 필터, 조건부 서식, 자동 열 너비를 적용합니다.
icon: \uE9F9
allowed-tools:
- folder_map
- file_read
- file_write
- process
- format_convert
- data_pivot
- template_render
tabs: cowork
---
CSV 파일을 전문적인 서식이 적용된 Excel 파일로 변환하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 Python 스크립트 경로로 변환/서식 자동화를 수행하세요.
- Python 불가: `format_convert`로 CSV를 XLSX로 변환하고, `data_pivot`으로 핵심 요약 시트를 구성한 뒤 `file_write`로 사용 안내를 남기세요.
## 사전 준비
먼저 필요한 패키지가 설치되어 있는지 확인하고, 없으면 설치하세요:
```
process: pip install openpyxl pandas
```
## 작업 절차
1. **파일 확인**: folder_map으로 작업 폴더에서 CSV 파일을 탐색
2. **CSV 분석**: file_read로 CSV 파일의 구조(컬럼, 행 수, 인코딩, 구분자) 파악
3. **변환 옵션 확인**: 사용자에게 다음 옵션을 확인
- 헤더 행 고정 여부 (기본: 활성)
- 자동 필터 적용 여부 (기본: 활성)
- 조건부 서식 대상 컬럼 (숫자 컬럼 자동 감지)
- 시트 이름 (기본: 파일명)
4. **Python 스크립트 작성**: file_write로 변환 스크립트 생성
5. **스크립트 실행**: `process`로 Python 스크립트 실행
6. **결과 확인**: 생성된 .xlsx 파일 경로와 요약 정보를 안내
## Python 스크립트 템플릿
```python
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.formatting.rule import CellIsRule
# CSV 읽기 (인코딩 자동 감지)
for enc in ['utf-8', 'cp949', 'euc-kr', 'utf-8-sig']:
try:
df = pd.read_csv('input.csv', encoding=enc)
break
except (UnicodeDecodeError, Exception):
continue
# Excel 저장
output_path = 'output.xlsx'
df.to_excel(output_path, index=False, sheet_name='Sheet1')
# 서식 적용
wb = load_workbook(output_path)
ws = wb.active
# 헤더 스타일
header_font = Font(bold=True, color='FFFFFF', size=11)
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_align = Alignment(horizontal='center', vertical='center', wrap_text=True)
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
for col_idx, cell in enumerate(ws[1], 1):
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_align
cell.border = thin_border
# 자동 열 너비
for col_idx in range(1, ws.max_column + 1):
max_length = 0
col_letter = get_column_letter(col_idx)
for row in ws.iter_rows(min_col=col_idx, max_col=col_idx):
for cell in row:
if cell.value:
max_length = max(max_length, len(str(cell.value)))
ws.column_dimensions[col_letter].width = min(max_length + 4, 50)
# 헤더 행 고정 (Freeze Panes)
ws.freeze_panes = 'A2'
# 자동 필터
ws.auto_filter.ref = ws.dimensions
# 숫자 컬럼 조건부 서식 (음수 빨강)
for col_idx in range(1, ws.max_column + 1):
col_letter = get_column_letter(col_idx)
sample_values = [ws.cell(row=r, column=col_idx).value for r in range(2, min(ws.max_row + 1, 12))]
if any(isinstance(v, (int, float)) for v in sample_values if v is not None):
cell_range = f'{col_letter}2:{col_letter}{ws.max_row}'
ws.conditional_formatting.add(cell_range,
CellIsRule(operator='lessThan', formula=['0'],
font=Font(color='FF0000')))
# 데이터 행 줄무늬 (가독성)
light_fill = PatternFill(start_color='D9E2F3', end_color='D9E2F3', fill_type='solid')
for row_idx in range(2, ws.max_row + 1):
for col_idx in range(1, ws.max_column + 1):
cell = ws.cell(row=row_idx, column=col_idx)
cell.border = thin_border
if row_idx % 2 == 0:
cell.fill = light_fill
wb.save(output_path)
print(f'변환 완료: {output_path} ({ws.max_row - 1}× {ws.max_column}열)')
```
## 서식 옵션
- **헤더 스타일**: 파란 배경 + 흰색 굵은 글씨 + 가운데 정렬
- **줄무늬**: 짝수 행 연한 파랑 배경 (가독성 향상)
- **열 너비**: 내용 기준 자동 조정 (최대 50)
- **조건부 서식**: 숫자 컬럼 음수 빨강 표시
- **Freeze Panes**: 헤더 행 고정
- **Auto Filter**: 전체 컬럼 필터 활성화
## 규칙
- 원본 CSV 파일은 수정하지 않음
- 인코딩 자동 감지 (UTF-8 → CP949 → EUC-KR 순)
- 대용량 파일 (100,000행 이상) 경고 후 진행
- 출력 파일명: 원본 파일명 기준 (.csv → .xlsx)
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,56 +0,0 @@
---
name: data-convert
label: 데이터 변환기
description: 데이터 포맷 간 변환, 정제, 필터링, 통계 요약을 수행합니다.
icon: \uE8AB
allowed-tools:
- file_read
- file_write
- json_tool
- csv_create
- data_pivot
- excel_create
tabs: cowork
---
데이터 파일의 포맷 변환, 정제, 필터링, 통계 요약을 수행하세요.
## 지원 변환
| 입력 → | JSON | CSV | Excel | Markdown |
|--------|------|-----|-------|----------|
| JSON | — | ✅ | ✅ | ✅ |
| CSV | ✅ | — | ✅ | ✅ |
| Excel | ✅ | ✅ | — | ✅ |
| TSV | ✅ | ✅ | ✅ | ✅ |
## 워크플로우
1. **파일 분석**: 입력 파일의 포맷, 인코딩, 구조 파악
2. **데이터 정제** (선택):
- 빈 행/열 제거
- 중복 제거
- 데이터 타입 정리 (숫자 문자열 → 숫자)
- 결측치 처리 (제거 또는 기본값)
3. **필터링** (선택):
- 조건 기반 행 필터링
- 필요 컬럼만 추출
4. **변환**: 대상 포맷으로 변환
5. **통계 요약**: 기본 통계 제공
## 정제 옵션
- `remove_empty`: 빈 행 제거
- `remove_duplicates`: 중복 행 제거
- `trim`: 공백 제거
- `fill_na`: 결측치 채우기 (값 지정)
## 출력
- 변환된 파일 저장
- 변환 통계 (원본 행 수, 변환 후 행 수, 제거 행 수)
- 컬럼별 기본 통계 (수치 컬럼: 합계, 평균, 최소, 최대)
## 규칙
- 원본 파일은 수정하지 않음 (새 파일로 저장)
- 인코딩: UTF-8 기본 (EUC-KR 옵션)
- 대용량 파일 (10MB 이상) 경고
- 한국어로 안내

View File

@@ -1,137 +0,0 @@
---
name: data-visualize-adv
label: 고급 데이터 시각화
description: Python matplotlib/seaborn을 사용하여 히트맵, 산점도, 상관관계 등 고급 시각화를 생성합니다.
icon: \uE9D9
allowed-tools:
- folder_map
- file_read
- file_write
- process
- data_pivot
- chart_create
- template_render
tabs: cowork
---
데이터를 고급 시각화 차트로 변환하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 matplotlib/seaborn 경로를 사용하세요.
- Python 불가: `data_pivot`으로 통계를 계산하고 `chart_create` + `template_render` + `file_write`로 HTML/SVG 리포트를 생성하세요.
## 사전 준비
필요한 패키지를 설치하세요:
```
process: pip install matplotlib seaborn pandas numpy
```
## 작업 절차
1. **데이터 확인**: 사용자가 제공한 CSV/JSON/Excel 데이터 파일 확인
2. **데이터 로드**: pandas로 데이터 읽기
3. **Python 스크립트 작성**: file_write로 시각화 스크립트 생성
4. **스크립트 실행**: `process`로 실행
5. **결과 확인**: 생성된 차트 이미지 경로를 사용자에게 안내
## 시각화 유형별 템플릿
### 공통 설정
```python
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style='whitegrid', font='Malgun Gothic')
```
### 히트맵 (상관관계 행렬)
```python
df = pd.read_csv('data.csv')
corr = df.select_dtypes(include=[np.number]).corr()
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(corr, annot=True, fmt='.2f', cmap='RdBu_r',
center=0, square=True, linewidths=0.5, ax=ax)
ax.set_title('상관관계 히트맵')
plt.tight_layout()
plt.savefig('heatmap.png', dpi=150)
```
### 산점도 (Scatter Plot)
```python
df = pd.read_csv('data.csv')
fig, ax = plt.subplots(figsize=(10, 8))
sns.scatterplot(data=df, x='col_x', y='col_y', hue='category',
size='value', sizes=(20, 200), alpha=0.7, ax=ax)
ax.set_title('산점도')
plt.tight_layout()
plt.savefig('scatter.png', dpi=150)
```
### 시계열 분석
```python
df = pd.read_csv('data.csv', parse_dates=['date'])
fig, ax = plt.subplots(figsize=(12, 6))
sns.lineplot(data=df, x='date', y='value', hue='category', ax=ax)
ax.set_title('시계열 트렌드')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('timeseries.png', dpi=150)
```
### 분포 비교 (박스플롯 + 바이올린)
```python
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
sns.boxplot(data=df, x='group', y='value', ax=axes[0])
axes[0].set_title('박스플롯')
sns.violinplot(data=df, x='group', y='value', ax=axes[1])
axes[1].set_title('바이올린 플롯')
plt.tight_layout()
plt.savefig('distribution.png', dpi=150)
```
### 다중 차트 대시보드
```python
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
# 좌상: 히트맵
sns.heatmap(corr, annot=True, fmt='.1f', ax=axes[0,0])
# 우상: 산점도
sns.scatterplot(data=df, x='x', y='y', ax=axes[0,1])
# 좌하: 히스토그램
sns.histplot(data=df, x='value', kde=True, ax=axes[1,0])
# 우하: 박스플롯
sns.boxplot(data=df, x='group', y='value', ax=axes[1,1])
fig.suptitle('데이터 분석 대시보드', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.savefig('dashboard.png', dpi=150)
```
### 페어플롯 (변수 간 관계 전체)
```python
g = sns.pairplot(df, hue='category', diag_kind='kde')
g.fig.suptitle('변수 간 관계', y=1.02)
plt.savefig('pairplot.png', dpi=150)
```
## 스타일 옵션
- seaborn 테마: `whitegrid`, `darkgrid`, `white`, `dark`, `ticks`
- 컬러 팔레트: `Set2`, `husl`, `coolwarm`, `RdBu_r`, `viridis`
- 출력 형식: png, svg, pdf
- 해상도: `dpi=150` (기본), `dpi=300` (인쇄용)
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,45 +0,0 @@
---
name: data-visualize
label: 데이터 시각화
description: CSV/Excel 데이터를 분석하여 차트가 포함된 HTML 보고서를 생성합니다.
icon: \uE9D9
allowed-tools:
- folder_map
- file_read
- file_write
- data_pivot
- chart_create
- template_render
tabs: cowork
---
작업 폴더의 데이터 파일을 분석하고 시각화 보고서를 생성하세요.
다음 도구를 사용하세요:
1. folder_map — 데이터 파일 탐색
2. file_read — CSV/Excel 데이터 읽기
3. file_write — HTML 시각화 보고서 생성
<EFBFBD><EFBFBD><EFBFBD>각화 전략:
1. **데이터 파악**: 컬럼 타입, 결측치, 기본 통계량 확인
2. **적절한 차트 선택**:
- 시계열 → 라인 차트
- 비교 → 바 차트
- 비율 → 파이/도넛 차트
- 분포 → 히스토그램
- 상관관계 → 산점도
3. **HTML 보고서 생성**: 인라인 SVG 또는 CSS 기반 차트 (외부 라이브러리 없이)
보고서 구성:
## 데이터 요약
- 기본 통계 테이블
## 시각화
- 데이터 특성에 맞는 2~4개 차트
- 각 차트에 대한 해석
## 인사이트
- 데이터에서 발견한 주요 패턴
- 이상치 또는 주목할 포인트
한국어로 작성하세요. 차트는 CSS/SVG 기반으로 외부 의존성 없이 생성하세요.

View File

@@ -1,66 +0,0 @@
---
name: db-schema
label: DB 스키마 분석
description: 데이터베이스 스키마를 분석하여 ERD 다이어그램과 테이블 문서를 생성합니다.
icon: \uE968
allowed-tools:
- folder_map
- grep
- file_read
- file_write
- search_codebase
tabs: code
---
작업 폴더의 코드에서 데이터베이스 스키마를 분석하고 문서화하세요.
## 분석 대상
- ORM 모델/엔티티 클래스 (Entity Framework, SQLAlchemy, Sequelize, JPA 등)
- 마이그레이션 파일
- SQL DDL 스크립트 (CREATE TABLE)
- 관계 정의 (FK, Navigation Property)
## 작업 절차
1. `folder_map` — 프로젝트 구조 파악
2. `grep` — 엔티티/모델 클래스 검색 (`DbSet`, `@Entity`, `Model.define`, `CREATE TABLE` 등)
3. `file_read` — 모델 파일 분석 (컬럼, 타입, 관계)
4. `grep` — 인덱스, 제약 조건 검색
5. `file_write` — 스키마 문서 + Mermaid ERD 생성
## 출력 형식
### 테이블 문서
각 테이블에 대해:
```
## Users 테이블
| 컬럼 | 타입 | Null | 기본값 | 설명 |
|------|------|------|--------|------|
| Id | int | NO | AUTO_INCREMENT | PK |
| Name | nvarchar(100) | NO | - | 사용자 이름 |
| CreatedAt | datetime | NO | GETDATE() | 생성일 |
- **인덱스**: IX_Users_Name (Name)
- **관계**: Orders (1:N), Profile (1:1)
```
### Mermaid ERD
```mermaid
erDiagram
Users ||--o{ Orders : "has"
Users ||--|| Profile : "has"
Users {
int Id PK
string Name
datetime CreatedAt
}
Orders {
int Id PK
int UserId FK
decimal Amount
}
```
## 주의사항
- 코드에서 실제 확인된 스키마만 문서화하세요.
- 관계(1:1, 1:N, N:M)를 정확히 파악하세요.
- 마이그레이션이 있으면 최종 상태를 기준으로 작성하세요.

View File

@@ -1,85 +0,0 @@
---
name: dependency-audit
label: 의존성 분석
description: 프로젝트 의존성 그래프를 분석하고 보안 취약점, 라이선스, 업데이트 현황을 보고합니다.
icon: \uE964
allowed-tools:
- file_read
- grep
- folder_map
- glob
- html_create
- process
tabs: code
---
프로젝트의 패키지 의존성을 분석하여 보안, 라이선스, 업데이트 보고서를 생성하세요.
## 워크플로우
1. **패키지 파일 탐지**: glob으로 의존성 파일 검색
- .NET: `*.csproj`, `packages.config`, `Directory.Build.props`
- Node: `package.json`, `package-lock.json`, `yarn.lock`
- Python: `requirements.txt`, `Pipfile`, `pyproject.toml`
- Java: `pom.xml`, `build.gradle`
2. **의존성 목록 추출**: file_read로 파일 파싱
3. **분석 수행**: 각 의존성에 대해 검사
4. **보고서 생성**: html_create로 분석 보고서 생성
## 분석 항목
### 1. 직접 의존성 목록
| 패키지 | 현재 버전 | 최신 버전 | 업데이트 필요 |
|--------|----------|----------|-------------|
| ... | ... | ... | ✅/⚠/❌ |
### 2. 보안 취약점 (알려진 패턴)
- 알려진 취약 버전 패턴 탐지
- 폐기된(deprecated) 패키지 식별
- 유지보수 중단된 패키지 경고
### 3. 라이선스 검사
| 라이선스 | 호환성 | 패키지 |
|---------|--------|--------|
| MIT | ✅ 허용 | lib-a, lib-b |
| GPL-3.0 | ⚠ 주의 | lib-c |
| 상용 | ❌ 검토 필요 | lib-d |
### 4. 의존성 크기 분석
- 패키지별 예상 크기
- 전체 node_modules / NuGet 캐시 크기
- 불필요하게 큰 패키지 식별
### 5. 중복/충돌 검사
- 동일 기능 중복 패키지 (예: lodash + underscore)
- 버전 충돌 가능성
## 프레임워크별 검사 명령
### .NET
```
dotnet list package --outdated
dotnet list package --vulnerable
```
### Node.js
```
npm audit
npm outdated
```
### Python
```
pip list --outdated
pip-audit
```
## 출력
- HTML 보고서: 위험도별 색상 구분, 차트 포함
- 요약: 총 패키지 수, 업데이트 필요 수, 보안 이슈 수, 라이선스 경고 수
## 규칙
- 외부 서버 접속 없이 로컬 파일 분석만 수행
- process 도구 사용 시 `dotnet list` / `npm audit` 등 읽기 전용 명령만
- 패키지를 직접 업데이트하지 않음 (보고서만)
- 한국어로 작성

View File

@@ -1,109 +0,0 @@
---
name: diagram-generator
label: 다이어그램 생성
description: Python matplotlib/graphviz를 사용하여 플로차트, 시퀀스, ER 다이어그램 등을 생성합니다.
icon: \uE9D9
allowed-tools:
- file_read
- file_write
- process
- chart_create
- template_render
tabs: cowork
---
사용자의 요구에 맞는 다이어그램을 Python으로 생성하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 Graphviz/matplotlib 경로를 사용하세요.
- Python 불가: `template_render``file_write`로 Mermaid 기반 다이어그램 문서를 생성하고, 필요 시 `chart_create`로 대체 시각화를 제공하세요.
## 사전 준비
필요한 패키지를 설치하세요:
```
process: pip install matplotlib graphviz
```
시스템에 Graphviz가 설치되어 있어야 합니다 (https://graphviz.org/download/).
## 작업 절차
1. **요구사항 파악**: 다이어그램 유형, 노드/관계, 스타일 확인
2. **Python 스크립트 작성**: file_write로 .py 파일 생성
3. **스크립트 실행**: `process`로 Python 스크립트 실행
4. **결과 확인**: 생성된 이미지 파일 경로를 사용자에게 안내
## 다이어그램 유형별 템플릿
### 플로차트 (Graphviz)
```python
from graphviz import Digraph
dot = Digraph(comment='Flowchart', format='png')
dot.attr(rankdir='TB', fontname='Malgun Gothic')
dot.attr('node', shape='box', style='rounded,filled', fillcolor='#E8F0FE')
dot.node('start', '시작', shape='ellipse', fillcolor='#34A853', fontcolor='white')
dot.node('process1', '데이터 수집')
dot.node('decision', '조건 확인?', shape='diamond', fillcolor='#FBBC04')
dot.node('process2', '처리')
dot.node('end', '종료', shape='ellipse', fillcolor='#EA4335', fontcolor='white')
dot.edge('start', 'process1')
dot.edge('process1', 'decision')
dot.edge('decision', 'process2', label='')
dot.edge('decision', 'end', label='아니오')
dot.edge('process2', 'end')
dot.render('flowchart', cleanup=True)
```
### 시퀀스 다이어그램 (matplotlib)
```python
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig, ax = plt.subplots(1, 1, figsize=(10, 8))
# 액터 라이프라인, 메시지 화살표 등을 matplotlib으로 직접 그리기
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.invert_yaxis()
ax.axis('off')
plt.savefig('sequence.png', dpi=150, bbox_inches='tight')
```
### ER 다이어그램 (Graphviz)
```python
from graphviz import Graph
er = Graph('ER', format='png', engine='neato')
er.attr('node', shape='box', style='filled', fillcolor='#E8F0FE')
er.node('user', 'User\n─────\nid (PK)\nname\nemail')
er.node('order', 'Order\n─────\nid (PK)\nuser_id (FK)\ntotal')
er.edge('user', 'order', label='1:N')
er.render('er_diagram', cleanup=True)
```
### 조직도 (Graphviz)
```python
from graphviz import Digraph
org = Digraph(format='png')
org.attr(rankdir='TB')
org.attr('node', shape='box', style='rounded,filled', fillcolor='#E3F2FD')
org.node('ceo', 'CEO')
org.node('cto', 'CTO')
org.node('cfo', 'CFO')
org.edges([('ceo', 'cto'), ('ceo', 'cfo')])
org.render('org_chart', cleanup=True)
```
## 스타일 옵션
- 폰트: `fontname='Malgun Gothic'` (한글 지원)
- 색상: HTML 컬러 코드 지원
- 출력 형식: png, svg, pdf
- 레이아웃 엔진: dot(계층), neato(스프링), circo(원형), fdp(포스)
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,109 +0,0 @@
---
name: docx-creator
label: Word 문서 생성
description: Python을 사용하여 전문적인 Word 문서(.docx)를 생성합니다. 작업 폴더의 양식 파일을 자동 활용합니다.
icon: \uE8A5
allowed-tools:
- folder_map
- document_read
- file_read
- file_write
- process
- document_assemble
- format_convert
tabs: cowork
---
사용자의 요구에 맞는 전문적인 Word 문서를 Python으로 생성하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 python-docx 경로를 사용하세요.
- Python 불가: `document_assemble`로 문서 본문을 구성하고 `format_convert`로 docx 산출을 시도하세요. 실패 시 Markdown/HTML 결과와 변환 가이드를 함께 제공하세요.
## 사전 준비
먼저 python-docx 패키지가 설치되어 있는지 확인하고, 없으면 설치하세요:
```
process: pip install python-docx
```
## 양식 활용 (템플릿 모드)
작업 폴더에 양식 파일이 있으면 **반드시** 활용하세요:
1. **양식 탐색**: `folder_map`으로 작업 폴더를 스캔하여 `.docx` 파일 확인
2. **양식 후보 판별**:
- 파일명에 "양식", "template", "서식", "표준", "기본" 포함
- 또는 사용자가 명시적으로 "XX 양식으로 작성해줘" 요청
- 또는 사용자가 특정 .docx 파일명을 언급
3. **양식 구조 파악**: `document_read`로 양식 파일의 구조(스타일, 헤더, 섹션)를 먼저 확인
4. **양식 기반 생성**:
```python
doc = Document('양식_보고서.docx') # ← 빈 Document() 대신 양식 로드
# 양식의 스타일, 머리글/바닥글, 로고, 페이지 설정이 자동 상속됨
# 기존 본문 내용을 지우고 새 내용만 추가
for paragraph in doc.paragraphs:
paragraph.clear() # 기존 내용 제거
# 또는 필요에 따라 특정 섹션만 교체
```
5. **양식이 없으면**: 아래 기본 템플릿으로 새 문서 생성
## 작업 절차
1. **요구사항 파악**: 사용자가 원하는 문서의 종류, 구조, 내용을 확인
2. **양식 확인**: folder_map으로 작업 폴더에 양식 .docx 파일이 있는지 확인
3. **Python 스크립트 작성**: file_write로 .py 파일 생성
4. **스크립트 실행**: `process`로 Python 스크립트 실행
5. **결과 확인**: 생성된 .docx 파일 경로를 사용자에게 안내
## Python 스크립트 템플릿
```python
from docx import Document
from docx.shared import Inches, Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.style import WD_STYLE_TYPE
import os
# 양식 파일 자동 감지
template_keywords = ['양식', 'template', '서식', '표준', '기본']
template_file = None
for f in os.listdir('.'):
if f.endswith('.docx') and any(kw in f.lower() for kw in template_keywords):
template_file = f
break
# 양식이 있으면 활용, 없으면 새 문서
if template_file:
doc = Document(template_file)
print(f'양식 활용: {template_file}')
else:
doc = Document()
# 스타일 설정 (양식이 없을 때만)
style = doc.styles['Normal']
font = style.font
font.name = '맑은 고딕'
font.size = Pt(11)
# 제목
doc.add_heading('문서 제목', level=0)
# 본문
doc.add_paragraph('내용을 여기에 작성합니다.')
# 표
table = doc.add_table(rows=2, cols=3)
table.style = 'Light Grid Accent 1'
# 저장
doc.save('output.docx')
```
## 지원 기능
- 제목/소제목 계층 구조
- 표 (스타일, 병합, 서식)
- 이미지 삽입
- 머리글/바닥글
- 페이지 번호
- 목차
- 글머리 기호/번호 목록
- **양식 파일 기반 스타일 상속** (로고, 헤더, 페이지 설정 자동 유지)
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,55 +0,0 @@
---
name: email-draft
label: 비즈니스 이메일 작성
description: 상황과 톤에 맞는 전문적인 비즈니스 이메일 초안을 생성합니다.
icon: \uE715
allowed-tools:
- clipboard_tool
- file_write
tabs: all
---
사용자의 요청에 맞는 전문적인 비즈니스 이메일을 작성하세요.
## 워크플로우
1. 사용자에게 다음 정보를 확인하세요:
- **수신자**: 이름, 직급, 부서 (알고 있다면)
- **목적**: 요청 / 보고 / 안내 / 감사 / 사과 / 협조 / 초대 / 회신
- **핵심 내용**: 전달할 주요 메시지 (1~3가지)
- **톤**: 공식(格式) / 반공식 / 친근
- **언어**: 한국어 / 영어 / 일본어
2. 이메일 초안을 작성합니다:
- 적절한 인사말로 시작
- 핵심 내용은 **굵게** 강조
- 요청 사항은 명확하고 구체적으로
- 마무리 인사와 서명 포함
3. 사용자에게 초안을 보여주고 수정 요청을 받습니다.
4. 최종본을 clipboard_tool로 클립보드에 복사합니다.
## 이메일 구조
```
제목: [간결하고 명확한 제목]
[수신자] 님께,
[인사말]
[본문 - 목적과 핵심 내용]
[요청 사항 또는 다음 단계]
[마무리 인사]
[서명]
```
## 규칙
- 한 문단은 3~4문장을 넘지 않도록 간결하게
- 수동적 표현보다 능동적 표현 선호
- 약어는 처음 사용 시 풀어 쓰기
- 긴급도에 따라 제목에 [긴급], [참고] 등 태그 사용

View File

@@ -1,152 +0,0 @@
---
name: env-setup
label: 프로젝트 환경 설정
description: .gitignore, requirements.txt, .editorconfig 등 프로젝트 환경 설정 파일을 자동 생성합니다.
icon: \uE835
tabs: code
allowed-tools:
- folder_map
- file_read
- file_write
- process
---
프로젝트 유형에 맞는 환경 설정 파일을 자동으로 생성하세요.
## 작업 절차
1. **프로젝트 분석**: folder_map으로 프로젝트 구조를 파악하고 유형 판별
- `.py` 파일 → Python 프로젝트
- `package.json` 또는 `.js/.ts` 파일 → Node.js 프로젝트
- `.csproj` 또는 `.sln` 파일 → .NET 프로젝트
- `pom.xml` 또는 `.java` 파일 → Java 프로젝트
- 복합 프로젝트인 경우 모든 유형을 병합
2. **기존 설정 확인**: 이미 존재하는 설정 파일이 있는지 확인
- 있으면: 내용을 분석하여 누락된 항목만 추가 제안
- 없으면: 새로 생성
3. **생성할 파일 목록 제안**: 사용자에게 생성할 파일 목록을 보여주고 확인
4. **파일 생성**: file_write로 각 설정 파일 생성
5. **결과 안내**: 생성된 파일 목록과 주요 설정 내용 요약
## 프로젝트별 템플릿
### Python 프로젝트
생성 파일: `.gitignore`, `requirements.txt`, `.editorconfig`, `setup.cfg`, `.flake8`
**.gitignore (Python)**:
```
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
.venv/
*.egg-info/
dist/
build/
.eggs/
*.egg
.mypy_cache/
.pytest_cache/
.coverage
htmlcov/
.env
.idea/
.vscode/
*.log
```
**requirements.txt**: 프로젝트에서 import 문을 스캔하여 자동 생성
### Node.js 프로젝트
생성 파일: `.gitignore`, `.editorconfig`, `.nvmrc`, `.prettierrc`
**.gitignore (Node)**:
```
node_modules/
dist/
build/
.env
.env.local
*.log
npm-debug.log*
.DS_Store
coverage/
.nyc_output/
.idea/
.vscode/
*.tgz
```
### .NET 프로젝트
생성 파일: `.gitignore`, `.editorconfig`, `Directory.Build.props`
**.gitignore (.NET)**:
```
bin/
obj/
.vs/
*.user
*.suo
*.cache
packages/
*.nupkg
TestResults/
.idea/
*.DotSettings.user
```
### Java 프로젝트
생성 파일: `.gitignore`, `.editorconfig`
**.gitignore (Java)**:
```
*.class
*.jar
*.war
*.ear
target/
.gradle/
build/
.idea/
*.iml
.settings/
.classpath
.project
out/
```
## 공통 .editorconfig
```ini
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.{json,js,ts,jsx,tsx}]
indent_size = 2
[Makefile]
indent_style = tab
```
## 규칙
- 기존 설정 파일이 있으면 덮어쓰지 않고, 누락 항목만 제안
- .env 파일은 생성하지 않음 (보안 — 사용자가 직접 생성)
- 생성 전 파일 목록을 반드시 사용자에게 확인
- 프로젝트 루트에 생성 (하위 폴더에 생성하지 않음)
한국어로 안내하세요.

View File

@@ -1,63 +0,0 @@
---
name: gen-test
label: 테스트 생성기
description: 지정 파일의 단위 테스트를 자동 생성하고 실행하여 커버리지를 분석합니다.
icon: \uE9D5
allowed-tools:
- file_read
- file_write
- test_loop
- grep
- folder_map
- dev_env_detect
- build_run
tabs: code
---
지정된 소스 파일의 단위 테스트를 자동 생성하고 실행하세요.
## 워크플로우
1. **환경 감지**: dev_env_detect로 프로젝트 타입, 테스트 프레임워크 확인
2. **대상 분석**: file_read로 테스트 대상 파일 분석
- 공개 메서드/함수 목록 추출
- 메서드별 입출력 타입 파악
- 의존성 확인 (Mock 필요 여부)
3. **테스트 생성**: 각 메서드에 대해 테스트 케이스 작성
- 정상 케이스 (Happy Path)
- 경계값 (Boundary)
- 예외/에러 케이스
- null/empty 입력
4. **실행 및 검증**: test_loop으로 테스트 실행
5. **결과 보고**: 성공/실패 요약, 커버리지 추정
## 테스트 작성 원칙
### 명명 규칙
- C#: `[메서드명]_[시나리오]_[기대결과]`
- Python: `test_[메서드명]_[시나리오]`
- JavaScript: `should [기대 동작] when [조건]`
### 테스트 구조 (AAA 패턴)
```
Arrange — 테스트 데이터 준비
Act — 대상 메서드 실행
Assert — 결과 검증
```
### 프레임워크별 지원
- C#: xUnit, NUnit, MSTest
- Python: pytest, unittest
- JavaScript: Jest, Mocha, Vitest
- Java: JUnit 5
## 출력
- 테스트 파일 생성 (프로젝트 컨벤션에 맞는 위치)
- 테스트 실행 결과 요약
- 커버리지 추정 (메서드별 테스트 유무)
## 규칙
- 기존 테스트가 있으면 스타일을 따르기
- 외부 의존성은 Mock/Stub 사용
- 테스트 간 독립성 보장 (상태 공유 금지)
- 한국어 주석으로 테스트 의도 설명

View File

@@ -1,45 +0,0 @@
---
name: hook-policy-demo
label: Hook Policy Demo
description: hooks/hook_filters 런타임 정책 적용 예시 스킬입니다.
icon: \uE943
tabs: code
allowed-tools:
- file_read
- file_edit
- grep
- build_run
when_to_use: 코드 수정 후 특정 훅만 pre/post로 선택 적용하고 싶을 때
argument-hint: 대상 파일 경로 또는 변경 목적
context: fork
agent: worker
effort: medium
sample: true
hooks:
file_edit:
pre:
- lint-pre
post:
- verify-post
build_run:
post:
- verify-post
hook_filters: lint-pre@pre@file_edit, verify-post@post@file_edit, verify-post@post@build_run
---
이 스킬은 `hooks`/`hook_filters` 정책을 함께 사용하는 예시입니다.
## 목표
- 수정 전에는 `lint-pre` 훅만 실행
- 수정 후/빌드 후에는 `verify-post` 훅만 실행
## 실행 가이드
1. `grep` + `file_read`로 대상 코드와 관련 호출부를 확인합니다.
2. 필요한 최소 범위만 `file_edit`로 수정합니다.
3. 수정 후 `build_run`으로 빌드/테스트를 실행해 검증합니다.
4. 변경 내용/검증 결과/남은 리스크를 간단히 보고합니다.
## 제약
- `allowed-tools` 목록 외 도구 호출 금지
- 불필요한 전면 리팩토링 금지
- 동일 실패 재시도 전에 원인/대안 먼저 제시

View File

@@ -1,87 +0,0 @@
---
name: image-processor
label: 이미지 처리
description: Python Pillow를 사용하여 이미지 리사이즈, 크롭, 워터마크, 포맷 변환을 수행합니다.
icon: \uEB9F
allowed-tools:
- folder_map
- file_read
- file_write
- process
- image_analyze
- format_convert
- file_manage
tabs: cowork
---
사용자의 요구에 맞게 이미지를 처리하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 Pillow 경로를 사용하세요.
- Python 불가: `image_analyze`로 이미지 상태를 점검하고, `file_manage`/`format_convert`/`file_write`로 가능한 변환 및 수동 처리 지침을 제공하세요.
## 사전 준비
먼저 Pillow 패키지가 설치되어 있<><EC9E88>지 확인하고, 없으면 설<><EC84A4><EFBFBD>하세요:
```
process: pip install Pillow
```
## 작업 절<><ECA088>
1. **요구사항 파악**: 처리할 이미지 파일과 원하는 작업 확인
2. **Python 스크립트 작성**: file_write로 .py 파일 생성
3. **스크립트 실행**: `process`로 Python 스크립<ED81AC><EBA6BD> 실행
4. **결과 확인**: 처리된 이미지 파일 경로를 사용자에게 안내
## 지원 기능
### 리사이즈
```python
from PIL import Image
img = Image.open('input.png')
img_resized = img.resize((800, 600)) # 고정 크기
# 또는 비율 유지
img.thumbnail((800, 800))
img.save('output.png')
```
### 크롭
```python
img = Image.open('input.png')
cropped = img.crop((left, top, right, bottom))
cropped.save('output.png')
```
### 워터마크
```python
from PIL import Image, ImageDraw, ImageFont
img = Image.open('input.png')
draw = ImageDraw.Draw(img)
draw.text((10, 10), "Watermark", fill=(255, 255, 255, 128))
img.save('output.png')
```
### 포맷 변환
```python
img = Image.open('input.png')
img.save('output.jpg', 'JPEG', quality=85)
img.save('output.webp', 'WEBP', quality=80)
```
### 배치 처리
```python
import glob
for path in glob.glob('*.png'):
img = Image.open(path)
img.thumbnail((800, 800))
img.save(f'resized_{path}')
```
## 추가 기능
- 회전/뒤집기 (rotate, transpose)
- 밝기/대비/선명도 조절 (ImageEnhance)
- 필터 적용 (ImageFilter: BLUR, SHARPEN, CONTOUR)
- 이미지 합성 (Image.paste, Image.alpha_composite)
- EXIF 정보 읽기
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,67 +0,0 @@
---
name: impact
label: 변경 영향 분석
description: 코드 변경 시 영향받는 파일, 함수, 테스트를 식별합니다.
icon: \uE946
allowed-tools:
- lsp_code_intel
- grep
- search_codebase
- git_tool
- file_read
- folder_map
tabs: code
---
코드 변경이 미치는 영향 범위를 분석하여 안전한 수정을 도와주세요.
## 워크플로우
1. **변경 대상 확인**: 어떤 파일/함수/클래스를 변경할 예정인지 파악
2. **직접 참조 분석**: lsp_code_intel의 find_references로 직접 호출처 확인
3. **간접 영향 분석**:
- grep으로 문자열 기반 참조 검색
- search_codebase로 의미적 유사 코드 탐색
- 상속/인터페이스 체인 추적
4. **테스트 영향**: 관련 테스트 파일 식별
5. **위험도 평가**: 변경 영향 범위와 위험도 매트릭스 생성
6. **보고서 작성**: 영향 분석 보고서 생성
## 분석 항목
### 직접 영향
- 해당 함수/클래스를 직접 호출하는 코드
- import/using 하는 파일
- 상속받는 클래스
### 간접 영향
- 인터페이스 구현체를 통한 호출
- 리플렉션/동적 호출
- 설정 파일 참조
- UI 바인딩
### 테스트 영향
- 직접 테스트하는 테스트 파일
- 관련 통합 테스트
- 테스트 더블(Mock) 대상 여부
## 출력 형식
### 영향 분석 보고서
| 영향 수준 | 파일 | 관련 함수 | 위험도 | 비고 |
|----------|------|----------|--------|------|
| 🔴 직접 | A.cs | MethodX() | 높음 | 시그니처 변경 시 컴파일 오류 |
| 🟡 간접 | B.cs | MethodY() | 중간 | 동적 호출, 런타임 오류 가능 |
| 🟢 테스트 | A.Tests.cs | Test1() | 낮음 | 테스트 수정 필요 |
### 변경 체크리스트
- [ ] 직접 참조 N개 확인 및 수정
- [ ] 테스트 M개 업데이트
- [ ] 관련 문서 갱신
## 규칙
- 코드를 직접 수정하지 않음 (분석만 수행)
- 가능하면 LSP 기반 정확한 참조 분석 우선
- LSP 불가 시 grep 기반 텍스트 검색으로 대체
- 위험도는 보수적으로 평가 (의심스러우면 높음)

View File

@@ -1,107 +0,0 @@
---
name: json-schema
label: JSON/YAML 스키마 도구
description: JSON 또는 YAML 데이터에서 스키마를 생성하고, 데이터의 유효성을 검증합니다.
icon: \uE943
tabs: code
allowed-tools:
- json_tool
- file_read
- file_write
- clipboard_tool
---
JSON/YAML 샘플 데이터에서 스키마를 추출하거나, 기존 스키마로 데이터를 검증하세요.
## 작업 절차
1. **요청 유형 판별**:
- **스키마 생성**: 샘플 JSON/YAML → JSON Schema 추출
- **스키마 검증**: 데이터 + 스키마 → 유효성 검사
- **스키마 문서화**: 기존 스키마 → 사람이 읽을 수 있는 설명 생성
2. **입력 데이터 확인**: file_read 또는 사용자 입력으로 데이터 로드
3. **스키마 생성 또는 검증 수행**: json_tool로 처리
4. **결과 출력**: 생성된 스키마 또는 검증 결과를 표시
5. **파일 저장**: file_write로 결과를 저장하거나 clipboard_tool로 복사
## 스키마 생성 규칙
### 타입 추론
| JSON 값 | JSON Schema 타입 | 추가 속성 |
|---------|-----------------|----------|
| `"text"` | `string` | — |
| `123` | `integer` | — |
| `1.5` | `number` | — |
| `true` | `boolean` | — |
| `null` | `null` | nullable 처리 |
| `[]` | `array` | items 스키마 |
| `{}` | `object` | properties 스키마 |
### 추론 강화
- **패턴 감지**: 이메일, URL, 날짜, UUID 등은 `format` 속성 추가
- **열거형 감지**: 값의 종류가 적으면 `enum` 으로 제안
- **필수 필드**: 모든 샘플에 존재하는 필드는 `required`로 표시
- **배열 항목**: 배열 내 모든 항목을 분석하여 통합 스키마 생성
### 출력 형식 (JSON Schema Draft 7)
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "사용자 정보",
"description": "사용자 프로필 데이터 스키마",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "사용자 이름"
},
"email": {
"type": "string",
"format": "email",
"description": "이메일 주소"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150,
"description": "나이"
}
},
"required": ["name", "email"],
"additionalProperties": false
}
```
## 검증 결과 형식
검증 결과는 다음과 같이 표시하세요:
```
검증 결과: ❌ 실패 (3건의 오류)
| # | 경로 | 오류 | 기대값 |
|---|------|------|--------|
| 1 | $.email | 필수 필드 누락 | string (required) |
| 2 | $.age | 타입 불일치 | integer (실제: string) |
| 3 | $.tags[2] | 열거형 불일치 | "A", "B", "C" 중 하나 |
```
## 스키마 문서화 형식
기존 스키마를 분석하여 사람이 읽기 쉬운 문서를 생성:
```
## 사용자 정보 스키마
| 필드 | 타입 | 필수 | 설명 | 제약 조건 |
|------|------|------|------|----------|
| name | string | ✅ | 사용자 이름 | — |
| email | string | ✅ | 이메일 주소 | format: email |
| age | integer | — | 나이 | 0~150 |
| tags | array | — | 태그 목록 | items: string |
```
## 규칙
- JSON Schema Draft 7 형식 사용
- 스키마 생성 시 description 필드를 한국어로 작성
- 복수 샘플이 제공되면 모든 샘플을 분석하여 통합 스키마 생성
- 중첩 객체는 재귀적으로 스키마 추출
- 결과는 파일 저장과 클립보드 복사 모두 제공
한국어로 안내하세요.

View File

@@ -1,69 +0,0 @@
---
name: log-analyze
label: 로그 분석기
description: 로그 파일의 패턴을 분석하고 에러를 요약하며 타임라인을 시각화합니다.
icon: \uE9D9
allowed-tools:
- file_read
- grep
- chart_create
- html_create
- data_pivot
- text_summarize
tabs: cowork
---
로그 파일을 분석하여 패턴, 에러, 추세를 파악하고 보고서를 생성하세요.
## 워크플로우
1. **로그 파일 로드**: file_read로 로그 파일 읽기
2. **패턴 분석**:
- grep으로 에러/경고/예외 패턴 검색
- 시간대별 이벤트 빈도 파악
- 반복되는 에러 패턴 식별
3. **통계 생성**: data_pivot으로 집계
4. **시각화**: chart_create로 타임라인 차트 생성
5. **보고서**: html_create로 분석 보고서 생성
## 분석 항목
### 에러 분석
- ERROR, WARN, FATAL, Exception 키워드 추출
- 에러 유형별 발생 빈도
- 최초 발생 시점 및 최근 발생 시점
- 에러 메시지 클러스터링 (유사 에러 그룹화)
### 시간대 분석
- 시간대별 로그 발생 빈도
- 피크 시간대 식별
- 에러 집중 시간대
### 패턴 분석
- 반복 패턴 (주기적 에러)
- 연쇄 에러 (A 에러 후 B 에러 발생 패턴)
- 비정상 패턴 (평소와 다른 로그량)
## 출력 형식
```
## 로그 분석 보고서
- 분석 기간: [시작] ~ [끝]
- 총 로그: N줄
### 에러 요약 (상위 10건)
| 순위 | 에러 유형 | 발생 횟수 | 최근 발생 |
|------|----------|----------|----------|
### 타임라인 차트
[시간대별 이벤트 빈도 차트]
### 상세 분석
[에러별 상세 내용 및 권장 조치]
```
## 지원 로그 형식
- 일반 텍스트 로그 (타임스탬프 자동 감지)
- JSON 로그 (각 줄이 JSON 객체)
- CSV 로그 (헤더 포함)
- syslog 형식

View File

@@ -1,174 +0,0 @@
---
name: markdown-to-doc
label: Markdown → 문서 변환
description: Markdown 파일을 서식이 적용된 Word(.docx) 또는 PDF 문서로 변환합니다.
icon: \uE8A5
allowed-tools:
- file_read
- file_write
- process
- format_convert
- document_assemble
tabs: cowork
---
Markdown 파일을 전문적인 Word 또는 PDF 문서로 변환하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 python-docx 경로를 사용하세요.
- Python 불가: `format_convert`를 우선 사용해 Markdown을 docx/pdf로 변환하고, 변환 제한 시 `file_write`로 보정 가이드를 생성하세요.
## 사전 준비
먼저 필요한 패키지가 설치되어 있는지 확인하고, 없으면 설치하세요:
```
process: pip install python-docx markdown
```
## 작업 절차
1. **Markdown 파일 확인**: file_read로 변환할 Markdown 파일의 내용과 구조를 파악
2. **변환 옵션 확인**: 사용자에게 다음 옵션을 확인
- 출력 형식: Word(.docx) 또는 PDF
- 폰트: 맑은 고딕 (기본) / 사용자 지정
- 여백, 페이지 크기 설정
- 머리글/바닥글 포함 여부
3. **Python 스크립트 작성**: file_write로 변환 스크립트 생성
4. **스크립트 실행**: `process`로 Python 스크립트 실행
5. **결과 확인**: 생성된 문서 파일 경로와 페이지 수를 안내
## 스타일 매핑
| Markdown | Word 스타일 | 설명 |
|----------|------------|------|
| `# 제목` | Heading 1 | 16pt, 굵게 |
| `## 소제목` | Heading 2 | 14pt, 굵게 |
| `### 항목` | Heading 3 | 12pt, 굵게 |
| 본문 텍스트 | Normal | 11pt |
| `**굵게**` | Bold run | 굵게 |
| `*기울임*` | Italic run | 기울임 |
| `` `코드` `` | 코드 스타일 | Consolas, 배경색 |
| `> 인용` | Quote | 들여쓰기 + 왼쪽 테두리 |
| `- 목록` | List Bullet | 글머리 기호 |
| `1. 번호` | List Number | 번호 목록 |
| 표 | Table Grid | 테두리 표 |
| `---` | 페이지 구분 | 가로선 → 페이지 나누기 |
| 코드 블록 | 코드 단락 | Consolas, 회색 배경 |
## Python 스크립트 템플릿
```python
import re
from docx import Document
from docx.shared import Inches, Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
import markdown
# Markdown 파일 읽기
with open('input.md', 'r', encoding='utf-8') as f:
md_content = f.read()
doc = Document()
# 기본 스타일 설정
style = doc.styles['Normal']
font = style.font
font.name = '맑은 고딕'
font.size = Pt(11)
style.element.rPr.rFonts.set(qn('w:eastAsia'), '맑은 고딕')
# 여백 설정
for section in doc.sections:
section.top_margin = Cm(2.54)
section.bottom_margin = Cm(2.54)
section.left_margin = Cm(3.17)
section.right_margin = Cm(3.17)
# Markdown 파싱 및 변환
lines = md_content.split('\n')
i = 0
while i < len(lines):
line = lines[i]
# 제목 (Heading)
if line.startswith('#'):
level = len(line) - len(line.lstrip('#'))
text = line.lstrip('#').strip()
doc.add_heading(text, level=min(level, 4))
# 코드 블록
elif line.startswith('```'):
code_lines = []
i += 1
while i < len(lines) and not lines[i].startswith('```'):
code_lines.append(lines[i])
i += 1
p = doc.add_paragraph()
run = p.add_run('\n'.join(code_lines))
run.font.name = 'Consolas'
run.font.size = Pt(9)
# 인용
elif line.startswith('>'):
text = line.lstrip('>').strip()
p = doc.add_paragraph(text)
p.paragraph_format.left_indent = Cm(1.27)
# 글머리 기호
elif line.startswith('- ') or line.startswith('* '):
text = line[2:].strip()
doc.add_paragraph(text, style='List Bullet')
# 번호 목록
elif re.match(r'^\d+\.\s', line):
text = re.sub(r'^\d+\.\s', '', line).strip()
doc.add_paragraph(text, style='List Number')
# 가로선 → 페이지 나누기
elif line.strip() in ('---', '***', '___'):
doc.add_page_break()
# 빈 줄
elif line.strip() == '':
pass
# 일반 텍스트
else:
p = doc.add_paragraph()
# 굵게, 기울임 처리
parts = re.split(r'(\*\*.*?\*\*|\*.*?\*|`.*?`)', line)
for part in parts:
if part.startswith('**') and part.endswith('**'):
run = p.add_run(part[2:-2])
run.bold = True
elif part.startswith('*') and part.endswith('*'):
run = p.add_run(part[1:-1])
run.italic = True
elif part.startswith('`') and part.endswith('`'):
run = p.add_run(part[1:-1])
run.font.name = 'Consolas'
run.font.size = Pt(10)
else:
p.add_run(part)
i += 1
# 저장
doc.save('output.docx')
print(f'변환 완료: output.docx ({len(doc.paragraphs)}개 단락)')
```
## 표 변환
Markdown 표가 있으면 Word 표로 변환합니다:
- 헤더 행: 굵게, 배경색 적용
- 셀 정렬: Markdown의 `:---`, `:---:`, `---:` 구문 반영
- 테두리: 전체 셀에 얇은 테두리
## 규칙
- 원본 Markdown 파일은 수정하지 않음
- 인코딩: UTF-8 기본
- 이미지 링크(`![](path)`)는 로컬 파일이면 삽입, URL이면 경로만 표시
- 복잡한 Markdown(수식, 다이어그램)은 지원 범위와 한계를 안내
- 출력 파일명: 원본 파일명 기준 (.md → .docx)
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,45 +0,0 @@
---
name: meeting-minutes
label: 회의록 정리
description: 회의 내용을 체계적으로 정리하여 회의록을 생성합니다.
icon: \uE771
allowed-tools:
- file_read
- file_write
- text_summarize
- template_render
tabs: cowork
---
사용자가 제공한 회의 내용(텍스트, 메모, 파일)을 정리하여 체계적인 회의록을 작성하세요.
다음 도구를 사용하세요:
1. file_read — 회의 관련 파일 읽기 (필요 시)
2. file_write — 회의록 파일 생성
회의록 형식:
## 회의 정보
- 일시:
- 참석자:
- 장소/방법:
- 주제:
## 안건 및 논의 내용
각 안건별로:
- **안건**: 주제
- **논의 내용**: 주요 발언 및 의견 정리
- **결정 사항**: 합의된 내용
## 액션 아이템
| 번호 | 담당자 | 내용 | 기한 | 비고 |
|------|--------|------|------|------|
| 1 | - | - | - | - |
## 다음 회의
- 예정일:
- 주요 안건:
규칙:
- 핵심 내용 위주로 간결하게 정리
- 결정 사항과 액션 아이템은 명확하게 기술
- 한국어로 작성

View File

@@ -1,118 +0,0 @@
---
name: ocr-extract
label: OCR 텍스트 추출
description: Python pytesseract를 사용하여 이미지/스캔 문서에서 텍스트를 추출합니다.
icon: \uE8D4
allowed-tools:
- file_read
- file_write
- process
- image_analyze
- text_summarize
tabs: cowork
---
이미지 또는 스캔된 문서에서 텍스트를 추출하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 pytesseract 경로를 사용하세요.
- Python 불가: `image_analyze`로 텍스트 후보를 추출하고 `text_summarize` + `file_write`로 정제본을 제공하세요.
## 사전 준비
1. Tesseract OCR 엔진이 시스템에 설치되어 있어야 합니다.
- Windows: https://github.com/UB-Mannheim/tesseract/wiki 에서 설치
- 한국어 지원: 설치 시 "Korean" 언어 데이터 선택
2. Python 패키지 설치:
```
process: pip install pytesseract Pillow
```
## 작업 절차
1. **이미지 확인**: 사용자가 제공한 이미지 파일 확인
2. **전처리 (선택)**: 이미지 품질이 낮으면 전처리 스크립트 적용
3. **OCR 실행**: pytesseract로 텍스트 추출
4. **결과 저장**: 추출된 텍스트를 파일로 저장하고 사용자에게 안내
## OCR 스크립트 템플릿
### 기본 텍스트 추출
```python
import pytesseract
from PIL import Image
# Windows에서 Tesseract 경로 지정 (필요 시)
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
img = Image.open('scan.png')
text = pytesseract.image_to_string(img, lang='kor+eng')
print(text)
with open('extracted.txt', 'w', encoding='utf-8') as f:
f.write(text)
```
### 이미지 전처리 (품질 개선)
```python
from PIL import Image, ImageFilter, ImageEnhance
img = Image.open('scan.png')
# 그레이스케일 변환
img = img.convert('L')
# 대비 향상
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
# 선명도 향상
img = img.filter(ImageFilter.SHARPEN)
# 이진화 (흑백)
threshold = 128
img = img.point(lambda x: 255 if x > threshold else 0)
img.save('preprocessed.png')
text = pytesseract.image_to_string(img, lang='kor+eng')
```
### 배치 OCR (여러 이미지)
```python
import glob
import pytesseract
from PIL import Image
results = []
for path in sorted(glob.glob('*.png')):
img = Image.open(path)
text = pytesseract.image_to_string(img, lang='kor+eng')
results.append(f'--- {path} ---\n{text}\n')
with open('all_extracted.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(results))
```
### 표 영역 추출
```python
import pytesseract
from PIL import Image
img = Image.open('table.png')
# TSV 형식으로 추출 (표 구조 보존)
tsv_data = pytesseract.image_to_data(img, lang='kor+eng', output_type=pytesseract.Output.DATAFRAME)
print(tsv_data)
```
## 지원 언어
- `kor` — 한국어
- `eng` — 영어
- `kor+eng` — 한국어+영어 혼합 (권장)
- `jpn` — 일본어
- `chi_sim` — 중국어 간체
## 팁
- 스캔 해상도 300dpi 이상이면 인식률이 높습니다
- 기울어진 이미지는 `img.rotate()` 로 보정 후 추출하세요
- 손글씨는 인식률이 낮을 수 있습니다
한국어로 안내하세요. 작업 폴더에 Python 스크립트와 결과 파일을 저장하세요.

View File

@@ -1,59 +0,0 @@
---
name: paper-review
label: 논문 분석
description: 논문 또는 기술 문서를 체계적으로 분석하고 핵심 내용을 정리합니다.
icon: \uE736
allowed-tools:
- document_read
- file_read
- file_write
- text_summarize
tabs: cowork
---
사용자가 제공한 논문 또는 기술 문서를 체계적으로 분석하세요.
## 파일 읽기 전략
- **PDF 파일**: 반드시 `document_read` 도구를 사용하세요. 페이지 범위 지정이 가능합니다.
- 먼저 전체 페이지 수를 확인한 후, 초록(1-2페이지)→본문→참고문헌 순으로 읽으세요.
- `file_read`로 PDF를 읽으면 텍스트가 깨질 수 있으므로 사용하지 마세요.
- **텍스트 파일** (.txt, .md, .html 등): `file_read` 도구를 사용하세요.
## 사용 도구
1. document_read — PDF 논문 파일 읽기 (페이지 범위 지정, 초록/참고문헌 추출)
2. file_read — 텍스트 기반 문서 파일 읽기
3. file_write — 분석 보고서 생성
## 분석 항목
## 논문 개요
- 제목, 저자, 발표 연도/학회
- 연구 분야 및 키워드
## 연구 목적 및 배경
- 연구 문제 정의
- 기존 연구의 한계점
## 방법론
- 제안 방법의 핵심 아이디어
- 실험 설계 및 데이터셋
## 주요 결과
- 핵심 실험 결과 (표/수치 인용)
- 기존 방법 대비 개선점
## 한계점 및 향후 연구
- 저자가 인정한 한계
- 발전 가능성
## 실무 적용 가능성
- 우리 업무에 적용할 수 있는 포인트
- 기술 도입 시 고려사항
## 작업 절차
1. 사용자가 파일명을 언급하면 작업 폴더에서 해당 파일을 찾아 읽기
2. PDF인 경우 `document_read`로 초록(1-2p) 먼저 읽어 전체 구조 파악
3. 본문을 페이지 범위별로 나누어 순차 읽기
4. 위 분석 항목에 따라 체계적으로 정리
5. `file_write`로 분석 보고서를 마크다운 파일로 저장
한국어로 작성하고, 전문 용어는 원문과 함께 표기하세요.

View File

@@ -1,74 +0,0 @@
---
name: pdf-processor
label: PDF 처리
description: Python을 사용하여 PDF에서 텍스트/표를 추출하거나 PDF를 생성합니다.
icon: \uE9F9
allowed-tools:
- folder_map
- document_read
- file_read
- file_write
- process
- format_convert
tabs: cowork
---
PDF 파일을 읽거나 새 PDF를 생성하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 pypdf/pdfplumber/reportlab 경로를 사용하세요.
- Python 불가: `document_read`로 텍스트/구조를 추출하고, 생성 작업은 `format_convert` + `file_write` 기반으로 대체하세요.
## 사전 준비
필요한 패키지를 확인하고 설치하세요:
```
process: pip install pypdf pdfplumber reportlab
```
## 작업 절차
### PDF 텍스트 추출
1. **파일 확인**: folder_map으로 PDF 파일 위치 확인
2. **추출 스크립트 작성**: file_write로 Python 스크립트 생성
3. **실행**: `process`로 실행
4. **결과 전달**: 추출된 텍스트를 사용자에게 전달
### PDF 생성
1. **내용 파악**: 사용자가 원하는 문서 내용 확인
2. **생성 스크립트 작성**: file_write로 Python 스크립트 생성
3. **실행 및 확인**: `process`로 실행
## 텍스트 추출 템플릿
```python
import pdfplumber
import json
results = []
with pdfplumber.open('input.pdf') as pdf:
for i, page in enumerate(pdf.pages):
text = page.extract_text() or ''
tables = page.extract_tables() or []
results.append({
'page': i + 1,
'text': text,
'tables': tables,
})
with open('pdf_extracted.json', 'w', encoding='utf-8') as f:
json.dump(results, f, ensure_ascii=False, indent=2)
for r in results:
print(f"--- 페이지 {r['page']} ---")
print(r['text'][:500])
```
## 지원 기능
- 텍스트 추출 (페이지별)
- 표 추출 (구조 보존)
- PDF 병합 / 분할
- PDF 생성 (reportlab)
- 페이지 회전
- 메타데이터 읽기
한국어로 안내하세요. 원본 PDF는 수정하지 마세요.

View File

@@ -1,94 +0,0 @@
---
name: perf-audit
label: 성능 감사
description: 코드 복잡도, 성능 병목, 메모리 이슈를 분석하고 최적화 방안을 제시합니다.
icon: \uE9D9
allowed-tools:
- file_read
- grep
- folder_map
- search_codebase
- glob
- html_create
tabs: code
---
코드베이스의 성능 관련 이슈를 분석하고 최적화 보고서를 생성하세요.
## 워크플로우
1. **프로젝트 분석**: folder_map으로 구조 파악, 언어/프레임워크 식별
2. **복잡도 분석**: 파일별 줄 수, 메서드 크기, 중첩 깊이 측정
3. **성능 안티패턴 탐지**: grep으로 알려진 성능 이슈 패턴 검색
4. **메모리 이슈 탐지**: 리소스 해제 누락, 대용량 할당 패턴
5. **보고서 생성**: html_create로 성능 감사 보고서
## 분석 항목
### 코드 복잡도 지표
- **파일 크기**: 500줄 이상 파일 식별
- **메서드 크기**: 50줄 이상 메서드 식별
- **중첩 깊이**: 4단계 이상 들여쓰기
- **매개변수 수**: 5개 이상 파라미터 메서드
### 성능 안티패턴
#### 데이터베이스
- N+1 쿼리 패턴 (루프 내 DB 호출)
- SELECT * 사용 (불필요한 컬럼 로드)
- 인덱스 미사용 쿼리 힌트
#### 메모리
- IDisposable 미해제 (using 미사용)
- 대용량 문자열 결합 (StringBuilder 미사용)
- 정적 컬렉션 무한 증가
- 이벤트 핸들러 미해제 (메모리 누수)
#### I/O
- 동기 파일 I/O (async 미사용)
- 동기 네트워크 호출
- 불필요한 직렬화/역직렬화
#### 알고리즘
- O(n²) 이상 루프 (중첩 foreach/for)
- 반복 계산 (캐싱 미적용)
- LINQ 체인의 불필요한 ToList()
#### 프론트엔드
- 불필요한 리렌더링 패턴
- 대용량 번들 임포트
- 이미지 최적화 미적용
### .NET 전용 패턴
```
탐지 대상:
- Task.Result / .Wait() (데드락 위험)
- lock 내부 async 호출
- GC.Collect() 직접 호출
- Reflection 반복 사용
- string + string 반복 (루프 내)
```
## 출력 형식
### 성능 감사 보고서
**요약 대시보드**
| 지표 | 값 | 상태 |
|------|-----|------|
| 총 파일 수 | ... | — |
| 대형 파일 (500줄+) | ... | ⚠ |
| 대형 메서드 (50줄+) | ... | ⚠ |
| 성능 안티패턴 | ... | 🔴 |
| 메모리 이슈 | ... | 🟡 |
**상세 이슈 목록**
| 우선순위 | 파일 | 라인 | 이슈 | 영향 | 권장 조치 |
|---------|------|------|------|------|----------|
| 🔴 높음 | ... | ... | N+1 쿼리 | 응답 지연 | 일괄 로드 |
## 규칙
- 코드를 수정하지 않음 (분석 + 보고서만)
- 성능 이슈는 영향도와 수정 난이도를 함께 평가
- 추측보다 패턴 기반 탐지 우선
- 한국어로 작성

View File

@@ -1,123 +0,0 @@
---
name: pptx-creator
label: PPT 프레젠테이션 생성
description: Python을 사용하여 전문적인 PowerPoint 프레젠테이션을 생성합니다. 작업 폴더의 양식 파일을 자동 활용합니다.
icon: \uE7BE
allowed-tools:
- folder_map
- document_read
- file_read
- file_write
- process
- pptx_create
- template_render
tabs: cowork
---
사용자의 요구에 맞는 PowerPoint 프레젠테이션을 Python으로 생성하세요.
## 실행 경로 선택 (Python 가능/불가)
- 먼저 `process``python --version`을 확인하세요.
- Python 가능: 기존 python-pptx 경로를 사용하세요.
- Python 불가: `pptx_create`로 슬라이드 초안을 생성하고 `template_render` + `file_write`로 발표자료 구조를 보강하세요.
## 사전 준비
필요한 패키지를 확인하고 설치하세요:
```
process: pip install python-pptx
```
## 양식 활용 (마스터 슬라이드 템플릿)
작업 폴더에 PPT 양식이 있으면 **반드시** 활용하세요:
1. **양식 탐색**: `folder_map`으로 작업 폴더를 스캔하여 `.pptx` 파일 확인
2. **양식 후보 판별**:
- 파일명에 "양식", "template", "서식", "표준", "기본" 포함
- 또는 사용자가 명시적으로 "XX 양식으로 작성해줘" 요청
- 또는 사용자가 특정 .pptx 파일명을 언급
3. **양식 구조 파악**: `document_read`로 양식의 슬라이드 레이아웃 목록 확인
4. **양식 기반 생성**:
```python
prs = Presentation('양식_발표.pptx')
# 마스터 슬라이드의 배경, 로고, 색 테마, 폰트가 자동 상속
# 기존 슬라이드 제거 후 새 내용 추가
while len(prs.slides) > 0:
rId = prs.slides._sldIdLst[0].rId
prs.part.drop_rel(rId)
del prs.slides._sldIdLst[0]
```
5. **레이아웃 확인** (양식마다 다를 수 있음):
```python
for i, layout in enumerate(prs.slide_layouts):
print(f'{i}: {layout.name}')
```
6. **양식이 없으면**: 아래 기본 템플릿으로 새 프레젠테이션 생성
## 작업 절차
1. **요구사항 파악**: 발표 주제, 슬라이드 수, 스타일 확인
2. **양식 확인**: folder_map으로 작업 폴더에 양식 .pptx 파일이 있는지 확인
3. **스크립트 작성**: file_write로 Python 스크립트 생성
4. **실행**: `process`로 스크립트 실행
5. **결과 안내**: 생성된 .pptx 파일 경로를 사용자에게 전달
## 스크립트 템플릿
```python
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
import os
# 양식 파일 자동 감지
template_keywords = ['양식', 'template', '서식', '표준', '기본']
template_file = None
for f in os.listdir('.'):
if f.endswith('.pptx') and any(kw in f.lower() for kw in template_keywords):
template_file = f
break
# 양식이 있으면 활용, 없으면 새 프레젠테이션
if template_file:
prs = Presentation(template_file)
# 기존 슬라이드 제거 (마스터/레이아웃은 유지)
while len(prs.slides) > 0:
rId = prs.slides._sldIdLst[0].rId
prs.part.drop_rel(rId)
del prs.slides._sldIdLst[0]
print(f'양식 활용: {template_file}')
print(f'사용 가능한 레이아웃: {[l.name for l in prs.slide_layouts]}')
else:
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
# 제목 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[0])
slide.shapes.title.text = '프레젠테이션 제목'
if len(slide.placeholders) > 1:
slide.placeholders[1].text = '부제목'
# 내용 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[1])
slide.shapes.title.text = '섹션 제목'
body = slide.placeholders[1]
body.text = '첫 번째 포인트'
p = body.text_frame.add_paragraph()
p.text = '두 번째 포인트'
prs.save('presentation.pptx')
print('프레젠테이션 생성 완료: presentation.pptx')
```
## 지원 기능
- 제목/내용/빈 슬라이드 레이아웃
- 텍스트 서식 (글꼴, 크기, 색상, 정렬)
- 표 삽입
- 이미지 삽입
- 도형 (사각형, 원, 화살표)
- 차트 (막대, 선, 원형)
- 슬라이드 번호
- 마스터 슬라이드 커스터마이징
- **양식 파일 기반 마스터/레이아웃 상속** (배경, 로고, 색 테마, 폰트 자동 유지)
한국어로 안내하세요. 작업 폴더에 결과 파일을 저장하세요.

View File

@@ -1,100 +0,0 @@
---
name: prd-generator
label: 요구사항 정의서 (PRD)
description: 제품 요구사항 정의서, 유저 스토리, 수용 기준을 체계적으로 생성합니다.
icon: \uE8A5
allowed-tools:
- file_read
- file_write
- html_create
- docx_create
- document_plan
- document_assemble
tabs: cowork
---
제품/기능의 요구사항 정의서(PRD)를 체계적으로 작성하세요.
## 워크플로우
1. **요구사항 수집**: 사용자에게 다음을 확인
- 제품/기능 이름
- 목적과 배경
- 대상 사용자
- 핵심 기능 목록
- 제약 조건 (기술, 일정, 예산)
2. **구조화**: document_plan으로 PRD 개요 설계
3. **상세 작성**: 섹션별 상세 내용 작성
- 유저 스토리 (As a... I want... So that...)
- 수용 기준 (Given... When... Then...)
- 기능 우선순위 (MoSCoW)
4. **문서 생성**: document_assemble 또는 html_create로 최종 문서
## PRD 구조
### 1. 개요
- 제품/기능 이름
- 버전 / 작성일 / 작성자
- 문서 목적
### 2. 배경 및 목적
- 비즈니스 배경
- 해결하려는 문제
- 기대 효과 (정량적 KPI)
### 3. 대상 사용자
- 사용자 페르소나
- 사용 시나리오
- 사용자 여정 맵
### 4. 기능 요구사항
#### 유저 스토리 형식
```
US-001: [기능명]
As a [역할],
I want [기능],
So that [가치].
수용 기준:
- Given [사전 조건], When [행동], Then [기대 결과]
- Given ..., When ..., Then ...
우선순위: Must Have / Should Have / Could Have / Won't Have
```
### 5. 비기능 요구사항
- 성능 (응답 시간, 처리량)
- 보안 (인증, 권한, 암호화)
- 접근성 (WCAG 수준)
- 호환성 (브라우저, OS, 디바이스)
### 6. 기술 제약
- 기술 스택 제한
- 연동 시스템
- 데이터 마이그레이션
### 7. 일정 및 마일스톤
| 마일스톤 | 예정일 | 산출물 |
|---------|--------|--------|
| 설계 완료 | ... | 상세 설계서 |
| 개발 완료 | ... | 릴리즈 빌드 |
| QA 완료 | ... | 테스트 보고서 |
### 8. 성공 지표
- 핵심 KPI 및 측정 방법
- 목표 수치
### 9. 리스크 및 대안
| 리스크 | 영향 | 대안 |
|--------|------|------|
| ... | 높음 | ... |
## 규칙
- 사용자 관점에서 작성 (기술 용어 최소화)
- 유저 스토리는 INVEST 원칙 준수 (Independent, Negotiable, Valuable, Estimable, Small, Testable)
- 수용 기준은 테스트 가능하도록 구체적으로
- 한국어로 작성 (영어 용어 병기 가능)

View File

@@ -1,72 +0,0 @@
---
name: refactor
label: 리팩토링 가이드
description: 코드베이스를 분석하여 리팩토링 포인트를 식별하고 실행 계획을 생성합니다.
icon: \uE90F
allowed-tools:
- search_codebase
- grep
- file_read
- code_review
- folder_map
- lsp_code_intel
tabs: code
# hooks / hook_filters 예시 (옵션):
# hooks:
# file_edit:
# pre:
# - lint-pre
# post: verify-post
# hook_filters: lint-pre@pre@file_edit, verify-post@post@*
---
코드베이스를 분석하여 리팩토링이 필요한 부분을 식별하고 개선 계획을 수립하세요.
## 워크플로우
1. **구조 파악**: folder_map + grep으로 프로젝트 전체 구조 분석
2. **코드 스멜 탐지**:
- 중복 코드 (grep으로 유사 패턴 검색)
- 긴 메서드/클래스 (file_read로 크기 확인)
- 복잡한 조건문 (중첩 if/switch)
- 미사용 코드 (lsp_code_intel로 참조 확인)
3. **의존성 분석**: lsp_code_intel로 참조 관계 파악
4. **우선순위 결정**: 영향도 × 난이도 매트릭스
5. **리팩토링 계획 생성**: 단계별 실행 계획
## 분석 항목
### 코드 스멜 (Code Smells)
- **중복 코드**: 3곳 이상 반복되는 유사 코드
- **거대 클래스**: 500줄 이상의 클래스
- **긴 메서드**: 50줄 이상의 메서드
- **매개변수 과다**: 5개 이상 파라미터
- **의존성 순환**: 상호 참조 관계
- **매직 넘버**: 하드코딩된 숫자/문자열
- **깊은 중첩**: 4단계 이상 들여쓰기
### 리팩토링 기법 (제안)
- Extract Method / Extract Class
- Rename (변수, 메서드, 클래스)
- Move Method / Move Field
- Replace Conditional with Polymorphism
- Introduce Parameter Object
- Remove Dead Code
## 출력 형식
### 리팩토링 보고서
| 우선순위 | 파일 | 이슈 | 제안 | 영향도 | 난이도 |
|---------|------|------|------|--------|--------|
| 🔴 높음 | ... | ... | ... | ★★★ | ★☆☆ |
| 🟡 중간 | ... | ... | ... | ★★☆ | ★★☆ |
### 실행 계획
1. [안전한 변경부터] ...
2. [테스트 추가 후] ...
3. [구조 변경] ...
## 규칙
- 코드를 직접 수정하지 않음 (분석 + 계획만)
- 기존 테스트가 있으면 테스트 커버리지 확인
- 팀 컨벤션/스타일 가이드 존중

View File

@@ -1,86 +0,0 @@
---
name: regex-helper
label: 정규식 도우미
description: 정규식 패턴을 생성하고, 기존 패턴을 해석하며, 테스트 케이스로 검증합니다.
icon: \uE8FD
tabs: code
allowed-tools:
- regex_tool
- clipboard_tool
- file_read
---
사용자의 요구에 맞는 정규식 패턴을 작성하거나, 기존 패턴을 해석하고 테스트하세요.
## 작업 절차
1. **요구사항 파악**: 사용자의 요청 유형을 판별
- **패턴 생성**: "이메일 주소를 찾는 정규식 만들어줘"
- **패턴 해석**: "이 정규식이 무슨 뜻이야? `^[\w.-]+@[\w.-]+\.\w+$`"
- **패턴 테스트**: "이 패턴이 이 문자열에 매칭되는지 확인해줘"
2. **패턴 작성 또는 분석**:
- 생성: 요구사항을 분석하여 정규식 패턴 작성
- 해석: 패턴을 구성 요소별로 분해하여 설명
3. **테스트 수행**: regex_tool로 패턴을 테스트 케이스에 적용
- 매칭되어야 할 문자열 (positive cases)
- 매칭되지 않아야 할 문자열 (negative cases)
4. **결과 설명**: 매칭 결과와 캡처 그룹을 상세히 설명
5. **최적화 제안**: 성능 또는 가독성 개선이 가능하면 대안 제시
## 패턴 생성 가이드
### 자주 사용되는 패턴
| 용도 | 패턴 | 설명 |
|------|------|------|
| 이메일 | `[\w.-]+@[\w.-]+\.\w{2,}` | 기본 이메일 형식 |
| 전화번호 (한국) | `0\d{1,2}-\d{3,4}-\d{4}` | 010-1234-5678 형식 |
| 날짜 (YYYY-MM-DD) | `\d{4}-(?:0[1-9]\|1[0-2])-(?:0[1-9]\|[12]\d\|3[01])` | ISO 날짜 형식 |
| IP 주소 | `(?:\d{1,3}\.){3}\d{1,3}` | IPv4 기본 |
| URL | `https?://[\w.-]+(?:/[\w./?#&=-]*)?` | HTTP/HTTPS URL |
| 한글만 | `[가-힣]+` | 한글 문자 |
| 사업자등록번호 | `\d{3}-\d{2}-\d{5}` | 123-45-67890 형식 |
### 패턴 해석 형식
패턴을 해석할 때는 다음 구조로 설명하세요:
```
패턴: ^(\d{3})-(\d{2})-(\d{5})$
해석:
^ → 문자열 시작
(\d{3}) → 캡처 그룹 1: 숫자 3자리
- → 하이픈 (리터럴)
(\d{2}) → 캡처 그룹 2: 숫자 2자리
- → 하이픈 (리터럴)
(\d{5}) → 캡처 그룹 3: 숫자 5자리
$ → 문자열 끝
```
## 테스트 형식
테스트 결과는 다음 형식으로 표시하세요:
```
패턴: \d{3}-\d{2}-\d{5}
✅ 매칭 성공:
"123-45-67890" → 전체 매칭: "123-45-67890"
"사업자번호: 123-45-67890입니다" → 부분 매칭: "123-45-67890"
❌ 매칭 실패:
"12-345-67890" → 형식 불일치
"abc-de-fghij" → 숫자가 아닌 문자
```
## 플래그 안내
| 플래그 | 설명 |
|--------|------|
| `i` | 대소문자 무시 |
| `m` | 멀티라인 (^$가 각 줄에 적용) |
| `s` | 점(.)이 줄바꿈도 매칭 |
| `g` | 전역 검색 (모든 매칭) |
## 규칙
- 패턴 생성 시 positive/negative 테스트 케이스를 반드시 포함
- 복잡한 패턴은 주석이 포함된 확장 모드(`x`)로 설명
- 캡처 그룹이 있으면 각 그룹의 의미를 설명
- 성능에 민감한 경우 탐욕적/게으른 수량자 선택 이유를 설명
- 최종 패턴은 clipboard_tool로 클립보드에 복사
한국어로 안내하세요.

View File

@@ -1,96 +0,0 @@
---
name: release-note
label: 릴리즈 노트 생성
description: 비개발자 대상의 사용자 친화적인 릴리즈 노트를 자동 생성합니다.
icon: \uE70B
tabs: code
allowed-tools:
- file_read
- file_write
- clipboard_tool
- html_create
---
사용자(비개발자) 대상의 읽기 쉬운 릴리즈 노트를 작성하세요.
## 작업 절차
1. **변경 정보 수집**: 사용자에게 다음 정보를 확인
- 버전 번호 (예: v1.6.0)
- 릴리즈 날짜
- 주요 변경 내용 (기능 추가, 개선, 버그 수정)
- 참고 자료: CHANGELOG, 커밋 이력, 개발 문서 등
2. **내용 분류**: 수집된 변경 사항을 사용자 관점으로 분류
- 기술 용어 → 사용자 언어로 변환
- 내부 리팩토링 등 사용자에게 무관한 항목은 제외
3. **릴리즈 노트 초안 작성**: 아래 템플릿 기반으로 작성
4. **사용자 검토**: 초안을 보여주고 수정 요청 반영
5. **최종 출력**: Markdown, HTML, 또는 텍스트 형식으로 저장
## 릴리즈 노트 템플릿
```markdown
# [제품명] v[버전] 업데이트 안내
안녕하세요. [제품명] v[버전] 업데이트 소식을 안내드립니다.
## 🎉 새로운 기능
- **[기능 이름]**: [사용자가 얻는 혜택 중심으로 1~2문장 설명]
- **[기능 이름]**: [사용자가 얻는 혜택 중심으로 1~2문장 설명]
## ✨ 개선 사항
- **[개선 영역]**: [무엇이 어떻게 좋아졌는지 설명]
- **[개선 영역]**: [무엇이 어떻게 좋아졌는지 설명]
## 🐛 문제 해결
- [사용자가 겪었던 문제]를 해결했습니다.
- [사용자가 겪었던 문제]를 해결했습니다.
## ⚠️ 알려진 이슈
- [현재 알려진 제한 사항이나 이슈]
- [해결 예정 시기 또는 임시 해결 방법]
---
문의 사항이 있으시면 [연락처/채널]로 알려주세요.
감사합니다.
```
## 작성 원칙
### 사용자 언어로 변환
| 개발 용어 (사용 금지) | 사용자 표현 (사용) |
|---------------------|-------------------|
| API 엔드포인트 추가 | 새로운 연동 기능 추가 |
| 메모리 누수 수정 | 장시간 사용 시 느려지는 문제 해결 |
| UI 리팩토링 | 화면 디자인 개선 |
| 캐시 최적화 | 실행 속도 개선 |
| null 참조 오류 수정 | 예기치 않은 오류로 종료되는 문제 해결 |
| 인코딩 이슈 수정 | 한글이 깨져 보이는 문제 해결 |
| 동시성 버그 수정 | 여러 작업 동시 실행 시 오류 발생 문제 해결 |
### 작성 규칙
- **혜택 중심**: "무엇을 했다"가 아닌 "사용자에게 어떤 도움이 되는지" 설명
- **간결**: 한 항목당 1~2문장 이내
- **구체적**: "성능 개선" → "파일 열기 속도가 약 2배 빨라졌습니다"
- **긍정적 톤**: 문제를 "해결했습니다", 기능을 "추가했습니다"
- **이모지 활용**: 섹션별 시각적 구분 (새 기능: 🎉, 개선: ✨, 수정: 🐛, 주의: ⚠️)
## 출력 형식
- **Markdown**: 기본 출력 형식 (.md)
- **HTML**: html_create로 스타일이 적용된 웹 페이지 생성
- **텍스트**: 이메일 본문용 서식 없는 텍스트
- 사용자가 원하는 형식으로 제공
## 규칙
- 내부 기술 구현 세부사항은 노출하지 않음
- 사용자에게 무관한 변경(코드 리팩토링, 테스트 추가 등)은 제외
- Breaking Change가 있으면 "이전 버전과 달라진 점" 섹션 추가
- 업데이트 방법 안내를 포함 (인스톨러 경로, 주의사항)
- 최종 결과는 파일 저장과 클립보드 복사 모두 제공
한국어로 안내하세요.

View File

@@ -1,41 +0,0 @@
---
name: report-writer
label: 보고서 작성
description: 작업 폴더의 데이터를 분석하여 체계적인 업무 보고서를 생성합<EC84B1><ED95A9>다.
icon: \uE9F9
allowed-tools:
- folder_map
- file_read
- file_write
- data_pivot
- chart_create
- template_render
- text_summarize
tabs: cowork
---
작업 폴더의 파일과 데이터를 분석하여 업무 보고서를 작성하세요.
다음 도구를 사용하세요:
1. folder_map — 작업 폴더의 파일 구조 파악
2. file_read — 관련 데이터 파일 읽기 (CSV, Excel, 텍스트)
3. file_write — 보고서 파일 생성 (HTML 또는 Markdown)
보고서 구성:
## 제목
- 작성 일시, 작성자 (요청 시)
## 요약 (Executive Summary)
- 핵심 내용을 3줄 이내로 요약
## 본문
- 데이터 기반 분석 결과
- 표/차트를 활용한 시각적 정리
- 주요 발견 사항
## 결론 및 제안
- 결론 요약
- 향후 조치 사항
HTML 보고서 생성 시 현재 적용된 디자인 무드를 반영하세요.
한국어로 작성하세요.

View File

@@ -1,93 +0,0 @@
---
name: security-audit
label: 보안 코드 감사
description: 코드베이스의 보안 취약점을 점검하고 OWASP 기반 보안 보고서를 생성합니다.
icon: \uE72E
allowed-tools:
- file_read
- grep
- folder_map
- search_codebase
- html_create
- glob
tabs: code
---
코드베이스의 보안 취약점을 체계적으로 점검하고 보고서를 생성하세요.
## 워크플로우
1. **스캔 범위 확인**: folder_map으로 프로젝트 구조 파악, 언어/프레임워크 식별
2. **취약점 패턴 탐지**: grep으로 위험 패턴 검색
3. **의존성 분석**: 패키지 파일(package.json, *.csproj, requirements.txt) 읽기
4. **비밀 정보 노출 검사**: API 키, 토큰, 비밀번호 하드코딩 탐지
5. **보고서 생성**: html_create로 보안 감사 보고서 생성
## OWASP Top 10 점검 항목
### A01 — 접근 제어 취약
- 인증 없는 API 엔드포인트
- 하드코딩된 권한 체크
- 관리자 경로 노출
### A02 — 암호화 실패
- 평문 비밀번호 저장
- 약한 해시 알고리즘 (MD5, SHA1)
- HTTP (비HTTPS) 통신
### A03 — 인젝션
- SQL 인젝션 (문자열 결합 쿼리)
- XSS (innerHTML, dangerouslySetInnerHTML)
- 명령 인젝션 (Process.Start, exec, system)
- 경로 순회 (../ 미검증)
### A04 — 불안전한 설계
- 비즈니스 로직 검증 누락
- 레이트 리밋 미적용
### A05 — 보안 설정 오류
- 디버그 모드 활성화 상태
- 기본 자격증명 사용
- 불필요한 포트/서비스 노출
### A07 — 인증 실패
- 약한 비밀번호 정책
- 세션 만료 미설정
- 브루트포스 방어 부재
### A09 — 로깅/모니터링 부족
- 보안 이벤트 미기록
- 민감 정보 로그 출력
## 비밀 정보 탐지 패턴
```
grep 대상 패턴:
- password\s*=\s*["'][^"']+["']
- api[_-]?key\s*=\s*["'][^"']+["']
- secret\s*=\s*["'][^"']+["']
- token\s*=\s*["'][A-Za-z0-9+/=]{20,}["']
- -----BEGIN (RSA |EC )?PRIVATE KEY-----
- AWS_ACCESS_KEY_ID
- AKIA[0-9A-Z]{16}
```
## 출력 형식
### 보안 감사 보고서
| 위험도 | 카테고리 | 파일 | 라인 | 내용 | 권장 조치 |
|--------|---------|------|------|------|----------|
| 🔴 심각 | A03 인젝션 | ... | ... | SQL 문자열 결합 | 파라미터 바인딩 사용 |
| 🟡 경고 | A02 암호화 | ... | ... | MD5 해시 사용 | SHA-256 이상 전환 |
| 🟢 참고 | A09 로깅 | ... | ... | 에러 로깅 미흡 | 보안 이벤트 로깅 추가 |
### 요약 통계
- 심각/경고/참고 건수
- OWASP 카테고리별 분포
- 우선 조치 항목 Top 5
## 규칙
- 코드를 수정하지 않음 (분석 + 보고서만)
- 발견된 비밀 정보는 마스킹하여 보고 (앞 4자만 표시)
- 위험도는 보수적으로 평가
- 한국어로 보고서 작성

View File

@@ -1,52 +0,0 @@
---
name: sql-report
label: SQL 리포트
description: DB 쿼리를 실행하고 결과를 차트화하여 HTML/Excel 보고서로 생성합니다.
icon: \uE968
allowed-tools:
- sql_tool
- chart_create
- excel_create
- html_create
- data_pivot
tabs: cowork
---
데이터베이스에서 쿼리를 실행하고 결과를 시각화된 보고서로 생성하세요.
## 워크플로우
1. **DB 확인**: sql_tool로 테이블 목록 및 스키마 확인
2. **쿼리 작성**: 사용자 요청에 맞는 SQL 쿼리 작성
3. **데이터 조회**: sql_tool로 쿼리 실행
4. **데이터 가공**: data_pivot으로 집계/피벗 (필요 시)
5. **시각화**: chart_create로 차트 생성
6. **보고서**: html_create 또는 excel_create로 최종 보고서 생성
## 쿼리 작성 원칙
- SELECT 쿼리만 실행 (데이터 변경 금지)
- 결과 행 수 제한: LIMIT 1000 (대량 데이터 방지)
- 인덱스 활용 쿼리 작성
- 한글 컬럼명은 alias로 변환
## 보고서 구성
### 1. 데이터 요약
- 조회 조건, 기간, 데이터 건수
- 기본 통계 (합계, 평균, 최대/최소)
### 2. 시각화
- 적절한 차트 유형 자동 선택
- 비교 → 바 차트
- 추세 → 라인 차트
- 비율 → 파이/도넛 차트
### 3. 상세 데이터
- Excel: 원본 데이터 + 서식 + 수식
- HTML: 테이블 + 정렬/필터
## 규칙
- DB 경로는 사용자에게 확인
- 민감 데이터(개인정보) 마스킹 권고
- 쿼리 실행 전 사용자 승인
- 한국어로 보고서 작성

Some files were not shown because too many files have changed in this diff Show More