fix(core): load config across git boundaries (#36577)

Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-07-13 00:05:41 +00:00 committed by GitHub
commit f112a73c06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 17 additions and 40 deletions

View file

@ -404,7 +404,7 @@ const layer = Layer.effect(
}
if (!Flag.OPENCODE_DISABLE_PROJECT_CONFIG) {
for (const file of yield* ConfigPaths.files("opencode", ctx.directory).pipe(Effect.orDie)) {
for (const file of yield* ConfigPaths.files("opencode", ctx.directory, ctx.worktree).pipe(Effect.orDie)) {
yield* merge(file, yield* loadFile(file, authEnv), "local")
}
}
@ -413,7 +413,7 @@ const layer = Layer.effect(
result.mode = result.mode || {}
result.plugin = result.plugin || []
const directories = yield* ConfigPaths.directories(ctx.directory)
const directories = yield* ConfigPaths.directories(ctx.directory, ctx.worktree)
if (Flag.OPENCODE_CONFIG_DIR) {
yield* Effect.logDebug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })

View file

@ -7,23 +7,29 @@ import { unique } from "remeda"
import * as Effect from "effect/Effect"
import { FSUtil } from "@opencode-ai/core/fs-util"
export const files = Effect.fn("ConfigPaths.projectFiles")(function* (name: string, directory: string) {
export const files = Effect.fn("ConfigPaths.projectFiles")(function* (
name: string,
directory: string,
worktree?: string,
) {
const afs = yield* FSUtil.Service
return (yield* afs.up({
targets: [`${name}.jsonc`, `${name}.json`],
start: directory,
stop: worktree,
})).toReversed()
})
export const directories = Effect.fn("ConfigPaths.directories")(function* (directory: string) {
export const directories = Effect.fn("ConfigPaths.directories")(function* (directory: string, worktree?: string) {
const afs = yield* FSUtil.Service
return unique([
Global.Path.config,
...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG
? (yield* afs.up({
? yield* afs.up({
targets: [".opencode"],
start: directory,
})).toReversed()
stop: worktree,
})
: []),
...(yield* afs.up({
targets: [".opencode"],

View file

@ -999,37 +999,6 @@ it.instance("resolves scoped npm plugins in config", () =>
}),
)
it.effect("loads shared config above a child git repository", () =>
Effect.gen(function* () {
const root = yield* tmpdirScoped()
const global = yield* tmpdirScoped()
const workspace = path.join(root, "workspace")
const project = path.join(workspace, "project")
yield* writeConfigEffect(path.join(workspace, ".opencode"), {
model: "shared/model",
username: "shared-user",
})
yield* writeConfigEffect(path.join(project, ".opencode"), { username: "project-user" })
yield* Effect.promise(async () => {
const child = Bun.spawn(["git", "init"], { cwd: project, stdout: "ignore", stderr: "ignore" })
await child.exited
})
yield* withGlobalConfigDir(
global,
withInstanceDir(
project,
Effect.gen(function* () {
const config = yield* Config.use.get()
expect(config.model).toBe("shared/model")
expect(config.username).toBe("project-user")
}),
),
)
}),
)
it.effect("merges plugin arrays from global and local configs", () =>
withConfigTree(
{