@@ -3,8 +3,10 @@ package commands
33import (
44 "os"
55 "path/filepath"
6+ "runtime"
67 "testing"
78
9+ "github.com/charmbracelet/crush/internal/skills"
810 "github.com/stretchr/testify/require"
911)
1012
@@ -51,3 +53,62 @@ func TestLoadAll_MixedSources(t *testing.T) {
5153 require.Len(t, cmds, 1)
5254 require.Equal(t, "user:cmd", cmds[0].ID)
5355}
56+
57+func TestFromSkillCatalog_UserInvocableOnly(t *testing.T) {
58+ t.Parallel()
59+
60+ cmds := FromSkillCatalog([]skills.CatalogEntry{
61+ {
62+ ID: "/skills/on/SKILL.md",
63+ Name: "on",
64+ Description: "Enabled.",
65+ Label: "user:on",
66+ UserInvocable: true,
67+ },
68+ {
69+ ID: "/skills/off/SKILL.md",
70+ Name: "off",
71+ Description: "Not invocable.",
72+ Label: "user:off",
73+ UserInvocable: false,
74+ },
75+ })
76+
77+ require.Len(t, cmds, 1)
78+ require.Equal(t, "user:on", cmds[0].ID)
79+ require.Equal(t, "user:on", cmds[0].Name)
80+ require.Equal(t, "on", cmds[0].Skill.Name)
81+ require.Equal(t, "Enabled.", cmds[0].Skill.Description)
82+ require.Equal(t, "/skills/on/SKILL.md", cmds[0].Skill.SkillFilePath)
83+}
84+
85+func TestFromSkillCatalog_UsesDiscoveredSymlinkedSkills(t *testing.T) {
86+ if runtime.GOOS == "windows" {
87+ t.Skip("symlink creation requires special privileges on Windows")
88+ }
89+ t.Parallel()
90+
91+ root := t.TempDir()
92+ targetParent := t.TempDir()
93+ targetSkillDir := filepath.Join(targetParent, "linked-skill")
94+ require.NoError(t, os.MkdirAll(targetSkillDir, 0o755))
95+ require.NoError(t, os.WriteFile(
96+ filepath.Join(targetSkillDir, skills.SkillFileName),
97+ []byte("---\nname: linked-skill\ndescription: Symlinked.\nuser-invocable: true\n---\nUse me.\n"),
98+ 0o644,
99+ ))
100+
101+ link := filepath.Join(root, "linked-skill")
102+ require.NoError(t, os.Symlink(targetSkillDir, link))
103+
104+ _, activeSkills, _ := skills.DiscoverFromConfig(skills.DiscoveryConfig{
105+ SkillsPaths: []string{root},
106+ })
107+ entries := skills.Catalog(activeSkills, []string{root}, "")
108+ cmds := FromSkillCatalog(entries)
109+
110+ require.Len(t, cmds, 1)
111+ require.Equal(t, "user:linked-skill", cmds[0].ID)
112+ require.Equal(t, "linked-skill", cmds[0].Skill.Name)
113+ require.Equal(t, filepath.Join(link, skills.SkillFileName), cmds[0].Skill.SkillFilePath)
114+}