From a08087c93639877daeb5aa47cd71ed393ee2ab75 Mon Sep 17 00:00:00 2001 From: Amolith Date: Wed, 17 Sep 2025 20:11:15 -0600 Subject: [PATCH] feat(prompt): distinguish user/project context Separate user-defined memory paths from project-specific context paths. Previously, memory paths were appended to context paths. Now, they are passed distinctly to the prompt generator. The CoderPrompt now formats project context within tags and user memory within tags, each with a specific explanatory header for the LLM. This allows for clearer separation and potential prioritization by the model. Issue: charmbracelet/crush#1050 --- internal/agent/prompt/prompt.go | 25 ++++++++++++++++++++----- internal/agent/templates/coder.md.tpl | 18 +++++++++++++++++- internal/config/load.go | 6 ++---- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/internal/agent/prompt/prompt.go b/internal/agent/prompt/prompt.go index d10fbcae3c3a37f295ec9f9de637cb130d9b6abc..216e7d32c0aebd4415b75885bb5bc523dae499db 100644 --- a/internal/agent/prompt/prompt.go +++ b/internal/agent/prompt/prompt.go @@ -35,6 +35,7 @@ type PromptDat struct { Date string GitStatus string ContextFiles []ContextFile + MemoryFiles []ContextFile } type ContextFile struct { @@ -150,16 +151,27 @@ func (p *Prompt) promptData(ctx context.Context, provider, model string, cfg con workingDir := cmp.Or(p.workingDir, cfg.WorkingDir()) platform := cmp.Or(p.platform, runtime.GOOS) - files := map[string][]ContextFile{} + contextFiles := map[string][]ContextFile{} + memoryFiles := map[string][]ContextFile{} for _, pth := range cfg.Options.ContextPaths { expanded := expandPath(pth, cfg) pathKey := strings.ToLower(expanded) - if _, ok := files[pathKey]; ok { + if _, ok := contextFiles[pathKey]; ok { continue } content := processContextPath(expanded, cfg) - files[pathKey] = content + contextFiles[pathKey] = content + } + + for _, pth := range cfg.Options.MemoryPaths { + expanded := expandPath(pth, cfg) + pathKey := strings.ToLower(expanded) + if _, ok := memoryFiles[pathKey]; ok { + continue + } + content := processContextPath(expanded, cfg) + memoryFiles[pathKey] = content } isGit := isGitRepo(cfg.WorkingDir()) @@ -180,8 +192,11 @@ func (p *Prompt) promptData(ctx context.Context, provider, model string, cfg con } } - for _, contextFiles := range files { - data.ContextFiles = append(data.ContextFiles, contextFiles...) + for _, files := range contextFiles { + data.ContextFiles = append(data.ContextFiles, files...) + } + for _, files := range memoryFiles { + data.MemoryFiles = append(data.MemoryFiles, files...) } return data, nil } diff --git a/internal/agent/templates/coder.md.tpl b/internal/agent/templates/coder.md.tpl index 363b59da2624405684bab9983204b795fb177471..9ec729d168915a3ab7c90ae9fd5e5db20b7b70e8 100644 --- a/internal/agent/templates/coder.md.tpl +++ b/internal/agent/templates/coder.md.tpl @@ -339,10 +339,26 @@ Diagnostics (lint/typecheck) included in tool output. {{if .ContextFiles}} +# Project-Specific Context +Make sure to follow the instructions in the context below. + {{range .ContextFiles}} {{.Content}} {{end}} - + + +{{if .MemoryFiles}} +# User context +The following is personal content added by the user that they'd like you to follow no matter what project you're working in, though project preferences might conflict with their personal preferences. In cases of conflict, prioritise project context over user context. + +{{range .MemoryFiles}} + +{{.Content}} + +{{end}} + +{{end}} + {{end}} diff --git a/internal/config/load.go b/internal/config/load.go index 10b44fcc2b290d45676843f671d6323c38077077..360b720a6ca41e0996e325f9b8a8805cc6a79b96 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -321,7 +321,8 @@ func (c *Config) setDefaults(workingDir, dataDir string) { filepath.Join(filepath.Dir(crushConfigDir), "AGENTS.md"), } } - c.Options.ContextPaths = append(c.Options.ContextPaths, c.Options.MemoryPaths...) + slices.Sort(c.Options.MemoryPaths) + c.Options.MemoryPaths = slices.Compact(c.Options.MemoryPaths) if dataDir != "" { c.Options.DataDirectory = dataDir @@ -350,9 +351,6 @@ func (c *Config) setDefaults(workingDir, dataDir string) { // Add the default context paths if they are not already present c.Options.ContextPaths = append(defaultContextPaths, c.Options.ContextPaths...) - - // The ordering of contexts can be important; maybe the user's stuff should - // go at the bottom? Or top? slices.Sort(c.Options.ContextPaths) c.Options.ContextPaths = slices.Compact(c.Options.ContextPaths)