From 22cb0395e2f601923a44a6d2093493de7eb1cfce Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 13:24:56 +0530 Subject: [PATCH] refactor(flags): migrate external skills flag (#27685) --- packages/core/src/flag/flag.ts | 1 - packages/opencode/src/effect/runtime-flags.ts | 1 + packages/opencode/src/skill/index.ts | 5 +- .../test/effect/runtime-flags.test.ts | 21 +++++++ packages/opencode/test/skill/skill.test.ts | 60 +++++++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/core/src/flag/flag.ts b/packages/core/src/flag/flag.ts index d08b2a19b3..dcb7a1528f 100644 --- a/packages/core/src/flag/flag.ts +++ b/packages/core/src/flag/flag.ts @@ -30,7 +30,6 @@ export const Flag = { OPENCODE_DISABLE_MOUSE: truthy("OPENCODE_DISABLE_MOUSE"), OPENCODE_DISABLE_CLAUDE_CODE, 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_SERVER_PASSWORD: process.env["OPENCODE_SERVER_PASSWORD"], OPENCODE_SERVER_USERNAME: process.env["OPENCODE_SERVER_USERNAME"], diff --git a/packages/opencode/src/effect/runtime-flags.ts b/packages/opencode/src/effect/runtime-flags.ts index 7ddc7521fb..50dd6d2f9a 100644 --- a/packages/opencode/src/effect/runtime-flags.ts +++ b/packages/opencode/src/effect/runtime-flags.ts @@ -17,6 +17,7 @@ export class Service extends ConfigService.Service()("@opencode/Runtime disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"), disableChannelDb: bool("OPENCODE_DISABLE_CHANNEL_DB"), disableEmbeddedWebUi: bool("OPENCODE_DISABLE_EMBEDDED_WEB_UI"), + disableExternalSkills: bool("OPENCODE_DISABLE_EXTERNAL_SKILLS"), disableClaudeCodeSkills: Config.all({ broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"), direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS"), diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts index 7347707949..c2830ee06d 100644 --- a/packages/opencode/src/skill/index.ts +++ b/packages/opencode/src/skill/index.ts @@ -5,7 +5,6 @@ import { NamedError } from "@opencode-ai/core/util/error" import type { Agent } from "@/agent/agent" import { Bus } from "@/bus" import { InstanceState } from "@/effect/instance-state" -import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" import { Permission } from "@/permission" import { AppFileSystem } from "@opencode-ai/core/filesystem" @@ -166,6 +165,7 @@ const discoverSkills = Effect.fnUntraced(function* ( discovery: Discovery.Interface, fsys: AppFileSystem.Interface, global: Global.Interface, + disableExternalSkills: boolean, disableClaudeCodeSkills: boolean, directory: string, worktree: string, @@ -173,7 +173,7 @@ const discoverSkills = Effect.fnUntraced(function* ( const state: ScanState = { matches: new Set(), dirs: new Set() } const externalDirs: string[] = [] - if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) { + if (!disableExternalSkills) { if (!disableClaudeCodeSkills) externalDirs.push(CLAUDE_EXTERNAL_DIR) externalDirs.push(AGENTS_EXTERNAL_DIR) @@ -249,6 +249,7 @@ export const layer = Layer.effect( discovery, fsys, global, + flags.disableExternalSkills, flags.disableClaudeCodeSkills, ctx.directory, ctx.worktree, diff --git a/packages/opencode/test/effect/runtime-flags.test.ts b/packages/opencode/test/effect/runtime-flags.test.ts index 3fd058b3df..8d265cdaca 100644 --- a/packages/opencode/test/effect/runtime-flags.test.ts +++ b/packages/opencode/test/effect/runtime-flags.test.ts @@ -27,6 +27,7 @@ describe("RuntimeFlags", () => { OPENCODE_DISABLE_CHANNEL_DB: "true", OPENCODE_AUTO_SHARE: "true", OPENCODE_DISABLE_EMBEDDED_WEB_UI: "true", + OPENCODE_DISABLE_EXTERNAL_SKILLS: "true", OPENCODE_EXPERIMENTAL: "true", OPENCODE_ENABLE_EXA: "true", OPENCODE_ENABLE_PARALLEL: "true", @@ -42,6 +43,7 @@ describe("RuntimeFlags", () => { expect(flags.disableDefaultPlugins).toBe(true) expect(flags.disableChannelDb).toBe(true) expect(flags.disableEmbeddedWebUi).toBe(true) + expect(flags.disableExternalSkills).toBe(true) expect(flags.enableExa).toBe(true) expect(flags.enableParallel).toBe(true) expect(flags.enableExperimentalModels).toBe(true) @@ -84,6 +86,7 @@ describe("RuntimeFlags", () => { expect(flags.disableDefaultPlugins).toBe(true) expect(flags.disableChannelDb).toBe(false) expect(flags.disableEmbeddedWebUi).toBe(false) + expect(flags.disableExternalSkills).toBe(false) expect(flags.disableClaudeCodeSkills).toBe(false) expect(flags.enableExa).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", () => Effect.gen(function* () { const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true" }))) @@ -222,6 +241,7 @@ describe("RuntimeFlags", () => { ConfigProvider.fromUnknown({ OPENCODE_PURE: "true", OPENCODE_DISABLE_DEFAULT_PLUGINS: "true", + OPENCODE_DISABLE_EXTERNAL_SKILLS: "true", OPENCODE_EXPERIMENTAL: "true", OPENCODE_ENABLE_EXA: "true", OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234", @@ -235,6 +255,7 @@ describe("RuntimeFlags", () => { expect(flags.disableDefaultPlugins).toBe(false) expect(flags.disableChannelDb).toBe(false) expect(flags.disableEmbeddedWebUi).toBe(false) + expect(flags.disableExternalSkills).toBe(false) expect(flags.disableClaudeCodeSkills).toBe(false) expect(flags.enableExa).toBe(false) expect(flags.experimentalIconDiscovery).toBe(false) diff --git a/packages/opencode/test/skill/skill.test.ts b/packages/opencode/test/skill/skill.test.ts index bc955728bb..149cad1f2d 100644 --- a/packages/opencode/test/skill/skill.test.ts +++ b/packages/opencode/test/skill/skill.test.ts @@ -29,6 +29,19 @@ const itWithoutClaudeCodeSkills = testEffect( 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) { 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 !== "") + expect(list.map((s) => s.name)).toEqual(["opencode-skill"]) + }), + { git: true }, + ), + ) + it.live("properly resolves directories that skills live in", () => provideTmpdirInstance( (dir) =>