mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-15 17:13:12 +00:00
refactor(flags): migrate claude code prompt flag (#27690)
This commit is contained in:
@@ -29,7 +29,6 @@ export const Flag = {
|
||||
OPENCODE_DISABLE_MODELS_FETCH: truthy("OPENCODE_DISABLE_MODELS_FETCH"),
|
||||
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_FAKE_VCS: process.env["OPENCODE_FAKE_VCS"],
|
||||
OPENCODE_SERVER_PASSWORD: process.env["OPENCODE_SERVER_PASSWORD"],
|
||||
OPENCODE_SERVER_USERNAME: process.env["OPENCODE_SERVER_USERNAME"],
|
||||
|
||||
@@ -18,6 +18,10 @@ export class Service extends ConfigService.Service<Service>()("@opencode/Runtime
|
||||
disableChannelDb: bool("OPENCODE_DISABLE_CHANNEL_DB"),
|
||||
disableEmbeddedWebUi: bool("OPENCODE_DISABLE_EMBEDDED_WEB_UI"),
|
||||
disableExternalSkills: bool("OPENCODE_DISABLE_EXTERNAL_SKILLS"),
|
||||
disableClaudeCodePrompt: Config.all({
|
||||
broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"),
|
||||
direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_PROMPT"),
|
||||
}).pipe(Config.map((flags) => flags.broad || flags.direct)),
|
||||
disableClaudeCodeSkills: Config.all({
|
||||
broad: bool("OPENCODE_DISABLE_CLAUDE_CODE"),
|
||||
direct: bool("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS"),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Effect, Layer, Context } from "effect"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
|
||||
import { Config } from "@/config/config"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { withTransientReadRetry } from "@/util/effect-http-client"
|
||||
@@ -10,9 +11,9 @@ import { Global } from "@opencode-ai/core/global"
|
||||
import type { MessageV2 } from "./message-v2"
|
||||
import type { MessageID } from "./schema"
|
||||
|
||||
const FILES = [
|
||||
const files = (disableClaudeCodePrompt: boolean) => [
|
||||
"AGENTS.md",
|
||||
...(Flag.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT ? [] : ["CLAUDE.md"]),
|
||||
...(disableClaudeCodePrompt ? [] : ["CLAUDE.md"]),
|
||||
"CONTEXT.md", // deprecated
|
||||
]
|
||||
|
||||
@@ -50,18 +51,20 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/In
|
||||
export const layer: Layer.Layer<
|
||||
Service,
|
||||
never,
|
||||
AppFileSystem.Service | Config.Service | Global.Service | HttpClient.HttpClient
|
||||
AppFileSystem.Service | Config.Service | Global.Service | HttpClient.HttpClient | RuntimeFlags.Service
|
||||
> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const cfg = yield* Config.Service
|
||||
const fs = yield* AppFileSystem.Service
|
||||
const global = yield* Global.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient))
|
||||
const globalFiles = [
|
||||
path.join(global.config, "AGENTS.md"),
|
||||
...(!Flag.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT ? [path.join(global.home, ".claude", "CLAUDE.md")] : []),
|
||||
...(!flags.disableClaudeCodePrompt ? [path.join(global.home, ".claude", "CLAUDE.md")] : []),
|
||||
]
|
||||
const instructionFiles = files(flags.disableClaudeCodePrompt)
|
||||
|
||||
const state = yield* InstanceState.make(
|
||||
Effect.fn("Instruction.state")(() =>
|
||||
@@ -117,7 +120,7 @@ export const layer: Layer.Layer<
|
||||
|
||||
// The first project-level match wins so we don't stack AGENTS.md/CLAUDE.md from every ancestor.
|
||||
if (!Flag.OPENCODE_DISABLE_PROJECT_CONFIG) {
|
||||
for (const file of FILES) {
|
||||
for (const file of instructionFiles) {
|
||||
const matches = yield* fs
|
||||
.findUp(file, ctx.directory, ctx.worktree)
|
||||
.pipe(Effect.catch(() => Effect.succeed([])))
|
||||
@@ -165,7 +168,7 @@ export const layer: Layer.Layer<
|
||||
})
|
||||
|
||||
const find = Effect.fn("Instruction.find")(function* (dir: string) {
|
||||
for (const file of FILES) {
|
||||
for (const file of instructionFiles) {
|
||||
const filepath = path.resolve(path.join(dir, file))
|
||||
if (yield* fs.existsSafe(filepath)) return filepath
|
||||
}
|
||||
@@ -225,6 +228,7 @@ export const defaultLayer = layer.pipe(
|
||||
Layer.provide(Global.layer),
|
||||
Layer.provide(AppFileSystem.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(RuntimeFlags.defaultLayer),
|
||||
)
|
||||
|
||||
export function loaded(messages: MessageV2.WithParts[]) {
|
||||
|
||||
@@ -44,6 +44,7 @@ describe("RuntimeFlags", () => {
|
||||
expect(flags.disableChannelDb).toBe(true)
|
||||
expect(flags.disableEmbeddedWebUi).toBe(true)
|
||||
expect(flags.disableExternalSkills).toBe(true)
|
||||
expect(flags.disableClaudeCodePrompt).toBe(false)
|
||||
expect(flags.enableExa).toBe(true)
|
||||
expect(flags.enableParallel).toBe(true)
|
||||
expect(flags.enableExperimentalModels).toBe(true)
|
||||
@@ -87,6 +88,7 @@ describe("RuntimeFlags", () => {
|
||||
expect(flags.disableChannelDb).toBe(false)
|
||||
expect(flags.disableEmbeddedWebUi).toBe(false)
|
||||
expect(flags.disableExternalSkills).toBe(false)
|
||||
expect(flags.disableClaudeCodePrompt).toBe(false)
|
||||
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||
expect(flags.enableExa).toBe(false)
|
||||
expect(flags.experimentalIconDiscovery).toBe(false)
|
||||
@@ -122,6 +124,30 @@ describe("RuntimeFlags", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("disableClaudeCodePrompt defaults to false", () =>
|
||||
Effect.gen(function* () {
|
||||
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
|
||||
|
||||
expect(flags.disableClaudeCodePrompt).toBe(false)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("disableClaudeCodePrompt reads OPENCODE_DISABLE_CLAUDE_CODE_PROMPT", () =>
|
||||
Effect.gen(function* () {
|
||||
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE_PROMPT: "true" })))
|
||||
|
||||
expect(flags.disableClaudeCodePrompt).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("disableClaudeCodePrompt inherits OPENCODE_DISABLE_CLAUDE_CODE", () =>
|
||||
Effect.gen(function* () {
|
||||
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE: "true" })))
|
||||
|
||||
expect(flags.disableClaudeCodePrompt).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" })))
|
||||
@@ -256,6 +282,7 @@ describe("RuntimeFlags", () => {
|
||||
expect(flags.disableChannelDb).toBe(false)
|
||||
expect(flags.disableEmbeddedWebUi).toBe(false)
|
||||
expect(flags.disableExternalSkills).toBe(false)
|
||||
expect(flags.disableClaudeCodePrompt).toBe(false)
|
||||
expect(flags.disableClaudeCodeSkills).toBe(false)
|
||||
expect(flags.enableExa).toBe(false)
|
||||
expect(flags.experimentalIconDiscovery).toBe(false)
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Instruction } from "../../src/session/instruction"
|
||||
import type { MessageV2 } from "../../src/session/message-v2"
|
||||
import { MessageID, PartID, SessionID } from "../../src/session/schema"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||
import { provideInstance, provideTmpdirInstance, tmpdirScoped } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { TestConfig } from "../fixture/config"
|
||||
@@ -18,18 +19,19 @@ const it = testEffect(Layer.mergeAll(CrossSpawnSpawner.defaultLayer, NodeFileSys
|
||||
|
||||
const configLayer = TestConfig.layer()
|
||||
|
||||
const instructionLayer = (global: Partial<Global.Interface>) =>
|
||||
const instructionLayer = (global: Partial<Global.Interface>, flags: Partial<RuntimeFlags.Info> = {}) =>
|
||||
Instruction.layer.pipe(
|
||||
Layer.provide(configLayer),
|
||||
Layer.provide(AppFileSystem.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(Global.layerWith(global)),
|
||||
Layer.provide(RuntimeFlags.layer(flags)),
|
||||
)
|
||||
|
||||
const provideInstruction =
|
||||
(global: Partial<Global.Interface>) =>
|
||||
(global: Partial<Global.Interface>, flags?: Partial<RuntimeFlags.Info>) =>
|
||||
<A, E, R>(self: Effect.Effect<A, E, R>) =>
|
||||
self.pipe(Effect.provide(instructionLayer(global)))
|
||||
self.pipe(Effect.provide(instructionLayer(global, flags)))
|
||||
|
||||
const write = (filepath: string, content: string) =>
|
||||
Effect.gen(function* () {
|
||||
@@ -215,6 +217,24 @@ describe("Instruction.system", () => {
|
||||
}).pipe(provideInstance(projectTmp), provideInstruction({ home: globalTmp, config: globalTmp }))
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("skips project and global CLAUDE.md when Claude Code prompt is disabled", () =>
|
||||
Effect.gen(function* () {
|
||||
const globalTmp = yield* tmpWithFiles({ ".claude/CLAUDE.md": "# Global Claude" })
|
||||
const projectTmp = yield* tmpWithFiles({ "CLAUDE.md": "# Project Claude" })
|
||||
|
||||
yield* Effect.gen(function* () {
|
||||
const svc = yield* Instruction.Service
|
||||
const paths = yield* svc.systemPaths()
|
||||
expect(paths.has(path.join(globalTmp, ".claude", "CLAUDE.md"))).toBe(false)
|
||||
expect(paths.has(path.join(projectTmp, "CLAUDE.md"))).toBe(false)
|
||||
expect(yield* svc.system()).toEqual([])
|
||||
}).pipe(
|
||||
provideInstance(projectTmp),
|
||||
provideInstruction({ home: globalTmp, config: globalTmp }, { disableClaudeCodePrompt: true }),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe("Instruction.systemPaths global config", () => {
|
||||
|
||||
Reference in New Issue
Block a user