diff --git a/src/CompilerBrain/CompilerBrainAIFunctions.cs b/src/CompilerBrain/CompilerBrainAIFunctions.cs index f91a76b..3cfd161 100644 --- a/src/CompilerBrain/CompilerBrainAIFunctions.cs +++ b/src/CompilerBrain/CompilerBrainAIFunctions.cs @@ -38,6 +38,7 @@ public IEnumerable GetAIFunctions() yield return AIFunctionFactory.Create(GetProjects, factoryOptions); yield return AIFunctionFactory.Create(GetDiagnostics, factoryOptions); yield return AIFunctionFactory.Create(ReadImportantInformationFiles, factoryOptions); + yield return AIFunctionFactory.Create(ReadContextFiles, factoryOptions); yield return AIFunctionFactory.Create(ReadCode, factoryOptions); yield return AIFunctionFactory.Create(ReadManyCodes, factoryOptions); yield return AIFunctionFactory.Create(AddOrReplaceCode, factoryOptions); @@ -82,6 +83,12 @@ public RootFile ReadImportantInformationFiles() return rootFile; } + [Description("Read context files (AGENTS.md, CLAUDE.md) from working directory or solution root.")] + public ContextFiles ReadContextFiles() + { + return ContextFileLoader.Load(memory.Solution?.FilePath); + } + [Description("Read existing code in current session context, if not found returns null.")] public string? ReadCode(string projectName, string fileNameOrFullPath) { diff --git a/src/CompilerBrain/CompilerBrainChatService.cs b/src/CompilerBrain/CompilerBrainChatService.cs index e4cef10..e84a723 100644 --- a/src/CompilerBrain/CompilerBrainChatService.cs +++ b/src/CompilerBrain/CompilerBrainChatService.cs @@ -42,6 +42,7 @@ public async Task ContinueAsync(ResponseContinuationToken cont string CreateInstructions(SessionMemory memory) { var runtimeInfo = MachineRuntimeInformation.FromCurrent(); + var contextFiles = ContextFileLoader.Load(memory.Solution?.FilePath); var inst = $$""" @@ -70,6 +71,11 @@ string CreateInstructions(SessionMemory memory) - **Avoid unnecessary output** No pleasantries or greetings """; + foreach (var (fileName, content) in contextFiles.Files) + { + inst += $"\n\n# {fileName}\n\n{content}"; + } + return inst; } } diff --git a/src/CompilerBrain/ContextFileLoader.cs b/src/CompilerBrain/ContextFileLoader.cs new file mode 100644 index 0000000..d03862c --- /dev/null +++ b/src/CompilerBrain/ContextFileLoader.cs @@ -0,0 +1,59 @@ +namespace CompilerBrain; + +public static class ContextFileLoader +{ + static readonly string[] FileNames = ["AGENTS.md", "CLAUDE.md"]; + + public static ContextFiles Load(string? solutionPath) + { + var files = new Dictionary(); + + // Build search directories: working directory first, then solution root + var searchDirs = new List { Environment.CurrentDirectory }; + + if (!string.IsNullOrEmpty(solutionPath)) + { + var solutionDir = Path.GetDirectoryName(solutionPath); + if (!string.IsNullOrEmpty(solutionDir) && solutionDir != Environment.CurrentDirectory) + { + searchDirs.Add(solutionDir); + } + } + + var seenContents = new HashSet(); + foreach (var fileName in FileNames) + { + var content = FindAndReadFile(searchDirs, fileName); + if (content != null && seenContents.Add(content)) + { + files[fileName] = content; + } + } + + return new ContextFiles(files); + } + + static string? FindAndReadFile(List searchDirs, string fileName) + { + foreach (var dir in searchDirs) + { + try + { + foreach (var file in Directory.EnumerateFiles(dir)) + { + var name = Path.GetFileName(file); + if (name.Equals(fileName, StringComparison.OrdinalIgnoreCase)) + { + return File.ReadAllText(file); + } + } + } + catch + { + // Directory access error - continue to next directory + } + } + + return null; + } +} diff --git a/src/CompilerBrain/RequestResponseTypes.cs b/src/CompilerBrain/RequestResponseTypes.cs index 8d192a3..d496a38 100644 --- a/src/CompilerBrain/RequestResponseTypes.cs +++ b/src/CompilerBrain/RequestResponseTypes.cs @@ -50,6 +50,8 @@ public record struct RootFile public string? EditorConfig { get; set; } } +public record struct ContextFiles(Dictionary Files); + public readonly record struct CodeLocation(int Start, int Length); public readonly record struct ReadManyCodesResult(string FilePath, string Code);