diff --git a/devagents/llm.go b/devagents/llm.go index 6cd1762..eb84a96 100644 --- a/devagents/llm.go +++ b/devagents/llm.go @@ -29,8 +29,8 @@ func (a *Agent) runLLM(ctx context.Context, msgCtx decision.MessageContext, memK // Load system prompt from file if configured, else use description systemPrompt := a.cfg.Agent.Description if spFile := a.cfg.LLM.Reasoning.SystemPromptFile; spFile != "" { - // Resolve path relative to agent directory - spPath := filepath.Join("agents", a.cfg.Agent.ID, spFile) + // Resolve path relative to the config directory (handles _specials/ and custom locations) + spPath := filepath.Join(a.cfg.ConfigDir, spFile) if data, err := os.ReadFile(spPath); err == nil { systemPrompt = string(data) } else { diff --git a/internal/config/loader.go b/internal/config/loader.go index 4bf891f..30d5fff 100644 --- a/internal/config/loader.go +++ b/internal/config/loader.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "path/filepath" "gopkg.in/yaml.v3" ) @@ -26,6 +27,8 @@ func Load(path string) (*AgentConfig, error) { return nil, fmt.Errorf("invalid config %s: %w", path, err) } + cfg.ConfigDir = filepath.Dir(path) + return &cfg, nil } diff --git a/internal/config/schema.go b/internal/config/schema.go index 7cb03b4..089d8f5 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -16,6 +16,11 @@ type AgentConfig struct { Storage StorageCfg `yaml:"storage"` Memory MemoryCfg `yaml:"memory"` Skills SkillsCfg `yaml:"skills"` + + // ConfigDir is the directory containing the config file. Set by the loader + // at load time, not from YAML. Used to resolve relative paths like + // system_prompt_file correctly regardless of where the agent lives. + ConfigDir string `yaml:"-"` } // ── Identity ──────────────────────────────────────────────────────────────