diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts index 65ca1e9bb286..6fb2a7aeb57f 100644 --- a/packages/opencode/src/session/instruction.ts +++ b/packages/opencode/src/session/instruction.ts @@ -17,13 +17,14 @@ const FILES = [ ] function globalFiles() { - const files = [path.join(Global.Path.config, "AGENTS.md")] - if (!Flag.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT) { - files.push(path.join(os.homedir(), ".claude", "CLAUDE.md")) - } + const files = [] if (Flag.OPENCODE_CONFIG_DIR) { files.push(path.join(Flag.OPENCODE_CONFIG_DIR, "AGENTS.md")) } + files.push(path.join(Global.Path.config, "AGENTS.md")) + if (!Flag.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT) { + files.push(path.join(os.homedir(), ".claude", "CLAUDE.md")) + } return files } diff --git a/packages/opencode/test/session/instruction.test.ts b/packages/opencode/test/session/instruction.test.ts index 4d57e92a259b..e0bf94a9500d 100644 --- a/packages/opencode/test/session/instruction.test.ts +++ b/packages/opencode/test/session/instruction.test.ts @@ -1,7 +1,8 @@ -import { describe, expect, test } from "bun:test" +import { afterEach, beforeEach, describe, expect, test } from "bun:test" import path from "path" import { InstructionPrompt } from "../../src/session/instruction" import { Instance } from "../../src/project/instance" +import { Global } from "../../src/global" import { tmpdir } from "../fixture/fixture" describe("InstructionPrompt.resolve", () => { @@ -68,3 +69,102 @@ describe("InstructionPrompt.resolve", () => { }) }) }) + +describe("InstructionPrompt.systemPaths OPENCODE_CONFIG_DIR", () => { + let originalConfigDir: string | undefined + + beforeEach(() => { + originalConfigDir = process.env["OPENCODE_CONFIG_DIR"] + }) + + afterEach(() => { + if (originalConfigDir === undefined) { + delete process.env["OPENCODE_CONFIG_DIR"] + } else { + process.env["OPENCODE_CONFIG_DIR"] = originalConfigDir + } + }) + + test("prefers OPENCODE_CONFIG_DIR AGENTS.md over global when both exist", async () => { + await using profileTmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "AGENTS.md"), "# Profile Instructions") + }, + }) + await using globalTmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "AGENTS.md"), "# Global Instructions") + }, + }) + await using projectTmp = await tmpdir() + + process.env["OPENCODE_CONFIG_DIR"] = profileTmp.path + const originalGlobalConfig = Global.Path.config + ;(Global.Path as { config: string }).config = globalTmp.path + + try { + await Instance.provide({ + directory: projectTmp.path, + fn: async () => { + const paths = await InstructionPrompt.systemPaths() + expect(paths.has(path.join(profileTmp.path, "AGENTS.md"))).toBe(true) + expect(paths.has(path.join(globalTmp.path, "AGENTS.md"))).toBe(false) + }, + }) + } finally { + ;(Global.Path as { config: string }).config = originalGlobalConfig + } + }) + + test("falls back to global AGENTS.md when OPENCODE_CONFIG_DIR has no AGENTS.md", async () => { + await using profileTmp = await tmpdir() + await using globalTmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "AGENTS.md"), "# Global Instructions") + }, + }) + await using projectTmp = await tmpdir() + + process.env["OPENCODE_CONFIG_DIR"] = profileTmp.path + const originalGlobalConfig = Global.Path.config + ;(Global.Path as { config: string }).config = globalTmp.path + + try { + await Instance.provide({ + directory: projectTmp.path, + fn: async () => { + const paths = await InstructionPrompt.systemPaths() + expect(paths.has(path.join(profileTmp.path, "AGENTS.md"))).toBe(false) + expect(paths.has(path.join(globalTmp.path, "AGENTS.md"))).toBe(true) + }, + }) + } finally { + ;(Global.Path as { config: string }).config = originalGlobalConfig + } + }) + + test("uses global AGENTS.md when OPENCODE_CONFIG_DIR is not set", async () => { + await using globalTmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "AGENTS.md"), "# Global Instructions") + }, + }) + await using projectTmp = await tmpdir() + + delete process.env["OPENCODE_CONFIG_DIR"] + const originalGlobalConfig = Global.Path.config + ;(Global.Path as { config: string }).config = globalTmp.path + + try { + await Instance.provide({ + directory: projectTmp.path, + fn: async () => { + const paths = await InstructionPrompt.systemPaths() + expect(paths.has(path.join(globalTmp.path, "AGENTS.md"))).toBe(true) + }, + }) + } finally { + ;(Global.Path as { config: string }).config = originalGlobalConfig + } + }) +})