메모리 규칙 경로 범위 적용을 위한 paths 프런트매터 지원 추가
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
계층형 메모리 문서에 YAML 유사 paths 프런트매터를 추가해 현재 작업 폴더 경로에 따라 규칙 적용 여부를 제어할 수 있도록 했습니다. AgentMemoryService에서 프런트매터를 파싱하고 프로젝트 루트 기준 상대 경로에 대해 *, **, ? glob 매칭을 수행하도록 구현했습니다. README와 DEVELOPMENT 문서에 메모리 규칙 범위 제어 기능과 동작 방식을 2026-04-07 00:22 (KST) 기준으로 반영했고, Release 빌드 경고 0 오류 0을 확인했습니다.
This commit is contained in:
@@ -405,12 +405,17 @@ public class AgentMemoryService
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
return;
|
||||
|
||||
var frontMatter = ParseFrontMatter(content);
|
||||
if (frontMatter.Paths.Count > 0 && !ShouldApplyToCurrentWorkFolder(frontMatter.Paths, projectRoot, _currentWorkFolder))
|
||||
return;
|
||||
|
||||
_instructionDocuments.Add(new MemoryInstructionDocument
|
||||
{
|
||||
Layer = layer,
|
||||
Label = label,
|
||||
Path = fullPath,
|
||||
Content = content.Trim()
|
||||
Content = frontMatter.Content.Trim(),
|
||||
Paths = frontMatter.Paths
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -613,6 +618,91 @@ public class AgentMemoryService
|
||||
}
|
||||
}
|
||||
|
||||
private static (string Content, List<string> Paths) ParseFrontMatter(string content)
|
||||
{
|
||||
var lines = content.Replace("\r\n", "\n").Split('\n').ToList();
|
||||
if (lines.Count < 3 || !string.Equals(lines[0].Trim(), "---", StringComparison.Ordinal))
|
||||
return (content, new List<string>());
|
||||
|
||||
var endIndex = -1;
|
||||
for (var i = 1; i < lines.Count; i++)
|
||||
{
|
||||
if (string.Equals(lines[i].Trim(), "---", StringComparison.Ordinal))
|
||||
{
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (endIndex < 1)
|
||||
return (content, new List<string>());
|
||||
|
||||
var paths = new List<string>();
|
||||
var inPaths = false;
|
||||
for (var i = 1; i < endIndex; i++)
|
||||
{
|
||||
var line = lines[i].Trim();
|
||||
if (line.StartsWith("paths:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
inPaths = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inPaths)
|
||||
{
|
||||
if (line.StartsWith("-", StringComparison.Ordinal))
|
||||
{
|
||||
var pattern = line[1..].Trim().Trim('"');
|
||||
if (!string.IsNullOrWhiteSpace(pattern))
|
||||
paths.Add(pattern);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.Length > 0)
|
||||
inPaths = false;
|
||||
}
|
||||
}
|
||||
|
||||
var stripped = string.Join("\n", lines.Skip(endIndex + 1)).Trim();
|
||||
return (stripped, paths);
|
||||
}
|
||||
|
||||
private static bool ShouldApplyToCurrentWorkFolder(IReadOnlyList<string> patterns, string? projectRoot, string? currentWorkFolder)
|
||||
{
|
||||
if (patterns.Count == 0)
|
||||
return true;
|
||||
if (string.IsNullOrWhiteSpace(projectRoot) || string.IsNullOrWhiteSpace(currentWorkFolder))
|
||||
return false;
|
||||
|
||||
var relative = Path.GetRelativePath(projectRoot, currentWorkFolder).Replace('\\', '/');
|
||||
if (string.Equals(relative, ".", StringComparison.Ordinal))
|
||||
relative = "";
|
||||
|
||||
return patterns.Any(pattern => GlobMatches(relative, pattern));
|
||||
}
|
||||
|
||||
private static bool GlobMatches(string relativePath, string pattern)
|
||||
{
|
||||
var normalizedPattern = (pattern ?? "").Replace('\\', '/').Trim();
|
||||
var normalizedPath = (relativePath ?? "").Replace('\\', '/').Trim('/');
|
||||
if (string.IsNullOrWhiteSpace(normalizedPattern))
|
||||
return false;
|
||||
|
||||
var regex = "^" + System.Text.RegularExpressions.Regex.Escape(normalizedPattern)
|
||||
.Replace(@"\*\*", "§§DOUBLESTAR§§")
|
||||
.Replace(@"\*", "[^/]*")
|
||||
.Replace(@"\?", "[^/]")
|
||||
.Replace("§§DOUBLESTAR§§", ".*")
|
||||
+ "$";
|
||||
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(normalizedPath, regex, System.Text.RegularExpressions.RegexOptions.IgnoreCase))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrEmpty(normalizedPath))
|
||||
normalizedPath += "/";
|
||||
return System.Text.RegularExpressions.Regex.IsMatch(normalizedPath, regex, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>텍스트를 토큰으로 분리합니다.</summary>
|
||||
private static HashSet<string> Tokenize(string text)
|
||||
{
|
||||
@@ -680,4 +770,7 @@ public class MemoryInstructionDocument
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("paths")]
|
||||
public List<string> Paths { get; set; } = new();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user