feat(agent/agentcontextconfig): discover skills from ~/.coder/skills (#25271)

The default skills lookup only scanned the project-relative
.agents/skills directory, so personal skills had to be repeated
per project or wired in via CODER_AGENT_EXP_SKILLS_DIRS. Now the
default is the comma-separated list ~/.coder/skills,.agents/skills,
which lets discoverSkills's existing first-occurrence-wins policy
prefer home-scoped skills over project ones with the same name.

The change is additive when ~/.coder/skills is absent
(missing directories are silently skipped in discoverSkills) and
unaffects users who set the env var explicitly.

Closes CODAGT-403
This commit is contained in:
Mathias Fredriksson
2026-05-13 12:56:24 +03:00
committed by GitHub
parent fb3aef1883
commit 5b87d7b74f
2 changed files with 36 additions and 1 deletions
+5 -1
View File
@@ -60,10 +60,14 @@ var skillNamePattern = regexp.MustCompile(
// Default values for agent-internal configuration. These are
// used when the corresponding env vars are unset.
//
// DefaultSkillsDir is a comma-separated list so home-scoped
// skills override project-scoped ones with the same name
// (discoverSkills picks the first occurrence per skill name).
const (
DefaultInstructionsDir = "~/.coder"
DefaultInstructionsFile = "AGENTS.md"
DefaultSkillsDir = ".agents/skills"
DefaultSkillsDir = "~/.coder/skills,.agents/skills"
DefaultSkillMetaFile = "SKILL.md"
DefaultMCPConfigFile = ".mcp.json"
)
+31
View File
@@ -461,6 +461,37 @@ func TestResolve(t *testing.T) {
require.Len(t, skillParts, 1)
require.Equal(t, "from skills1", skillParts[0].SkillDescription)
})
//nolint:paralleltest // Uses t.Setenv to mutate HOME.
t.Run("DefaultDiscoversHomeAndProjectSkillsHomeWins", func(t *testing.T) {
fakeHome := t.TempDir()
t.Setenv("HOME", fakeHome)
t.Setenv("USERPROFILE", fakeHome)
workDir := t.TempDir()
homeSkills := filepath.Join(fakeHome, ".coder", "skills")
writeSkillMetaFileInRoot(t, homeSkills, "home-only", "home only")
writeSkillMetaFileInRoot(t, homeSkills, "shared", "from home")
writeSkillMetaFile(t, workDir, "project-only", "project only")
writeSkillMetaFile(t, workDir, "shared", "from project")
// Construct the Config directly with the package defaults
// to verify the default skills list (and only the defaults).
cfg, _ := agentcontextconfig.Resolve(workDir, agentcontextconfig.Config{
SkillsDirs: agentcontextconfig.DefaultSkillsDir,
SkillMetaFile: agentcontextconfig.DefaultSkillMetaFile,
})
got := map[string]string{}
for _, p := range filterParts(cfg.Parts, codersdk.ChatMessagePartTypeSkill) {
got[p.SkillName] = p.SkillDescription
}
require.Equal(t, map[string]string{
"home-only": "home only",
"project-only": "project only",
"shared": "from home",
}, got)
})
}
func TestNewAPI_LazyDirectory(t *testing.T) {