11import { test , expect } from "bun:test"
22import { Skill } from "../../src/skill"
3- import { SystemPrompt } from "../../src/session/system"
43import { Instance } from "../../src/project/instance"
54import { tmpdir } from "../fixture/fixture"
65import path from "path"
6+ import fs from "fs/promises"
7+
8+ async function createGlobalSkill ( homeDir : string ) {
9+ const skillDir = path . join ( homeDir , ".claude" , "skills" , "global-test-skill" )
10+ await fs . mkdir ( skillDir , { recursive : true } )
11+ await Bun . write (
12+ path . join ( skillDir , "SKILL.md" ) ,
13+ `---
14+ name: global-test-skill
15+ description: A global skill from ~/.claude/skills for testing.
16+ ---
17+
18+ # Global Test Skill
19+
20+ This skill is loaded from the global home directory.
21+ ` ,
22+ )
23+ }
724
825test ( "discovers skills from .opencode/skill/ directory" , async ( ) => {
926 await using tmp = await tmpdir ( {
@@ -30,9 +47,10 @@ Instructions here.
3047 fn : async ( ) => {
3148 const skills = await Skill . all ( )
3249 expect ( skills . length ) . toBe ( 1 )
33- expect ( skills [ 0 ] . name ) . toBe ( "test-skill" )
34- expect ( skills [ 0 ] . description ) . toBe ( "A test skill for verification." )
35- expect ( skills [ 0 ] . location ) . toContain ( "skill/test-skill/SKILL.md" )
50+ const testSkill = skills . find ( ( s ) => s . name === "test-skill" )
51+ expect ( testSkill ) . toBeDefined ( )
52+ expect ( testSkill ! . description ) . toBe ( "A test skill for verification." )
53+ expect ( testSkill ! . location ) . toContain ( "skill/test-skill/SKILL.md" )
3654 } ,
3755 } )
3856} )
@@ -41,15 +59,26 @@ test("discovers multiple skills from .opencode/skill/ directory", async () => {
4159 await using tmp = await tmpdir ( {
4260 git : true ,
4361 init : async ( dir ) => {
44- const skillDir = path . join ( dir , ".opencode" , "skill" , "my-skill" )
62+ const skillDir1 = path . join ( dir , ".opencode" , "skill" , "skill-one" )
63+ const skillDir2 = path . join ( dir , ".opencode" , "skill" , "skill-two" )
4564 await Bun . write (
46- path . join ( skillDir , "SKILL.md" ) ,
65+ path . join ( skillDir1 , "SKILL.md" ) ,
4766 `---
48- name: my- skill
49- description: Another test skill.
67+ name: skill-one
68+ description: First test skill.
5069---
5170
52- # My Skill
71+ # Skill One
72+ ` ,
73+ )
74+ await Bun . write (
75+ path . join ( skillDir2 , "SKILL.md" ) ,
76+ `---
77+ name: skill-two
78+ description: Second test skill.
79+ ---
80+
81+ # Skill Two
5382` ,
5483 )
5584 } ,
@@ -59,8 +88,9 @@ description: Another test skill.
5988 directory : tmp . path ,
6089 fn : async ( ) => {
6190 const skills = await Skill . all ( )
62- expect ( skills . length ) . toBe ( 1 )
63- expect ( skills [ 0 ] . name ) . toBe ( "my-skill" )
91+ expect ( skills . length ) . toBe ( 2 )
92+ expect ( skills . find ( ( s ) => s . name === "skill-one" ) ) . toBeDefined ( )
93+ expect ( skills . find ( ( s ) => s . name === "skill-two" ) ) . toBeDefined ( )
6494 } ,
6595 } )
6696} )
@@ -89,18 +119,6 @@ Just some content without YAML frontmatter.
89119 } )
90120} )
91121
92- test ( "returns empty array when no skills exist" , async ( ) => {
93- await using tmp = await tmpdir ( { git : true } )
94-
95- await Instance . provide ( {
96- directory : tmp . path ,
97- fn : async ( ) => {
98- const skills = await Skill . all ( )
99- expect ( skills ) . toEqual ( [ ] )
100- } ,
101- } )
102- } )
103-
104122test ( "discovers skills from .claude/skills/ directory" , async ( ) => {
105123 await using tmp = await tmpdir ( {
106124 git : true ,
@@ -124,8 +142,44 @@ description: A skill in the .claude/skills directory.
124142 fn : async ( ) => {
125143 const skills = await Skill . all ( )
126144 expect ( skills . length ) . toBe ( 1 )
127- expect ( skills [ 0 ] . name ) . toBe ( "claude-skill" )
128- expect ( skills [ 0 ] . location ) . toContain ( ".claude/skills/claude-skill/SKILL.md" )
145+ const claudeSkill = skills . find ( ( s ) => s . name === "claude-skill" )
146+ expect ( claudeSkill ) . toBeDefined ( )
147+ expect ( claudeSkill ! . location ) . toContain ( ".claude/skills/claude-skill/SKILL.md" )
148+ } ,
149+ } )
150+ } )
151+
152+ test ( "discovers global skills from ~/.claude/skills/ directory" , async ( ) => {
153+ await using tmp = await tmpdir ( { git : true } )
154+
155+ const originalHome = process . env . OPENCODE_TEST_HOME
156+ process . env . OPENCODE_TEST_HOME = tmp . path
157+
158+ try {
159+ await createGlobalSkill ( tmp . path )
160+ await Instance . provide ( {
161+ directory : tmp . path ,
162+ fn : async ( ) => {
163+ const skills = await Skill . all ( )
164+ expect ( skills . length ) . toBe ( 1 )
165+ expect ( skills [ 0 ] . name ) . toBe ( "global-test-skill" )
166+ expect ( skills [ 0 ] . description ) . toBe ( "A global skill from ~/.claude/skills for testing." )
167+ expect ( skills [ 0 ] . location ) . toContain ( ".claude/skills/global-test-skill/SKILL.md" )
168+ } ,
169+ } )
170+ } finally {
171+ process . env . OPENCODE_TEST_HOME = originalHome
172+ }
173+ } )
174+
175+ test ( "returns empty array when no skills exist" , async ( ) => {
176+ await using tmp = await tmpdir ( { git : true } )
177+
178+ await Instance . provide ( {
179+ directory : tmp . path ,
180+ fn : async ( ) => {
181+ const skills = await Skill . all ( )
182+ expect ( skills ) . toEqual ( [ ] )
129183 } ,
130184 } )
131185} )
0 commit comments