AX Agent 메모리 편집 UI 추가 및 계층형 메모리 관리 흐름 보강
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- AX Agent 설정의 에이전트 메모리 섹션에 관리형/사용자/프로젝트/로컬 메모리 편집 버튼 추가 - 계층형 메모리 파일을 현재 테마 기반 다이얼로그에서 직접 열고 저장/삭제할 수 있는 편집 UI 구현 - description 및 paths frontmatter 예시를 편집기 안내에 포함해 claw-code 수준의 메모리 규칙 작성 흐름 보강 - 저장 후 메모리 계층을 즉시 다시 로드하도록 연결해 /memory 명령과 설정 UI가 같은 상태를 보도록 정리 - README 및 DEVELOPMENT 문서에 2026-04-07 00:45 (KST) 기준 작업 이력 반영 검증 결과 - 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:
@@ -1408,3 +1408,6 @@ MIT License
|
||||
- 업데이트: 2026-04-07 00:39 (KST)
|
||||
- [AgentMemoryService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AgentMemoryService.cs)에 계층형 메모리 우선순위/병합 정책을 추가했습니다. 같은 내용의 규칙이 여러 계층에 중복될 경우 더 가까운 규칙만 남기고, 최종 메모리 문서는 `managed → user → project → local` 순으로 다시 정렬됩니다.
|
||||
- [MemoryTool.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/MemoryTool.cs) 의 `list/search`는 이제 최종 우선순위 번호를 같이 보여줘, 어떤 규칙이 실제로 더 강하게 적용되는지 바로 확인할 수 있습니다.
|
||||
- 업데이트: 2026-04-07 00:45 (KST)
|
||||
- AX Copilot 메인 설정의 에이전트 메모리 영역에서 `관리형 / 사용자 / 프로젝트 / 로컬` 메모리 파일을 직접 열어 수정할 수 있게 했습니다. [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml), [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs)에 계층형 메모리 편집 버튼과 전용 편집 다이얼로그를 추가했습니다.
|
||||
- 메모리 편집 다이얼로그는 현재 테마를 따르는 안내 패널과 멀티라인 편집기를 제공하고, `description`/`paths` frontmatter 예시를 바로 볼 수 있습니다. 저장 시 해당 scope 파일을 즉시 갱신하고, 빈 내용으로 저장하면 파일을 삭제한 뒤 메모리 계층을 다시 로드합니다.
|
||||
|
||||
@@ -5195,3 +5195,13 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
|
||||
- [MemoryTool.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/MemoryTool.cs)
|
||||
- `list`, `search` 출력에 계층형 메모리 문서의 우선순위를 함께 노출하도록 정리했다.
|
||||
- 사용자는 이제 `/memory` 결과에서 어떤 규칙이 실제로 더 강하게 적용되는지 바로 읽을 수 있다.
|
||||
|
||||
## 2026-04-07 00:45 (KST)
|
||||
|
||||
- [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml)
|
||||
- 에이전트 메모리 섹션에 `관리형 편집`, `사용자 편집`, `프로젝트 편집`, `로컬 편집` 버튼을 추가했다.
|
||||
- 메모리 관리는 `/memory` 명령만이 아니라 설정 UI에서도 직접 접근 가능하도록 확장했고, 같은 섹션 안에서 학습 메모리 초기화와 계층형 메모리 편집을 함께 다룰 수 있게 정리했다.
|
||||
- [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs)
|
||||
- scope별 메모리 파일 경로를 계산해 실제 내용을 읽고 수정할 수 있는 `OpenMemoryScopeEditor(...)`를 추가했다.
|
||||
- 편집 다이얼로그는 현재 테마 색을 사용하고, `description`/`paths` frontmatter 예시를 안내로 보여준다.
|
||||
- 저장 시 해당 scope 메모리 파일을 즉시 갱신하고, 빈 내용이면 파일을 삭제한 뒤 `AgentMemoryService.Load(...)`를 다시 호출해 메모리 계층이 곧바로 반영되도록 했다.
|
||||
|
||||
@@ -114,6 +114,36 @@
|
||||
<Setter Property="Margin" Value="2,18,0,8"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MemoryScopeButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource ItemBackground}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryText}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="0,0,8,8"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ItemHoverBackground}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource AccentColor}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<!-- ─── 도움말 아이콘 (? 버튼 + 커스텀 툴팁) ──────────────────────── -->
|
||||
<Style x:Key="HelpTooltipStyle" TargetType="ToolTip">
|
||||
@@ -4860,15 +4890,21 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource AgentSettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="메모리 관리"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="현재 작업 폴더의 메모리를 확인하거나 초기화합니다."/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Content="메모리 초기화" Click="BtnClearMemory_Click"
|
||||
Background="#DC2626" Foreground="White" FontSize="12"
|
||||
<Border Style="{StaticResource AgentSettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="메모리 관리"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="계층형 메모리 파일을 직접 열어 수정하거나, 학습 메모리를 초기화합니다."/>
|
||||
<WrapPanel Margin="0,10,0,0">
|
||||
<Button Style="{StaticResource MemoryScopeButton}" Content="관리형 편집" Click="BtnEditManagedMemory_Click"/>
|
||||
<Button Style="{StaticResource MemoryScopeButton}" Content="사용자 편집" Click="BtnEditUserMemory_Click"/>
|
||||
<Button Style="{StaticResource MemoryScopeButton}" Content="프로젝트 편집" Click="BtnEditProjectMemory_Click"/>
|
||||
<Button Style="{StaticResource MemoryScopeButton}" Content="로컬 편집" Click="BtnEditLocalMemory_Click"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Content="메모리 초기화" Click="BtnClearMemory_Click"
|
||||
Background="#DC2626" Foreground="White" FontSize="12"
|
||||
Padding="14,6" Cursor="Hand" BorderThickness="0" Margin="8,0,0,0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Windows.Data;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -2981,6 +2982,214 @@ public partial class SettingsWindow : Window
|
||||
CustomMessageBox.Show("에이전트 메모리가 초기화되었습니다.", "완료", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void BtnEditManagedMemory_Click(object sender, RoutedEventArgs e) => OpenMemoryScopeEditor("managed", "관리형 메모리");
|
||||
private void BtnEditUserMemory_Click(object sender, RoutedEventArgs e) => OpenMemoryScopeEditor("user", "사용자 메모리");
|
||||
private void BtnEditProjectMemory_Click(object sender, RoutedEventArgs e) => OpenMemoryScopeEditor("project", "프로젝트 메모리");
|
||||
private void BtnEditLocalMemory_Click(object sender, RoutedEventArgs e) => OpenMemoryScopeEditor("local", "로컬 메모리");
|
||||
|
||||
private void OpenMemoryScopeEditor(string scope, string title)
|
||||
{
|
||||
var app = System.Windows.Application.Current as App;
|
||||
var memory = app?.MemoryService;
|
||||
if (memory == null)
|
||||
{
|
||||
CustomMessageBox.Show("메모리 서비스를 사용할 수 없습니다.", "오류", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var workFolder = _vm.Service.Settings.Llm.WorkFolder;
|
||||
var path = memory.GetWritableInstructionPath(scope, workFolder);
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
CustomMessageBox.Show("해당 메모리 파일 경로를 결정할 수 없습니다. 작업 폴더를 먼저 확인하세요.", "메모리 편집", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = memory.ReadInstructionFile(scope, workFolder) ?? "";
|
||||
var bgBrush = TryFindResource("LauncherBackground") as Brush ?? Brushes.White;
|
||||
var fgBrush = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
||||
var subFgBrush = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.LightGray;
|
||||
var itemBg = TryFindResource("ItemBackground") as Brush ?? Brushes.WhiteSmoke;
|
||||
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? Brushes.Gainsboro;
|
||||
var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = title,
|
||||
Width = 760,
|
||||
Height = 620,
|
||||
MinWidth = 620,
|
||||
MinHeight = 480,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this,
|
||||
Background = bgBrush,
|
||||
Foreground = fgBrush,
|
||||
ShowInTaskbar = false
|
||||
};
|
||||
|
||||
var root = new Grid { Margin = new Thickness(18) };
|
||||
root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
root.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
|
||||
var header = new StackPanel { Margin = new Thickness(0, 0, 0, 12) };
|
||||
header.Children.Add(new TextBlock
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 18,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = fgBrush
|
||||
});
|
||||
header.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"경로: {path}",
|
||||
FontSize = 12,
|
||||
Foreground = subFgBrush,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(0, 6, 0, 0)
|
||||
});
|
||||
Grid.SetRow(header, 0);
|
||||
root.Children.Add(header);
|
||||
|
||||
var hint = new Border
|
||||
{
|
||||
Background = itemBg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(12, 10, 12, 10),
|
||||
Margin = new Thickness(0, 0, 0, 12),
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = "frontmatter 예시:\n---\ndescription: 결제 모듈 규칙\npaths:\n - src/Payments/**\n---\n\n메모리 내용은 /memory 명령과 함께 사용되며, 빈 내용으로 저장하면 파일이 삭제됩니다.",
|
||||
FontSize = 12,
|
||||
Foreground = subFgBrush,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
LineHeight = 18
|
||||
}
|
||||
};
|
||||
Grid.SetRow(hint, 1);
|
||||
root.Children.Add(hint);
|
||||
|
||||
var editor = new TextBox
|
||||
{
|
||||
Text = existing,
|
||||
AcceptsReturn = true,
|
||||
AcceptsTab = true,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
||||
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
|
||||
Background = Brushes.White,
|
||||
Foreground = fgBrush,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
Padding = new Thickness(14),
|
||||
FontSize = 13,
|
||||
FontFamily = new FontFamily("Consolas, Malgun Gothic, Segoe UI")
|
||||
};
|
||||
Grid.SetRow(editor, 2);
|
||||
root.Children.Add(editor);
|
||||
|
||||
var footer = new Grid { Margin = new Thickness(0, 14, 0, 0) };
|
||||
footer.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
footer.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
footer.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
|
||||
var openFolder = new Border
|
||||
{
|
||||
Background = itemBg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(12, 7, 12, 7),
|
||||
Cursor = Cursors.Hand,
|
||||
Child = new TextBlock { Text = "폴더 열기", FontSize = 12, Foreground = fgBrush }
|
||||
};
|
||||
openFolder.MouseEnter += (_, _) => openFolder.Background = hoverBg;
|
||||
openFolder.MouseLeave += (_, _) => openFolder.Background = itemBg;
|
||||
openFolder.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "explorer.exe",
|
||||
Arguments = $"/select,\"{path}\"",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CustomMessageBox.Show($"폴더를 열 수 없습니다.\n{ex.Message}", "메모리 편집", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
};
|
||||
Grid.SetColumn(openFolder, 0);
|
||||
footer.Children.Add(openFolder);
|
||||
|
||||
var cancelBtn = new Border
|
||||
{
|
||||
Background = itemBg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(16, 7, 16, 7),
|
||||
Cursor = Cursors.Hand,
|
||||
Margin = new Thickness(8, 0, 0, 0),
|
||||
Child = new TextBlock { Text = "취소", FontSize = 12, Foreground = fgBrush }
|
||||
};
|
||||
cancelBtn.MouseEnter += (_, _) => cancelBtn.Background = hoverBg;
|
||||
cancelBtn.MouseLeave += (_, _) => cancelBtn.Background = itemBg;
|
||||
cancelBtn.MouseLeftButtonUp += (_, _) => dlg.Close();
|
||||
Grid.SetColumn(cancelBtn, 1);
|
||||
footer.Children.Add(cancelBtn);
|
||||
|
||||
var saveBtn = new Border
|
||||
{
|
||||
Background = accentBrush,
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(16, 7, 16, 7),
|
||||
Cursor = Cursors.Hand,
|
||||
Margin = new Thickness(8, 0, 0, 0),
|
||||
Child = new TextBlock { Text = "저장", FontSize = 12, FontWeight = FontWeights.SemiBold, Foreground = Brushes.White }
|
||||
};
|
||||
saveBtn.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
var text = editor.Text.Trim();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText(path, editor.Text.Replace("\r\n", "\n"));
|
||||
}
|
||||
|
||||
memory.Load(workFolder);
|
||||
CustomMessageBox.Show("메모리 파일이 저장되었습니다.", "메모리 편집", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
dlg.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CustomMessageBox.Show($"메모리 파일 저장에 실패했습니다.\n{ex.Message}", "메모리 편집", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
};
|
||||
Grid.SetColumn(saveBtn, 2);
|
||||
footer.Children.Add(saveBtn);
|
||||
|
||||
Grid.SetRow(footer, 3);
|
||||
root.Children.Add(footer);
|
||||
|
||||
dlg.Content = root;
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
// ─── 에이전트 훅 관리 ─────────────────────────────────────────────────
|
||||
|
||||
private void AddHookBtn_Click(object sender, MouseButtonEventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user