diff --git a/internal/skills/registry/registry.go b/internal/skills/registry/registry.go index a5e018176cf..ae98570fe56 100644 --- a/internal/skills/registry/registry.go +++ b/internal/skills/registry/registry.go @@ -2,6 +2,7 @@ package registry import ( "fmt" + "os" "path/filepath" "strings" @@ -30,6 +31,8 @@ const ( DefaultAgentID = "github-copilot" + claudeConfigDirEnv = "CLAUDE_CONFIG_DIR" + sharedProjectSkillsDir = ".agents/skills" ) @@ -387,6 +390,11 @@ func (h *AgentHost) InstallDir(scope Scope, gitRoot, homeDir string) (string, er } return filepath.Join(gitRoot, h.ProjectDir), nil case ScopeUser: + if h.ID == "claude-code" { + if configDir := os.Getenv(claudeConfigDirEnv); configDir != "" { + return filepath.Join(configDir, "skills"), nil + } + } if homeDir == "" { return "", fmt.Errorf("could not determine home directory") } diff --git a/internal/skills/registry/registry_test.go b/internal/skills/registry/registry_test.go index bd0c4470963..b7a8e9a64f5 100644 --- a/internal/skills/registry/registry_test.go +++ b/internal/skills/registry/registry_test.go @@ -38,8 +38,11 @@ func TestFindByID(t *testing.T) { } func TestInstallDir(t *testing.T) { + t.Setenv(claudeConfigDirEnv, "") + tests := []struct { name string + setup func(*testing.T) hostID string scope Scope gitRoot string @@ -71,6 +74,25 @@ func TestInstallDir(t *testing.T) { homeDir: "/home/monalisa", wantDir: filepath.Join("/tmp/monalisa-repo", ".claude", "skills"), }, + { + name: "claude code user scope", + hostID: "claude-code", + scope: ScopeUser, + gitRoot: "/tmp/monalisa-repo", + homeDir: "/home/monalisa", + wantDir: filepath.Join("/home/monalisa", ".claude", "skills"), + }, + { + name: "claude code user scope, respect env var", + setup: func(t *testing.T) { + t.Setenv("CLAUDE_CONFIG_DIR", filepath.Join("/home", "monalisa", ".config", "claude")) + }, + hostID: "claude-code", + scope: ScopeUser, + gitRoot: "/tmp/monalisa-repo", + homeDir: "/home/monalisa", + wantDir: filepath.Join("/home", "monalisa", ".config", "claude", "skills"), + }, { name: "cursor project scope", hostID: "cursor", @@ -130,6 +152,10 @@ func TestInstallDir(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup(t) + } + host, err := FindByID(tt.hostID) require.NoError(t, err) diff --git a/pkg/cmd/skills/install/install_test.go b/pkg/cmd/skills/install/install_test.go index 64cded9bf67..7e8267c7600 100644 --- a/pkg/cmd/skills/install/install_test.go +++ b/pkg/cmd/skills/install/install_test.go @@ -311,6 +311,7 @@ func TestInstallRun(t *testing.T) { wantErr string wantStdout string wantStderr string + assert func(t *testing.T) }{ { name: "non-interactive without repo errors", @@ -1527,6 +1528,37 @@ func TestInstallRun(t *testing.T) { wantStdout: "Installed hidden-skill", wantStderr: "Skills in hidden directories", }, + { + name: "respect claude code config dir env var for user scope", + setup: func(t *testing.T) { + t.Setenv("CLAUDE_CONFIG_DIR", t.TempDir()) + }, + stubs: func(reg *httpmock.Registry) { + stubResolveVersion(reg, "monalisa", "skills-repo", "v1.0.0", "abc123") + stubDiscoverTree(reg, "monalisa", "skills-repo", "abc123", + singleSkillTreeJSON("git-commit", "treeSHA", "blobSHA")) + stubInstallFiles(reg, "monalisa", "skills-repo", "treeSHA", "blobSHA", gitCommitContent) + }, + opts: func(ios *iostreams.IOStreams, reg *httpmock.Registry) *InstallOptions { + t.Helper() + return &InstallOptions{ + IO: ios, + HttpClient: func() (*http.Client, error) { return &http.Client{Transport: reg}, nil }, + GitClient: &git.Client{RepoDir: t.TempDir()}, + SkillSource: "monalisa/skills-repo", + SkillName: "git-commit", + Agent: "claude-code", + Scope: "user", + ScopeChanged: true, + Telemetry: &telemetry.NoOpService{}, + } + }, + assert: func(t *testing.T) { + assert.FileExists(t, filepath.Join(os.Getenv("CLAUDE_CONFIG_DIR"), "skills", "git-commit", "SKILL.md")) + assert.NoFileExists(t, filepath.Join(os.Getenv("HOME"), ".claude", "skills", "git-commit", "SKILL.md")) + }, + wantStdout: "Installed git-commit", + }, } for _, tt := range tests { @@ -1572,6 +1604,9 @@ func TestInstallRun(t *testing.T) { if tt.verify != nil { tt.verify(t) } + if tt.assert != nil { + tt.assert(t) + } }) } }