Repository navigation
Expand file tree
/
Copy pathskill-loader.test.ts
More file actions
121 lines (98 loc) · 3.51 KB
/
Copy pathskill-loader.test.ts
File metadata and controls
121 lines (98 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises'
import { tmpdir } from 'os'
import { join } from 'path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const mockedHome = vi.hoisted(() => ({ path: '' }))
vi.mock('os', async () => {
const actual = await vi.importActual<typeof import('os')>('os')
return {
...actual,
homedir: () => mockedHome.path
}
})
import { OpenCodeEmbeddedSkillMcp } from '../index.js'
import {
discoverOpencodeGlobalSkills,
discoverOpencodeProjectSkills,
discoverSkills
} from '../skill-loader.js'
function skillMarkdown(name: string, serverName: string): string {
return `---
name: ${name}
description: Test skill
mcp:
${serverName}:
command: ["node", "server.js"]
---
# ${name}
`
}
async function writeSkill(root: string, name: string, serverName = `${name}-mcp`): Promise<void> {
const skillDir = join(root, name)
await mkdir(skillDir, { recursive: true })
await writeFile(join(skillDir, 'SKILL.md'), skillMarkdown(name, serverName))
}
describe('skill discovery', () => {
let temporaryRoot: string
let projectRoot: string
let originalForceValue: string | undefined
beforeEach(async () => {
temporaryRoot = await mkdtemp(join(tmpdir(), 'opencode-lazy-loader-'))
mockedHome.path = join(temporaryRoot, 'home')
projectRoot = join(temporaryRoot, 'project')
await mkdir(projectRoot, { recursive: true })
vi.spyOn(process, 'cwd').mockReturnValue(projectRoot)
originalForceValue = process.env.OPENCODE_LAZY_LOADER_FORCE
})
afterEach(async () => {
if (originalForceValue === undefined) {
delete process.env.OPENCODE_LAZY_LOADER_FORCE
} else {
process.env.OPENCODE_LAZY_LOADER_FORCE = originalForceValue
}
vi.restoreAllMocks()
await rm(temporaryRoot, { recursive: true, force: true })
})
it('discovers embedded MCPs from the plural global OpenCode skills directory', async () => {
const skillsRoot = join(mockedHome.path, '.config', 'opencode', 'skills')
await writeSkill(skillsRoot, 'read-pdf', 'pdf-reader')
const skills = await discoverOpencodeGlobalSkills()
expect(skills).toHaveLength(1)
expect(skills[0]).toMatchObject({
name: 'read-pdf',
scope: 'opencode',
mcpConfig: {
'pdf-reader': {
command: ['node', 'server.js']
}
}
})
})
it('discovers skills from the plural project OpenCode skills directory', async () => {
const skillsRoot = join(projectRoot, '.opencode', 'skills')
await writeSkill(skillsRoot, 'hello-world')
const skills = await discoverOpencodeProjectSkills()
expect(skills.map((skill) => skill.name)).toEqual(['hello-world'])
})
it('uses project skills in preference to global skills with the same name', async () => {
await writeSkill(join(mockedHome.path, '.agents', 'skills'), 'shared', 'global-mcp')
await writeSkill(join(projectRoot, '.claude', 'skills'), 'shared', 'project-mcp')
const skills = await discoverSkills()
expect(skills).toHaveLength(1)
expect(skills[0]).toMatchObject({
name: 'shared',
scope: 'claude-project',
mcpConfig: {
'project-mcp': {
command: ['node', 'server.js']
}
}
})
})
it('registers skill_mcp without replacing OpenCode native skill tool', async () => {
process.env.OPENCODE_LAZY_LOADER_FORCE = '1'
const hooks = await OpenCodeEmbeddedSkillMcp({ client: {} } as never)
expect(hooks.tool).toHaveProperty('skill_mcp')
expect(hooks.tool).not.toHaveProperty('skill')
})
})