mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
fix(execenv): write Copilot skills to .github/skills/ for native discovery (#1270)
GitHub Copilot CLI scans project-level skills from .github/skills/<name>/SKILL.md (per the official cli-config-dir-reference docs), not from .agent_context/skills/. Previously, skills injected for the copilot provider were placed under .agent_context/skills/ and only referenced by name in AGENTS.md, meaning Copilot would not actually pick them up. - resolveSkillsDir: add a dedicated copilot case writing to .github/skills/ - Update doc comments in context.go and runtime_config.go - Add TestWriteContextFilesCopilotNativeSkills covering the new path and ensuring .agent_context/skills/ is not created for copilot Co-authored-by: Devv <devv@Devvs-Mac-mini.local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
//
|
||||
// Claude: skills → {workDir}/.claude/skills/{name}/SKILL.md (native discovery)
|
||||
// Codex: skills → handled separately in Prepare via codex-home
|
||||
// Copilot: skills → {workDir}/.agent_context/skills/{name}/SKILL.md (via AGENTS.md references)
|
||||
// Copilot: skills → {workDir}/.github/skills/{name}/SKILL.md (native project-level discovery)
|
||||
// OpenCode: skills → {workDir}/.config/opencode/skills/{name}/SKILL.md (native discovery)
|
||||
// Pi: skills → {workDir}/.pi/agent/skills/{name}/SKILL.md (native discovery)
|
||||
// Cursor: skills → {workDir}/.cursor/skills/{name}/SKILL.md (native discovery)
|
||||
@@ -54,6 +54,12 @@ func resolveSkillsDir(workDir, provider string) (string, error) {
|
||||
case "claude":
|
||||
// Claude Code natively discovers skills from .claude/skills/ in the workdir.
|
||||
skillsDir = filepath.Join(workDir, ".claude", "skills")
|
||||
case "copilot":
|
||||
// GitHub Copilot CLI natively discovers project-level skills from
|
||||
// .github/skills/<name>/SKILL.md (takes precedence over user-level
|
||||
// skills in ~/.copilot/skills/).
|
||||
// See: https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
||||
skillsDir = filepath.Join(workDir, ".github", "skills")
|
||||
case "opencode":
|
||||
// OpenCode natively discovers skills from .config/opencode/skills/ in the workdir.
|
||||
skillsDir = filepath.Join(workDir, ".config", "opencode", "skills")
|
||||
|
||||
@@ -479,6 +479,56 @@ func TestInjectRuntimeConfigNoSkills(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteContextFilesCopilotNativeSkills(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
ctx := TaskContextForEnv{
|
||||
IssueID: "copilot-skill-test",
|
||||
AgentSkills: []SkillContextForEnv{
|
||||
{
|
||||
Name: "Go Conventions",
|
||||
Content: "Follow Go conventions.",
|
||||
Files: []SkillFileContextForEnv{
|
||||
{Path: "templates/example.go", Content: "package main"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := writeContextFiles(dir, "copilot", ctx); err != nil {
|
||||
t.Fatalf("writeContextFiles failed: %v", err)
|
||||
}
|
||||
|
||||
// Copilot CLI natively discovers project-level skills from .github/skills/.
|
||||
skillMd, err := os.ReadFile(filepath.Join(dir, ".github", "skills", "go-conventions", "SKILL.md"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read .github/skills/go-conventions/SKILL.md: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(skillMd), "Follow Go conventions.") {
|
||||
t.Error("SKILL.md missing content")
|
||||
}
|
||||
|
||||
// Supporting files should also be under .github/skills/.
|
||||
supportFile, err := os.ReadFile(filepath.Join(dir, ".github", "skills", "go-conventions", "templates", "example.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read supporting file: %v", err)
|
||||
}
|
||||
if string(supportFile) != "package main" {
|
||||
t.Errorf("supporting file content = %q, want %q", string(supportFile), "package main")
|
||||
}
|
||||
|
||||
// .agent_context/skills/ should NOT exist for Copilot.
|
||||
if _, err := os.Stat(filepath.Join(dir, ".agent_context", "skills")); !os.IsNotExist(err) {
|
||||
t.Error("expected .agent_context/skills/ to NOT exist for Copilot provider")
|
||||
}
|
||||
|
||||
// issue_context.md should still be in .agent_context/.
|
||||
if _, err := os.Stat(filepath.Join(dir, ".agent_context", "issue_context.md")); os.IsNotExist(err) {
|
||||
t.Error("expected .agent_context/issue_context.md to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteContextFilesOpencodeNativeSkills(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
//
|
||||
// For Claude: writes {workDir}/CLAUDE.md (skills discovered natively from .claude/skills/)
|
||||
// For Codex: writes {workDir}/AGENTS.md (skills discovered natively via CODEX_HOME)
|
||||
// For Copilot: writes {workDir}/AGENTS.md (Copilot CLI natively reads AGENTS.md)
|
||||
// For Copilot: writes {workDir}/AGENTS.md (skills discovered natively from .github/skills/)
|
||||
// For OpenCode: writes {workDir}/AGENTS.md (skills discovered natively from .config/opencode/skills/)
|
||||
// For OpenClaw: writes {workDir}/AGENTS.md (skills discovered natively from .openclaw/skills/)
|
||||
// For Gemini: writes {workDir}/GEMINI.md (discovered natively by the Gemini CLI)
|
||||
|
||||
Reference in New Issue
Block a user