refactor(flags): migrate external skills flag (#27685)
This commit is contained in:
@@ -30,7 +30,6 @@ export const Flag = {
|
|||||||
OPENCODE_DISABLE_MOUSE: truthy("OPENCODE_DISABLE_MOUSE"),
|
OPENCODE_DISABLE_MOUSE: truthy("OPENCODE_DISABLE_MOUSE"),
|
||||||
OPENCODE_DISABLE_CLAUDE_CODE,
|
OPENCODE_DISABLE_CLAUDE_CODE,
|
||||||
OPENCODE_DISABLE_CLAUDE_CODE_PROMPT: OPENCODE_DISABLE_CLAUDE_CODE || truthy("OPENCODE_DISABLE_CLAUDE_CODE_PROMPT"),
|
OPENCODE_DISABLE_CLAUDE_CODE_PROMPT: OPENCODE_DISABLE_CLAUDE_CODE || truthy("OPENCODE_DISABLE_CLAUDE_CODE_PROMPT"),
|
||||||
OPENCODE_DISABLE_EXTERNAL_SKILLS: truthy("OPENCODE_DISABLE_EXTERNAL_SKILLS"),
|
|
||||||
OPENCODE_FAKE_VCS: process.env["OPENCODE_FAKE_VCS"],
|
OPENCODE_FAKE_VCS: process.env["OPENCODE_FAKE_VCS"],
|
||||||
OPENCODE_SERVER_PASSWORD: process.env["OPENCODE_SERVER_PASSWORD"],
|
OPENCODE_SERVER_PASSWORD: process.env["OPENCODE_SERVER_PASSWORD"],
|
||||||
OPENCODE_SERVER_USERNAME: process.env["OPENCODE_SERVER_USERNAME"],
|
OPENCODE_SERVER_USERNAME: process.env["OPENCODE_SERVER_USERNAME"],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class Service extends ConfigService.Service<Service>()("@opencode/Runtime
|
|||||||
disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"),
|
disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"),
|
||||||
disableChannelDb: bool("OPENCODE_DISABLE_CHANNEL_DB"),
|
disableChannelDb: bool("OPENCODE_DISABLE_CHANNEL_DB"),
|
||||||
disableEmbeddedWebUi: bool("OPENCODE_DISABLE_EMBEDDED_WEB_UI"),
|
disableEmbeddedWebUi: bool("OPENCODE_DISABLE_EMBEDDED_WEB_UI"),
|
||||||
|
disableExternalSkills: bool("OPENCODE_DISABLE_EXTERNAL_SKILLS"),
|
||||||
disableClaudeCodeSkills: Config.all({
|
disableClaudeCodeSkills: Config.all({
|
||||||
broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"),
|
broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"),
|
||||||
direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS"),
|
direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS"),
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { NamedError } from "@opencode-ai/core/util/error"
|
|||||||
import type { Agent } from "@/agent/agent"
|
import type { Agent } from "@/agent/agent"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { Permission } from "@/permission"
|
import { Permission } from "@/permission"
|
||||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
@@ -166,6 +165,7 @@ const discoverSkills = Effect.fnUntraced(function* (
|
|||||||
discovery: Discovery.Interface,
|
discovery: Discovery.Interface,
|
||||||
fsys: AppFileSystem.Interface,
|
fsys: AppFileSystem.Interface,
|
||||||
global: Global.Interface,
|
global: Global.Interface,
|
||||||
|
disableExternalSkills: boolean,
|
||||||
disableClaudeCodeSkills: boolean,
|
disableClaudeCodeSkills: boolean,
|
||||||
directory: string,
|
directory: string,
|
||||||
worktree: string,
|
worktree: string,
|
||||||
@@ -173,7 +173,7 @@ const discoverSkills = Effect.fnUntraced(function* (
|
|||||||
const state: ScanState = { matches: new Set(), dirs: new Set() }
|
const state: ScanState = { matches: new Set(), dirs: new Set() }
|
||||||
|
|
||||||
const externalDirs: string[] = []
|
const externalDirs: string[] = []
|
||||||
if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) {
|
if (!disableExternalSkills) {
|
||||||
if (!disableClaudeCodeSkills) externalDirs.push(CLAUDE_EXTERNAL_DIR)
|
if (!disableClaudeCodeSkills) externalDirs.push(CLAUDE_EXTERNAL_DIR)
|
||||||
externalDirs.push(AGENTS_EXTERNAL_DIR)
|
externalDirs.push(AGENTS_EXTERNAL_DIR)
|
||||||
|
|
||||||
@@ -249,6 +249,7 @@ export const layer = Layer.effect(
|
|||||||
discovery,
|
discovery,
|
||||||
fsys,
|
fsys,
|
||||||
global,
|
global,
|
||||||
|
flags.disableExternalSkills,
|
||||||
flags.disableClaudeCodeSkills,
|
flags.disableClaudeCodeSkills,
|
||||||
ctx.directory,
|
ctx.directory,
|
||||||
ctx.worktree,
|
ctx.worktree,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ describe("RuntimeFlags", () => {
|
|||||||
OPENCODE_DISABLE_CHANNEL_DB: "true",
|
OPENCODE_DISABLE_CHANNEL_DB: "true",
|
||||||
OPENCODE_AUTO_SHARE: "true",
|
OPENCODE_AUTO_SHARE: "true",
|
||||||
OPENCODE_DISABLE_EMBEDDED_WEB_UI: "true",
|
OPENCODE_DISABLE_EMBEDDED_WEB_UI: "true",
|
||||||
|
OPENCODE_DISABLE_EXTERNAL_SKILLS: "true",
|
||||||
OPENCODE_EXPERIMENTAL: "true",
|
OPENCODE_EXPERIMENTAL: "true",
|
||||||
OPENCODE_ENABLE_EXA: "true",
|
OPENCODE_ENABLE_EXA: "true",
|
||||||
OPENCODE_ENABLE_PARALLEL: "true",
|
OPENCODE_ENABLE_PARALLEL: "true",
|
||||||
@@ -42,6 +43,7 @@ describe("RuntimeFlags", () => {
|
|||||||
expect(flags.disableDefaultPlugins).toBe(true)
|
expect(flags.disableDefaultPlugins).toBe(true)
|
||||||
expect(flags.disableChannelDb).toBe(true)
|
expect(flags.disableChannelDb).toBe(true)
|
||||||
expect(flags.disableEmbeddedWebUi).toBe(true)
|
expect(flags.disableEmbeddedWebUi).toBe(true)
|
||||||
|
expect(flags.disableExternalSkills).toBe(true)
|
||||||
expect(flags.enableExa).toBe(true)
|
expect(flags.enableExa).toBe(true)
|
||||||
expect(flags.enableParallel).toBe(true)
|
expect(flags.enableParallel).toBe(true)
|
||||||
expect(flags.enableExperimentalModels).toBe(true)
|
expect(flags.enableExperimentalModels).toBe(true)
|
||||||
@@ -84,6 +86,7 @@ describe("RuntimeFlags", () => {
|
|||||||
expect(flags.disableDefaultPlugins).toBe(true)
|
expect(flags.disableDefaultPlugins).toBe(true)
|
||||||
expect(flags.disableChannelDb).toBe(false)
|
expect(flags.disableChannelDb).toBe(false)
|
||||||
expect(flags.disableEmbeddedWebUi).toBe(false)
|
expect(flags.disableEmbeddedWebUi).toBe(false)
|
||||||
|
expect(flags.disableExternalSkills).toBe(false)
|
||||||
expect(flags.disableClaudeCodeSkills).toBe(false)
|
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||||
expect(flags.enableExa).toBe(false)
|
expect(flags.enableExa).toBe(false)
|
||||||
expect(flags.experimentalIconDiscovery).toBe(false)
|
expect(flags.experimentalIconDiscovery).toBe(false)
|
||||||
@@ -103,6 +106,22 @@ describe("RuntimeFlags", () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.effect("disableExternalSkills defaults to false", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
|
||||||
|
|
||||||
|
expect(flags.disableExternalSkills).toBe(false)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.effect("disableExternalSkills reads OPENCODE_DISABLE_EXTERNAL_SKILLS", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_EXTERNAL_SKILLS: "true" })))
|
||||||
|
|
||||||
|
expect(flags.disableExternalSkills).toBe(true)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.effect("experimentalIconDiscovery reads OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", () =>
|
it.effect("experimentalIconDiscovery reads OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true" })))
|
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true" })))
|
||||||
@@ -222,6 +241,7 @@ describe("RuntimeFlags", () => {
|
|||||||
ConfigProvider.fromUnknown({
|
ConfigProvider.fromUnknown({
|
||||||
OPENCODE_PURE: "true",
|
OPENCODE_PURE: "true",
|
||||||
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
|
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
|
||||||
|
OPENCODE_DISABLE_EXTERNAL_SKILLS: "true",
|
||||||
OPENCODE_EXPERIMENTAL: "true",
|
OPENCODE_EXPERIMENTAL: "true",
|
||||||
OPENCODE_ENABLE_EXA: "true",
|
OPENCODE_ENABLE_EXA: "true",
|
||||||
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234",
|
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234",
|
||||||
@@ -235,6 +255,7 @@ describe("RuntimeFlags", () => {
|
|||||||
expect(flags.disableDefaultPlugins).toBe(false)
|
expect(flags.disableDefaultPlugins).toBe(false)
|
||||||
expect(flags.disableChannelDb).toBe(false)
|
expect(flags.disableChannelDb).toBe(false)
|
||||||
expect(flags.disableEmbeddedWebUi).toBe(false)
|
expect(flags.disableEmbeddedWebUi).toBe(false)
|
||||||
|
expect(flags.disableExternalSkills).toBe(false)
|
||||||
expect(flags.disableClaudeCodeSkills).toBe(false)
|
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||||
expect(flags.enableExa).toBe(false)
|
expect(flags.enableExa).toBe(false)
|
||||||
expect(flags.experimentalIconDiscovery).toBe(false)
|
expect(flags.experimentalIconDiscovery).toBe(false)
|
||||||
|
|||||||
@@ -29,6 +29,19 @@ const itWithoutClaudeCodeSkills = testEffect(
|
|||||||
node,
|
node,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
const itWithoutExternalSkills = testEffect(
|
||||||
|
Layer.mergeAll(
|
||||||
|
Skill.layer.pipe(
|
||||||
|
Layer.provide(Discovery.defaultLayer),
|
||||||
|
Layer.provide(Config.defaultLayer),
|
||||||
|
Layer.provide(Bus.layer),
|
||||||
|
Layer.provide(AppFileSystem.defaultLayer),
|
||||||
|
Layer.provide(Global.layer),
|
||||||
|
Layer.provide(RuntimeFlags.layer({ disableExternalSkills: true })),
|
||||||
|
),
|
||||||
|
node,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
async function createGlobalSkill(homeDir: string) {
|
async function createGlobalSkill(homeDir: string) {
|
||||||
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill")
|
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill")
|
||||||
@@ -420,6 +433,53 @@ description: A skill in the .agents/skills directory.
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
itWithoutExternalSkills.live("skips external skill directories when disabled", () =>
|
||||||
|
provideTmpdirInstance(
|
||||||
|
(dir) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* Effect.promise(() =>
|
||||||
|
Promise.all([
|
||||||
|
Bun.write(
|
||||||
|
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
|
||||||
|
`---
|
||||||
|
name: claude-skill
|
||||||
|
description: A skill in the .claude/skills directory.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Claude Skill
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
Bun.write(
|
||||||
|
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
|
||||||
|
`---
|
||||||
|
name: agent-skill
|
||||||
|
description: A skill in the .agents/skills directory.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Skill
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
Bun.write(
|
||||||
|
path.join(dir, ".opencode", "skill", "opencode-skill", "SKILL.md"),
|
||||||
|
`---
|
||||||
|
name: opencode-skill
|
||||||
|
description: A skill in the .opencode/skill directory.
|
||||||
|
---
|
||||||
|
|
||||||
|
# OpenCode Skill
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
const skill = yield* Skill.Service
|
||||||
|
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
|
||||||
|
expect(list.map((s) => s.name)).toEqual(["opencode-skill"])
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
it.live("properly resolves directories that skills live in", () =>
|
it.live("properly resolves directories that skills live in", () =>
|
||||||
provideTmpdirInstance(
|
provideTmpdirInstance(
|
||||||
(dir) =>
|
(dir) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user