diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 867bc0fe9ea..4cce0a0b216 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -31,6 +31,7 @@ export namespace Agent { }) .optional(), prompt: z.string().optional(), + instructions: z.array(z.string()).optional(), tools: z.record(z.string(), z.boolean()), options: z.record(z.string(), z.any()), }) @@ -148,13 +149,27 @@ export namespace Agent { tools: {}, builtIn: false, } - const { name, model, prompt, tools, description, temperature, top_p, mode, permission, color, ...extra } = value + const { + name, + model, + prompt, + tools, + description, + temperature, + top_p, + mode, + permission, + color, + instructions, + ...extra + } = value item.options = { ...item.options, ...extra, } if (model) item.model = Provider.parseModel(model) if (prompt) item.prompt = prompt + if (instructions) item.instructions = instructions if (tools) item.tools = { ...item.tools, diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 440d95818e2..409766802dc 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -364,6 +364,10 @@ export namespace Config { .regex(/^#[0-9a-fA-F]{6}$/, "Invalid hex color format") .optional() .describe("Hex color code for the agent (e.g., #FF5733)"), + instructions: z + .array(z.string()) + .optional() + .describe("Additional instruction files or patterns to include for this agent"), permission: z .object({ edit: Permission.optional(), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 8e27d7148bd..c9205a15285 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -556,6 +556,9 @@ export namespace SessionPrompt { ) system.push(...(await SystemPrompt.environment())) system.push(...(await SystemPrompt.custom())) + if (input.agent.instructions) { + system.push(...(await SystemPrompt.agentInstructions(input.agent.instructions))) + } // max 2 system prompt messages for caching purposes const [first, ...rest] = system system = [first, rest.join("\n")] diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 7d44bbda4e9..3221793dc69 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -116,6 +116,37 @@ export namespace SystemPrompt { return Promise.all(found).then((result) => result.filter(Boolean)) } + export async function agentInstructions(instructions: string[]) { + const paths = new Set() + + for (let instruction of instructions) { + if (instruction.startsWith("~/")) { + instruction = path.join(os.homedir(), instruction.slice(2)) + } + let matches: string[] = [] + if (path.isAbsolute(instruction)) { + matches = await Array.fromAsync( + new Bun.Glob(path.basename(instruction)).scan({ + cwd: path.dirname(instruction), + absolute: true, + onlyFiles: true, + }), + ).catch(() => []) + } else { + matches = await Filesystem.globUp(instruction, Instance.directory, Instance.worktree).catch(() => []) + } + matches.forEach((path) => paths.add(path)) + } + + const found = Array.from(paths).map((p) => + Bun.file(p) + .text() + .catch(() => "") + .then((x) => "Instructions from: " + p + "\n" + x), + ) + return Promise.all(found).then((result) => result.filter(Boolean)) + } + export function summarize(providerID: string) { switch (providerID) { case "anthropic": diff --git a/packages/web/src/content/docs/agents.mdx b/packages/web/src/content/docs/agents.mdx index c99988ad8a5..774c293ad9a 100644 --- a/packages/web/src/content/docs/agents.mdx +++ b/packages/web/src/content/docs/agents.mdx @@ -299,6 +299,48 @@ Use the `model` config to override the default model for this agent. Useful for --- +### Instructions + +Use `instructions` to load extra instruction files just for a single agent. + +```json title="opencode.json" +{ + "agent": { + "review": { + "mode": "subagent", + "instructions": ["./code-standards.md", "./.app/docs/*.md", "~/team-guidelines.md"] + } + } +} +``` + +Instruction files are appended after the agent's prompt and global instructions. Paths can be: + +- Relative or glob paths resolved from your project up to the git worktree +- Absolute paths (e.g., "/path/to/file.md") +- Home-relative paths using `~/` (e.g., "~/standards.md") + +You can also set `instructions` in markdown agents: + +```markdown title="~/.config/opencode/agent/review.md" +--- +description: Reviews code for quality and best practices +mode: subagent +instructions: + - ./review-checklist.md + - ./.app/docs/*.md + - ~/coding-standards.md +--- + +You are in code review mode. Follow the guidelines in the instruction files. +``` + +:::note +Agent-specific instructions are loaded **in addition to** global `instructions` in your main config. Global instructions apply to all agents; agent instructions apply only to that agent. +::: + +--- + ### Tools Control which tools are available in this agent with the `tools` config. You can enable or disable specific tools by setting them to `true` or `false`.