diff --git a/docs/AGENT_ROADMAP.md b/docs/AGENT_ROADMAP.md
index 323783d..c7160c6 100644
--- a/docs/AGENT_ROADMAP.md
+++ b/docs/AGENT_ROADMAP.md
@@ -241,13 +241,16 @@
| 17-D2 | **경로 기반 스킬 활성화** | paths: 프론트매터 — 해당 파일 작업 시 스킬 자동 컨텍스트 주입 | 높음 |
| 17-D3 | **스킬 범위 훅·모델 오버라이드** | hooks: (스킬 범위 훅), model: (스킬별 모델), user-invocable:false (AI 전용) | 중간 |
-### Group E — 메모리/컨텍스트 고도화 (CC 메모리 문서 기반)
+### Group E — 메모리/컨텍스트 고도화 (CC 메모리 문서 기반) ✅ 완료
-| # | 기능 | 설명 | 우선순위 |
-|---|------|------|----------|
-| 17-E1 | **@include 지시어** | AX.md / .ax/rules에서 @파일경로로 외부 파일 포함. 최대 5단계 | 높음 |
-| 17-E2 | **경로 기반 규칙 주입** | .ax/rules/*.md의 paths: — 해당 파일 작업 시만 규칙 주입 | 높음 |
-| 17-E3 | **컨텍스트 컴팩션 + 파일 되감기** | /compact 명령, PreCompact/PostCompact 훅, 파일 변경 되감기 | 중간 |
+| # | 기능 | 설명 | 우선순위 | 구현 |
+|---|------|------|----------|------|
+| 17-E1 | **@include 지시어** | AX.md / .ax/rules에서 @파일경로로 외부 파일 포함. 최대 5단계 | 높음 | AxMdIncludeResolver.ResolveAsync() — AgentLoopService.Memory.cs에서 세션 시작 시 호출 |
+| 17-E2 | **경로 기반 규칙 주입** | .ax/rules/*.md의 paths: — 해당 파일 작업 시만 규칙 주입 | 높음 | PathScopedRuleInjector — 파일 도구 결과 후 InjectPathScopedRulesAsync() 호출 |
+| 17-E3 | **컨텍스트 컴팩션 + 파일 되감기** | /compact 명령, PreCompact/PostCompact 훅, 파일 변경 되감기 | 중간 | — (차기 Phase) |
+
+새 파일: `AgentLoopService.Memory.cs` (105줄) — `InjectHierarchicalMemoryAsync()` + `InjectPathScopedRulesAsync()`
+설정 추가: `EnableMemorySystem` (기본 true)
### Group F — 권한 시스템 고도화 (CC 권한 문서 기반)
diff --git a/src/AxCopilot/Models/AppSettings.LlmSettings.cs b/src/AxCopilot/Models/AppSettings.LlmSettings.cs
index 405d78a..16c1e9f 100644
--- a/src/AxCopilot/Models/AppSettings.LlmSettings.cs
+++ b/src/AxCopilot/Models/AppSettings.LlmSettings.cs
@@ -360,6 +360,10 @@ public class LlmSettings
[JsonPropertyName("enableSkillSystem")]
public bool EnableSkillSystem { get; set; } = true;
+ /// 계층 메모리 시스템 활성화 여부 (AX.md @include 해석 + 경로 범위 규칙). 기본 true.
+ [JsonPropertyName("enableMemorySystem")]
+ public bool EnableMemorySystem { get; set; } = true;
+
/// 추가 스킬 폴더 경로. 빈 문자열이면 기본 폴더만 사용.
[JsonPropertyName("skillsFolderPath")]
public string SkillsFolderPath { get; set; } = "";
diff --git a/src/AxCopilot/Services/Agent/AgentLoopService.Execution.cs b/src/AxCopilot/Services/Agent/AgentLoopService.Execution.cs
index 8a43175..aa672bb 100644
--- a/src/AxCopilot/Services/Agent/AgentLoopService.Execution.cs
+++ b/src/AxCopilot/Services/Agent/AgentLoopService.Execution.cs
@@ -311,6 +311,9 @@ public partial class AgentLoopService
// Phase 17-D: paths: glob 패턴 매칭 스킬 자동 주입
InjectPathBasedSkills(result.FilePath, messages);
+ // Phase 17-E: .ax/rules/*.md 경로 범위 규칙 주입
+ _ = InjectPathScopedRulesAsync(result.FilePath, messages, CancellationToken.None);
+
// ToolResultSizer 적용
var sizedResult = ToolResultSizer.Apply(result.Output ?? "", call.ToolName);
messages.Add(LlmService.CreateToolResultMessage(call.ToolId, call.ToolName, sizedResult.Output));
diff --git a/src/AxCopilot/Services/Agent/AgentLoopService.Memory.cs b/src/AxCopilot/Services/Agent/AgentLoopService.Memory.cs
new file mode 100644
index 0000000..e2326db
--- /dev/null
+++ b/src/AxCopilot/Services/Agent/AgentLoopService.Memory.cs
@@ -0,0 +1,162 @@
+using AxCopilot.Models;
+
+namespace AxCopilot.Services.Agent;
+
+///
+/// Phase 17-E: AgentLoopService — 계층 메모리/컨텍스트 고도화 통합.
+/// · 4-layer 계층 메모리 (Managed→User→Project→Local) 시스템 메시지 주입
+/// · @include 지시어 재귀 해석 (최대 5단계, 순환 참조 감지)
+/// · .ax/rules/*.md paths: 프론트매터 기반 경로 범위 규칙 주입
+///
+public partial class AgentLoopService
+{
+ // 세션 공유 서비스 인스턴스 (상태 없음 → 정적)
+ private static readonly HierarchicalMemoryService _hierarchicalMemory = new();
+ private static readonly AxMdIncludeResolver _axMdResolver = new();
+
+ // workFolder별 PathScopedRuleInjector 캐시 (rules 폴더 로드 비용 절감)
+ private PathScopedRuleInjector? _pathRuleInjector;
+ private string? _pathRuleInjectorFolder;
+
+ // 시스템 메시지 내 마커 (in-place 교체, 중복 방지)
+ private const string MemoryMarker = "## 프로젝트 메모리 (계층 컨텍스트)";
+ private const string PathRulesMarker = "## 현재 파일에 적용된 경로 규칙";
+
+ // ─────────────────────────────────────────────────────────────────────
+ // 세션 시작 시 계층 메모리 주입
+ // ─────────────────────────────────────────────────────────────────────
+
+ ///
+ /// 4-layer 계층 메모리(AX.md 파일군)를 수집하고 @include 지시어를 재귀 해석하여
+ /// 시스템 메시지에 주입합니다. 세션 시작 시 한 번만 호출합니다.
+ ///
+ internal async Task InjectHierarchicalMemoryAsync(
+ List messages, string? workFolder, CancellationToken ct)
+ {
+ if (!(_settings.Settings.Llm.EnableMemorySystem)) return;
+
+ try
+ {
+ // 4-layer 메모리 병합 문자열 생성
+ var merged = _hierarchicalMemory.BuildMergedContext(workFolder);
+ if (string.IsNullOrWhiteSpace(merged)) return;
+
+ // @include 지시어 재귀 해석
+ var basePath = string.IsNullOrWhiteSpace(workFolder)
+ ? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
+ : workFolder;
+
+ var resolved = await _axMdResolver.ResolveAsync(merged, basePath);
+
+ if (string.IsNullOrWhiteSpace(resolved)) return;
+
+ // 크기 경고 (40,000자 초과 시)
+ if (AxMdIncludeResolver.IsOversized(resolved))
+ LogService.Warn($"[Memory] 계층 메모리 컨텍스트가 40,000자를 초과합니다 ({resolved.Length:N0}자).");
+
+ var injection = $"{MemoryMarker}\n\n{resolved.TrimEnd()}";
+
+ // 시스템 메시지에 in-place 삽입 (마커 기반 중복 방지)
+ var sysMsg = messages.FirstOrDefault(m => m.Role == "system");
+ if (sysMsg != null)
+ {
+ var idx = sysMsg.Content.IndexOf(MemoryMarker, StringComparison.Ordinal);
+ sysMsg.Content = idx >= 0
+ ? sysMsg.Content[..idx] + injection
+ : sysMsg.Content + "\n\n" + injection;
+ }
+ else
+ {
+ messages.Insert(0, new ChatMessage { Role = "system", Content = injection });
+ }
+
+ var fileCount = _hierarchicalMemory.CollectAll(workFolder).Count;
+ EmitEvent(AgentEventType.Thinking, "memory",
+ $"계층 메모리 주입됨: {fileCount}개 파일, {resolved.Length:N0}자");
+
+ _ = _eventLog?.AppendAsync(AgentEventLogType.SkillActivated,
+ System.Text.Json.JsonSerializer.Serialize(new
+ {
+ type = "hierarchical_memory",
+ fileCount,
+ charCount = resolved.Length,
+ workFolder = workFolder ?? ""
+ }));
+ }
+ catch (OperationCanceledException) { /* 취소 시 무시 */ }
+ catch (Exception ex)
+ {
+ LogService.Warn($"[Memory] 계층 메모리 주입 실패: {ex.Message}");
+ }
+ }
+
+ // ─────────────────────────────────────────────────────────────────────
+ // 파일 도구 호출 후 경로 범위 규칙 주입
+ // ─────────────────────────────────────────────────────────────────────
+
+ ///
+ /// 파일 도구 실행 후, 해당 파일에 적용 가능한 .ax/rules/*.md 경로 범위 규칙을
+ /// 시스템 메시지에 주입합니다. 마커 기반 in-place 교체로 중복을 방지합니다.
+ ///
+ internal async Task InjectPathScopedRulesAsync(
+ string? filePath, List messages, CancellationToken ct)
+ {
+ if (!(_settings.Settings.Llm.EnableMemorySystem)) return;
+ if (string.IsNullOrWhiteSpace(filePath)) return;
+
+ var workFolder = _settings.Settings.Llm.WorkFolder;
+ if (string.IsNullOrWhiteSpace(workFolder)) return;
+
+ try
+ {
+ // workFolder 변경 시 injector 재생성 및 규칙 재로드
+ if (_pathRuleInjector == null || _pathRuleInjectorFolder != workFolder)
+ {
+ _pathRuleInjector = new PathScopedRuleInjector(workFolder);
+ _pathRuleInjectorFolder = workFolder;
+ await _pathRuleInjector.LoadRulesAsync();
+ }
+
+ var activeRules = _pathRuleInjector.GetActiveRulesForFile(filePath);
+ if (activeRules.Count == 0) return;
+
+ var injection = $"{PathRulesMarker}\n\n{_pathRuleInjector.BuildInjection(activeRules).TrimEnd()}";
+
+ // 시스템 메시지에 in-place 삽입 (마커 기반 중복 방지)
+ var sysMsg = messages.FirstOrDefault(m => m.Role == "system");
+ if (sysMsg != null)
+ {
+ var idx = sysMsg.Content.IndexOf(PathRulesMarker, StringComparison.Ordinal);
+ sysMsg.Content = idx >= 0
+ ? sysMsg.Content[..idx] + injection
+ : sysMsg.Content + "\n\n" + injection;
+ }
+ else
+ {
+ messages.Insert(0, new ChatMessage { Role = "system", Content = injection });
+ }
+
+ var ruleNames = string.Join(", ", activeRules
+ .Select(r => string.IsNullOrEmpty(r.Frontmatter.Name)
+ ? System.IO.Path.GetFileName(r.FilePath)
+ : r.Frontmatter.Name));
+
+ EmitEvent(AgentEventType.Thinking, "path_rules",
+ $"경로 규칙 주입됨: {activeRules.Count}개 ({ruleNames})");
+
+ _ = _eventLog?.AppendAsync(AgentEventLogType.SkillActivated,
+ System.Text.Json.JsonSerializer.Serialize(new
+ {
+ type = "path_scoped_rules",
+ filePath,
+ ruleCount = activeRules.Count,
+ ruleNames
+ }));
+ }
+ catch (OperationCanceledException) { /* 취소 시 무시 */ }
+ catch (Exception ex)
+ {
+ LogService.Warn($"[Memory] 경로 범위 규칙 주입 실패: {ex.Message}");
+ }
+ }
+}
diff --git a/src/AxCopilot/Services/Agent/AgentLoopService.cs b/src/AxCopilot/Services/Agent/AgentLoopService.cs
index cee74e9..2a0fa0f 100644
--- a/src/AxCopilot/Services/Agent/AgentLoopService.cs
+++ b/src/AxCopilot/Services/Agent/AgentLoopService.cs
@@ -226,6 +226,9 @@ public partial class AgentLoopService
// Phase 17-A: Reflexion — 과거 교훈을 시스템 메시지에 주입
await InjectReflexionContextAsync(messages, userQuery);
+ // Phase 17-E: 4-layer 계층 메모리 + @include 해석 → 시스템 메시지 주입
+ await InjectHierarchicalMemoryAsync(messages, llm.WorkFolder, ct);
+
try
{
// ── 플랜 모드 "always": 첫 번째 호출은 계획만 생성 (도구 없이) ──