mirror of
https://github.com/anomalyco/opencode.git
synced 2026-06-01 19:05:38 +00:00
refactor(flags): migrate claude code skills flag to RuntimeFlags (#27605)
This commit is contained in:
@@ -14,8 +14,6 @@ function number(key: string) {
|
|||||||
|
|
||||||
const OPENCODE_EXPERIMENTAL = truthy("OPENCODE_EXPERIMENTAL")
|
const OPENCODE_EXPERIMENTAL = truthy("OPENCODE_EXPERIMENTAL")
|
||||||
const OPENCODE_DISABLE_CLAUDE_CODE = truthy("OPENCODE_DISABLE_CLAUDE_CODE")
|
const OPENCODE_DISABLE_CLAUDE_CODE = truthy("OPENCODE_DISABLE_CLAUDE_CODE")
|
||||||
const OPENCODE_DISABLE_CLAUDE_CODE_SKILLS =
|
|
||||||
OPENCODE_DISABLE_CLAUDE_CODE || truthy("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")
|
|
||||||
const copy = process.env["OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT"]
|
const copy = process.env["OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT"]
|
||||||
|
|
||||||
export const Flag = {
|
export const Flag = {
|
||||||
@@ -41,7 +39,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_CLAUDE_CODE_SKILLS,
|
|
||||||
OPENCODE_DISABLE_EXTERNAL_SKILLS: truthy("OPENCODE_DISABLE_EXTERNAL_SKILLS"),
|
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"],
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ const enabledByExperimental = (name: string) =>
|
|||||||
export class Service extends ConfigService.Service<Service>()("@opencode/RuntimeFlags", {
|
export class Service extends ConfigService.Service<Service>()("@opencode/RuntimeFlags", {
|
||||||
pure: bool("OPENCODE_PURE"),
|
pure: bool("OPENCODE_PURE"),
|
||||||
disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"),
|
disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"),
|
||||||
|
disableClaudeCodeSkills: Config.all({
|
||||||
|
broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"),
|
||||||
|
direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS"),
|
||||||
|
}).pipe(Config.map((flags) => flags.broad || flags.direct)),
|
||||||
enableExa: Config.all({
|
enableExa: Config.all({
|
||||||
experimental,
|
experimental,
|
||||||
enabled: bool("OPENCODE_ENABLE_EXA"),
|
enabled: bool("OPENCODE_ENABLE_EXA"),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Permission } from "@/permission"
|
|||||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { ConfigMarkdown } from "@/config/markdown"
|
import { ConfigMarkdown } from "@/config/markdown"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
import { Glob } from "@opencode-ai/core/util/glob"
|
import { Glob } from "@opencode-ai/core/util/glob"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { Discovery } from "./discovery"
|
import { Discovery } from "./discovery"
|
||||||
@@ -165,6 +166,7 @@ const discoverSkills = Effect.fnUntraced(function* (
|
|||||||
discovery: Discovery.Interface,
|
discovery: Discovery.Interface,
|
||||||
fsys: AppFileSystem.Interface,
|
fsys: AppFileSystem.Interface,
|
||||||
global: Global.Interface,
|
global: Global.Interface,
|
||||||
|
disableClaudeCodeSkills: boolean,
|
||||||
directory: string,
|
directory: string,
|
||||||
worktree: string,
|
worktree: string,
|
||||||
) {
|
) {
|
||||||
@@ -172,7 +174,7 @@ const discoverSkills = Effect.fnUntraced(function* (
|
|||||||
|
|
||||||
const externalDirs: string[] = []
|
const externalDirs: string[] = []
|
||||||
if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) {
|
if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) {
|
||||||
if (!Flag.OPENCODE_DISABLE_CLAUDE_CODE_SKILLS) externalDirs.push(CLAUDE_EXTERNAL_DIR)
|
if (!disableClaudeCodeSkills) externalDirs.push(CLAUDE_EXTERNAL_DIR)
|
||||||
externalDirs.push(AGENTS_EXTERNAL_DIR)
|
externalDirs.push(AGENTS_EXTERNAL_DIR)
|
||||||
|
|
||||||
for (const dir of externalDirs) {
|
for (const dir of externalDirs) {
|
||||||
@@ -239,9 +241,18 @@ export const layer = Layer.effect(
|
|||||||
const bus = yield* Bus.Service
|
const bus = yield* Bus.Service
|
||||||
const fsys = yield* AppFileSystem.Service
|
const fsys = yield* AppFileSystem.Service
|
||||||
const global = yield* Global.Service
|
const global = yield* Global.Service
|
||||||
|
const flags = yield* RuntimeFlags.Service
|
||||||
const discovered = yield* InstanceState.make(
|
const discovered = yield* InstanceState.make(
|
||||||
Effect.fn("Skill.discovery")(function* (ctx) {
|
Effect.fn("Skill.discovery")(function* (ctx) {
|
||||||
return yield* discoverSkills(config, discovery, fsys, global, ctx.directory, ctx.worktree)
|
return yield* discoverSkills(
|
||||||
|
config,
|
||||||
|
discovery,
|
||||||
|
fsys,
|
||||||
|
global,
|
||||||
|
flags.disableClaudeCodeSkills,
|
||||||
|
ctx.directory,
|
||||||
|
ctx.worktree,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const state = yield* InstanceState.make(
|
const state = yield* InstanceState.make(
|
||||||
@@ -291,6 +302,7 @@ export const defaultLayer = layer.pipe(
|
|||||||
Layer.provide(Bus.layer),
|
Layer.provide(Bus.layer),
|
||||||
Layer.provide(AppFileSystem.defaultLayer),
|
Layer.provide(AppFileSystem.defaultLayer),
|
||||||
Layer.provide(Global.layer),
|
Layer.provide(Global.layer),
|
||||||
|
Layer.provide(RuntimeFlags.defaultLayer),
|
||||||
)
|
)
|
||||||
|
|
||||||
export function fmt(list: Info[], opts: { verbose: boolean }) {
|
export function fmt(list: Info[], opts: { verbose: boolean }) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ describe("RuntimeFlags", () => {
|
|||||||
|
|
||||||
expect(flags.pure).toBe(false)
|
expect(flags.pure).toBe(false)
|
||||||
expect(flags.disableDefaultPlugins).toBe(true)
|
expect(flags.disableDefaultPlugins).toBe(true)
|
||||||
|
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||||
expect(flags.enableExa).toBe(false)
|
expect(flags.enableExa).toBe(false)
|
||||||
expect(flags.client).toBe("cli")
|
expect(flags.client).toBe("cli")
|
||||||
}),
|
}),
|
||||||
@@ -70,8 +71,35 @@ describe("RuntimeFlags", () => {
|
|||||||
|
|
||||||
expect(flags.pure).toBe(false)
|
expect(flags.pure).toBe(false)
|
||||||
expect(flags.disableDefaultPlugins).toBe(false)
|
expect(flags.disableDefaultPlugins).toBe(false)
|
||||||
|
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||||
expect(flags.enableExa).toBe(false)
|
expect(flags.enableExa).toBe(false)
|
||||||
expect(flags.client).toBe("cli")
|
expect(flags.client).toBe("cli")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.effect("disableClaudeCodeSkills defaults to false", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
|
||||||
|
|
||||||
|
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.effect("disableClaudeCodeSkills reads OPENCODE_DISABLE_CLAUDE_CODE_SKILLS", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* readFlags.pipe(
|
||||||
|
Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE_SKILLS: "true" })),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(flags.disableClaudeCodeSkills).toBe(true)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.effect("disableClaudeCodeSkills inherits OPENCODE_DISABLE_CLAUDE_CODE", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE: "true" })))
|
||||||
|
|
||||||
|
expect(flags.disableClaudeCodeSkills).toBe(true)
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { describe, expect } from "bun:test"
|
import { describe, expect } from "bun:test"
|
||||||
import { Effect, Layer } from "effect"
|
import { Effect, Layer } from "effect"
|
||||||
import { Skill } from "../../src/skill"
|
import { Skill } from "../../src/skill"
|
||||||
|
import { Discovery } from "../../src/skill/discovery"
|
||||||
|
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||||
|
import { Bus } from "../../src/bus"
|
||||||
|
import { Config } from "../../src/config/config"
|
||||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||||
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { provideInstance, provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
import { provideInstance, provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
@@ -10,6 +16,19 @@ import fs from "fs/promises"
|
|||||||
const node = CrossSpawnSpawner.defaultLayer
|
const node = CrossSpawnSpawner.defaultLayer
|
||||||
|
|
||||||
const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node))
|
const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node))
|
||||||
|
const itWithoutClaudeCodeSkills = 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({ disableClaudeCodeSkills: 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")
|
||||||
@@ -364,6 +383,43 @@ description: A skill in the .agents/skills directory.
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
itWithoutClaudeCodeSkills.live("skips Claude Code skills 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
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
const skill = yield* Skill.Service
|
||||||
|
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
|
||||||
|
expect(list.map((s) => s.name)).toEqual(["agent-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