Merge pull request #2397 from code-yeongyu/fix/browser-provider-skill-context-playwright
fix(skill-context): gate discovered browser skills by provider
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"
|
||||||
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
|
import { tmpdir } from "node:os"
|
||||||
|
import { join } from "node:path"
|
||||||
|
|
||||||
|
import { OhMyOpenCodeConfigSchema } from "../config"
|
||||||
|
import * as mcpLoader from "../features/claude-code-mcp-loader"
|
||||||
|
import * as skillLoader from "../features/opencode-skill-loader"
|
||||||
|
import { createSkillContext } from "./skill-context"
|
||||||
|
|
||||||
|
describe("createSkillContext", () => {
|
||||||
|
const testDirectory = join(tmpdir(), `skill-context-test-${Date.now()}`)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mkdirSync(testDirectory, { recursive: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
rmSync(testDirectory, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
it("excludes discovered playwright skill when browser provider is agent-browser", async () => {
|
||||||
|
// given
|
||||||
|
const discoveredPlaywrightDir = join(testDirectory, ".claude", "skills", "playwright")
|
||||||
|
mkdirSync(discoveredPlaywrightDir, { recursive: true })
|
||||||
|
writeFileSync(
|
||||||
|
join(discoveredPlaywrightDir, "SKILL.md"),
|
||||||
|
[
|
||||||
|
"---",
|
||||||
|
"name: playwright",
|
||||||
|
"description: Discovered playwright skill",
|
||||||
|
"---",
|
||||||
|
"Discovered playwright body.",
|
||||||
|
"",
|
||||||
|
].join("\n"),
|
||||||
|
)
|
||||||
|
|
||||||
|
const discoverConfigSourceSkillsSpy = spyOn(
|
||||||
|
skillLoader,
|
||||||
|
"discoverConfigSourceSkills",
|
||||||
|
).mockResolvedValue([])
|
||||||
|
const discoverUserClaudeSkillsSpy = spyOn(
|
||||||
|
skillLoader,
|
||||||
|
"discoverUserClaudeSkills",
|
||||||
|
).mockResolvedValue([])
|
||||||
|
const discoverOpencodeGlobalSkillsSpy = spyOn(
|
||||||
|
skillLoader,
|
||||||
|
"discoverOpencodeGlobalSkills",
|
||||||
|
).mockResolvedValue([])
|
||||||
|
const discoverProjectAgentsSkillsSpy = spyOn(
|
||||||
|
skillLoader,
|
||||||
|
"discoverProjectAgentsSkills",
|
||||||
|
).mockResolvedValue([])
|
||||||
|
const discoverGlobalAgentsSkillsSpy = spyOn(
|
||||||
|
skillLoader,
|
||||||
|
"discoverGlobalAgentsSkills",
|
||||||
|
).mockResolvedValue([])
|
||||||
|
const getSystemMcpServerNamesSpy = spyOn(
|
||||||
|
mcpLoader,
|
||||||
|
"getSystemMcpServerNames",
|
||||||
|
).mockReturnValue(new Set<string>())
|
||||||
|
|
||||||
|
const pluginConfig = OhMyOpenCodeConfigSchema.parse({
|
||||||
|
browser_automation_engine: { provider: "agent-browser" },
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
// when
|
||||||
|
const result = await createSkillContext({
|
||||||
|
directory: testDirectory,
|
||||||
|
pluginConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.browserProvider).toBe("agent-browser")
|
||||||
|
expect(result.mergedSkills.some((skill) => skill.name === "agent-browser")).toBe(true)
|
||||||
|
expect(result.mergedSkills.some((skill) => skill.name === "playwright")).toBe(false)
|
||||||
|
expect(result.availableSkills.some((skill) => skill.name === "playwright")).toBe(false)
|
||||||
|
} finally {
|
||||||
|
discoverConfigSourceSkillsSpy.mockRestore()
|
||||||
|
discoverUserClaudeSkillsSpy.mockRestore()
|
||||||
|
discoverOpencodeGlobalSkillsSpy.mockRestore()
|
||||||
|
discoverProjectAgentsSkillsSpy.mockRestore()
|
||||||
|
discoverGlobalAgentsSkillsSpy.mockRestore()
|
||||||
|
getSystemMcpServerNamesSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -26,12 +26,27 @@ export type SkillContext = {
|
|||||||
disabledSkills: Set<string>
|
disabledSkills: Set<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PROVIDER_GATED_SKILL_NAMES = new Set(["agent-browser", "playwright"])
|
||||||
|
|
||||||
function mapScopeToLocation(scope: SkillScope): AvailableSkill["location"] {
|
function mapScopeToLocation(scope: SkillScope): AvailableSkill["location"] {
|
||||||
if (scope === "user" || scope === "opencode") return "user"
|
if (scope === "user" || scope === "opencode") return "user"
|
||||||
if (scope === "project" || scope === "opencode-project") return "project"
|
if (scope === "project" || scope === "opencode-project") return "project"
|
||||||
return "plugin"
|
return "plugin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterProviderGatedSkills(
|
||||||
|
skills: LoadedSkill[],
|
||||||
|
browserProvider: BrowserAutomationProvider,
|
||||||
|
): LoadedSkill[] {
|
||||||
|
return skills.filter((skill) => {
|
||||||
|
if (!PROVIDER_GATED_SKILL_NAMES.has(skill.name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return skill.name === browserProvider
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function createSkillContext(args: {
|
export async function createSkillContext(args: {
|
||||||
directory: string
|
directory: string
|
||||||
pluginConfig: OhMyOpenCodeConfig
|
pluginConfig: OhMyOpenCodeConfig
|
||||||
@@ -71,14 +86,34 @@ export async function createSkillContext(args: {
|
|||||||
discoverGlobalAgentsSkills(),
|
discoverGlobalAgentsSkills(),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const filteredConfigSourceSkills = filterProviderGatedSkills(
|
||||||
|
configSourceSkills,
|
||||||
|
browserProvider,
|
||||||
|
)
|
||||||
|
const filteredUserSkills = filterProviderGatedSkills(userSkills, browserProvider)
|
||||||
|
const filteredGlobalSkills = filterProviderGatedSkills(globalSkills, browserProvider)
|
||||||
|
const filteredProjectSkills = filterProviderGatedSkills(projectSkills, browserProvider)
|
||||||
|
const filteredOpencodeProjectSkills = filterProviderGatedSkills(
|
||||||
|
opencodeProjectSkills,
|
||||||
|
browserProvider,
|
||||||
|
)
|
||||||
|
const filteredAgentsProjectSkills = filterProviderGatedSkills(
|
||||||
|
agentsProjectSkills,
|
||||||
|
browserProvider,
|
||||||
|
)
|
||||||
|
const filteredAgentsGlobalSkills = filterProviderGatedSkills(
|
||||||
|
agentsGlobalSkills,
|
||||||
|
browserProvider,
|
||||||
|
)
|
||||||
|
|
||||||
const mergedSkills = mergeSkills(
|
const mergedSkills = mergeSkills(
|
||||||
builtinSkills,
|
builtinSkills,
|
||||||
pluginConfig.skills,
|
pluginConfig.skills,
|
||||||
configSourceSkills,
|
filteredConfigSourceSkills,
|
||||||
[...userSkills, ...agentsGlobalSkills],
|
[...filteredUserSkills, ...filteredAgentsGlobalSkills],
|
||||||
globalSkills,
|
filteredGlobalSkills,
|
||||||
[...projectSkills, ...agentsProjectSkills],
|
[...filteredProjectSkills, ...filteredAgentsProjectSkills],
|
||||||
opencodeProjectSkills,
|
filteredOpencodeProjectSkills,
|
||||||
{ configDir: directory },
|
{ configDir: directory },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user