Dynamic system prompts for custom agents #2116

Open
opened 2026-02-16 17:34:13 -05:00 by yindo · 15 comments
Owner

Originally created by @Flux159 on GitHub (Oct 15, 2025).

Originally assigned to: @rekram1-node on GitHub.

Hi, I am writing a few custom agents using the markdown format as described here: https://opencode.ai/docs/agents#markdown. Currently I'm able to add custom tools via the ~/.opencode/tool folder & then specify them in the markdown frontmatter to customize the tools available for said agent. This works well enough when the system prompt is static & then the agent can use the tools to retrieve additional information, however it doesn't seem to support dynamically generating the system prompt before any tool calls.

What I wanted to do is allow the system prompt for the agent to dynamically add additional information (without having to use tool calls after the first message for this). This doesn't seem to be possible in markdown (or in the agent json format using a prompt with {file://...) since that simply reads the file).

Not sure if this is the correct code path, but I was taking a look at agent.ts and it seems to use system.ts to add environment information dynamically:
https://github.com/sst/opencode/blob/dev/packages/opencode/src/agent/agent.ts
https://github.com/sst/opencode/blob/dev/packages/opencode/src/session/system.ts#L33

I would want to do something similar, but configurable per agent for different use cases. Essentially supporting either json prompt w/ {file://path/to/typescriptpromptorplugin.ts} or somehow being able to also configure prompt via the markdown frontmatter in a similar way (note that I don't have full context of opencode's codebase so this may not be the right approach).

I was also looking at plugins to see if this is possible with plugins, but going from the list of plugin events, I don't believe that this is possible by using the existing plugin events? https://github.com/sst/opencode/blob/dev/packages/sdk/js/src/gen/types.gen.ts#L1175

One other benefit of being able to dynamically create the system prompt is you avoid a number of initial tool calls & requests to an LLM to get dynamic information for your agent to operate. In other words, something like this:

system (w/ tools to get info) -> 
user request -> 
initial tool calls [N] to get dynamic info -> 
start working on user request

Becomes this:

system + dynamic data preloaded (still with tools, but potentially fewer tools since no need for agent to get dynamic info) -> 
user request -> 
start working on user request

I was wondering if there was currently a way to dynamically create the system prompt for an agent or if this was on the roadmap. Alternatively, if someone can give a code pointer to where I could potentially customize the prompt via code (via typescript file checking / loading or some other method), I could see if I can make a PR to get feedback / comments. Thanks!

Originally created by @Flux159 on GitHub (Oct 15, 2025). Originally assigned to: @rekram1-node on GitHub. Hi, I am writing a few custom agents using the markdown format as described here: https://opencode.ai/docs/agents#markdown. Currently I'm able to add custom tools via the ~/.opencode/tool folder & then specify them in the markdown frontmatter to customize the tools available for said agent. This works well enough when the system prompt is static & then the agent can use the tools to retrieve additional information, however it doesn't seem to support dynamically generating the system prompt before any tool calls. What I wanted to do is allow the system prompt for the agent to dynamically add additional information (without having to use tool calls after the first message for this). This doesn't seem to be possible in markdown (or in the agent json format using a prompt with `{file://...)` since that simply reads the file). Not sure if this is the correct code path, but I was taking a look at agent.ts and it seems to use system.ts to add environment information dynamically: https://github.com/sst/opencode/blob/dev/packages/opencode/src/agent/agent.ts https://github.com/sst/opencode/blob/dev/packages/opencode/src/session/system.ts#L33 I would want to do something similar, but configurable per agent for different use cases. Essentially supporting either json prompt w/ `{file://path/to/typescriptpromptorplugin.ts}` or somehow being able to also configure prompt via the markdown frontmatter in a similar way (note that I don't have full context of opencode's codebase so this may not be the right approach). I was also looking at plugins to see if this is possible with plugins, but going from the list of plugin events, I don't believe that this is possible by using the existing plugin events? https://github.com/sst/opencode/blob/dev/packages/sdk/js/src/gen/types.gen.ts#L1175 One other benefit of being able to dynamically create the system prompt is you avoid a number of initial tool calls & requests to an LLM to get dynamic information for your agent to operate. In other words, something like this: ``` system (w/ tools to get info) -> user request -> initial tool calls [N] to get dynamic info -> start working on user request ``` Becomes this: ``` system + dynamic data preloaded (still with tools, but potentially fewer tools since no need for agent to get dynamic info) -> user request -> start working on user request ``` --- I was wondering if there was currently a way to dynamically create the system prompt for an agent or if this was on the roadmap. Alternatively, if someone can give a code pointer to where I could potentially customize the prompt via code (via typescript file checking / loading or some other method), I could see if I can make a PR to get feedback / comments. Thanks!
Author
Owner

@rekram1-node commented on GitHub (Oct 15, 2025):

@Flux159 do you want it to be hyper dynamic with plugins or would it be good if the agent definitions supported other ways of injecting context, for example with commands we have:

!echo hello -> becomes -> "echo hello"

example for commands: https://opencode.ai/docs/commands/#shell-output

We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents

Would something like that suffice for your use case? Or are you looking for a plugin to edit system prompts given certain events

@rekram1-node commented on GitHub (Oct 15, 2025): @Flux159 do you want it to be hyper dynamic with plugins or would it be good if the agent definitions supported other ways of injecting context, for example with commands we have: !`echo hello` -> becomes -> "echo hello" example for commands: https://opencode.ai/docs/commands/#shell-output We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents Would something like that suffice for your use case? Or are you looking for a plugin to edit system prompts given certain events
Author
Owner

@Flux159 commented on GitHub (Oct 15, 2025):

@rekram1-node - Oh I didn't know about that bash command functionality - I think if that was supported in the agent markdown that would technically allow for any dynamic content w/out needing custom JS? I guess there's no reason I couldn't just do something like:

This is markdown.

!`node myscript.js`

This is static again.

I did a (very quick) prototype of allowing custom JS files as system prompts in #3197 which would've just enabled using js or typescript files inside of file:// prompts - similar to how custom tools are defined / exported, but I'm not tied to any specific method.

Re: Edit system prompts given certain events - I wasn't thinking about this for this issue - I would need to think through what are the right use cases (it would also make it very difficult to debug a single agent session potentially? Like in the middle of a session, the system prompt changes, so any logs that you have would need to indicate that). I don't have any specific uses of this right now, but can add to a discussion here if I think of some later.

@Flux159 commented on GitHub (Oct 15, 2025): @rekram1-node - Oh I didn't know about that bash command functionality - I think if that was supported in the agent markdown that would technically allow for any dynamic content w/out needing custom JS? I guess there's no reason I couldn't just do something like: ```shell This is markdown. !`node myscript.js` This is static again. ``` I did a (very quick) prototype of allowing custom JS files as system prompts in #3197 which would've just enabled using js or typescript files inside of `file://` prompts - similar to how custom tools are defined / exported, but I'm not tied to any specific method. Re: Edit system prompts given certain events - I wasn't thinking about this for this issue - I would need to think through what are the right use cases (it would also make it very difficult to debug a single agent session potentially? Like in the middle of a session, the system prompt changes, so any logs that you have would need to indicate that). I don't have any specific uses of this right now, but can add to a discussion here if I think of some later.
Author
Owner

@rekram1-node commented on GitHub (Oct 15, 2025):

I don't have any specific uses of this right now, but can add to a discussion here if I think of some later.

No need to discuss here I think we have another issue that has that

Perfect I think making the command syntax more universal is best move here then

@rekram1-node commented on GitHub (Oct 15, 2025): > I don't have any specific uses of this right now, but can add to a discussion here if I think of some later. No need to discuss here I think we have another issue that has that Perfect I think making the command syntax more universal is best move here then
Author
Owner

@taqtiqa-mark commented on GitHub (Oct 15, 2025):

I was taking a look at agent.ts and it seems to use system.ts to add environment information dynamically:

@Flux159, I believe this is broken behavior, see https://github.com/sst/opencode/issues/2108#issuecomment-3403600775. It isn't yet clear if its going to be fixed or embraced.

however it doesn't seem to support dynamically generating the system prompt before any tool calls.

I believe the approach proposed here, https://github.com/sst/opencode/issues/1894#issuecomment-3400090446, covers your use case, you would in effect generate SYSTEM.md:

agent/ide.md
agent/ide/SYSTEM.md
agent/ide/create-ticket.md
...

You will need some hook that fires when your tab cycle results in the TUI entering, say, the IDE AGENT state (or +1 sec say): EventAgentSelected

Like in the middle of a session, the system prompt changes

This gets quickly gets fraught. First, yes some vendors do have stateless API's and you can send system messages (prompts) in any order, any number of times. If you want reproducible results you probably don't want that, but it's not impossible to get the same outcome from a random ordering - you just really have to know what you are doing.

Consequently, I suggest conforming with the AI SDK description/definition of a System Prompt:

System prompts are the initial set of instructions given to models that help guide and constrain the models' behaviors and responses....

That said nothing I've proposed thwarts you if that is what you want to do - regenerate the agent/<role>/SYSTEM.md whenever you want. If you want to resend it, you can do so by tab ing back to the <role> AGENT state.

Perhaps there can be a config variable that governs whether multiple System Prompts are submitted per agent-session, say:

//default: true
single_system_prompt: true|false
@taqtiqa-mark commented on GitHub (Oct 15, 2025): > I was taking a look at agent.ts and it seems to use system.ts to add environment information dynamically: @Flux159, I believe this is broken behavior, see https://github.com/sst/opencode/issues/2108#issuecomment-3403600775. It isn't yet clear if its going to be fixed or embraced. > however it doesn't seem to support dynamically generating the system prompt before any tool calls. I believe the approach proposed here, https://github.com/sst/opencode/issues/1894#issuecomment-3400090446, covers your use case, you would in effect generate `SYSTEM.md`: ``` agent/ide.md agent/ide/SYSTEM.md agent/ide/create-ticket.md ... ``` You will need some hook that fires when your `tab` cycle results in the TUI entering, say, the `IDE AGENT` state (or +1 sec say): `EventAgentSelected` > Like in the middle of a session, the system prompt changes This gets quickly gets fraught. First, yes some vendors do have stateless API's and you can send system messages (prompts) in any order, any number of times. If you want reproducible results you probably don't want that, but it's not impossible to get the same outcome from a random ordering - you just really have to know what you are doing. Consequently, I suggest conforming with the AI SDK description/definition of a System Prompt: > [System prompts are the initial set of instructions given to models that help guide and constrain the models' behaviors and responses....](https://ai-sdk.dev/docs/foundations/prompts#system-prompts) That said nothing I've proposed thwarts you if that is what you want to do - regenerate the `agent/<role>/SYSTEM.md` whenever you want. If you want to resend it, you can do so by `tab` ing back to the `<role> AGENT` state. Perhaps there can be a config variable that governs whether multiple System Prompts are submitted per agent-session, say: ```jsonc //default: true single_system_prompt: true|false ```
Author
Owner

@Flux159 commented on GitHub (Oct 15, 2025):

@rekram1-node Put up a new PR that uses templating instead. Works using bun dev for me & seems cleaner since it's consistent with commands.

@Flux159 commented on GitHub (Oct 15, 2025): @rekram1-node Put up a new PR that uses templating instead. Works using `bun dev` for me & seems cleaner since it's consistent with commands.
Author
Owner

@rekram1-node commented on GitHub (Oct 15, 2025):

yo thanks! ill take a look in a bit

@rekram1-node commented on GitHub (Oct 15, 2025): yo thanks! ill take a look in a bit
Author
Owner

@taqtiqa-mark commented on GitHub (Oct 16, 2025):

Note to self... the need for a dedicated SYSTEM.md prompt also applies to subagents. Depending on your set-up perhaps even more so. I such case, extending the layout logic:

agent/ide.md
agent/ide-SYSTEM.md
agent/ide/create-ticket.md
agent/ide/plan.md
agent/ide/plan-SYSTEM.md

...

While this keeps sub-agent SYSTEM.md Prompt content along side their parent agent Prompt and its System Prompt, and renders them easily editable. It does mean the OC parsing logic for sub-agents is no longer a simple file lookup:

  • Now consuming the command file text @ide/plan
  • Needs to resolve to:
    • Submit System message for sub-agent plan session agent/ide/plan-SYSTEM.md
    • Submit User message for sub-agent plan session agent/ide/plan.md

@Flux159, if you also want to make the Sub-Agent's System Prompt/Message dynamic, you'll need different event hook..... nothing intuitive comes to mind.

@taqtiqa-mark commented on GitHub (Oct 16, 2025): Note to self... the need for a dedicated `SYSTEM.md` prompt also applies to subagents. Depending on your set-up perhaps even more so. I such case, extending the layout logic: ``` agent/ide.md agent/ide-SYSTEM.md agent/ide/create-ticket.md agent/ide/plan.md agent/ide/plan-SYSTEM.md ... ``` While this keeps sub-agent `SYSTEM.md` Prompt content along side their parent agent Prompt and its System Prompt, and renders them easily editable. It does mean the OC parsing logic for sub-agents is no longer a simple file lookup: - Now consuming the command file text `@ide/plan` - Needs to resolve to: - Submit System message for sub-agent plan session `agent/ide/plan-SYSTEM.md` - Submit User message for sub-agent plan session `agent/ide/plan.md` @Flux159, if you also want to make the Sub-Agent's System Prompt/Message dynamic, you'll need different event hook..... nothing intuitive comes to mind.
Author
Owner

@FergusFettes commented on GitHub (Oct 19, 2025):

Would love to see this feature! Being able to dynamically inject context into the system prompt would be super useful for custom agents.

Just saw the discussion about templating with shell commands (like !command``) - that would be perfect for my use case with headless background agents. Looking forward to this!

For now I'm working around it by putting dynamic instructions in ~/.config/opencode/AGENTS.md but per-agent dynamic prompts would be much cleaner.

+1 for this feature!

@FergusFettes commented on GitHub (Oct 19, 2025): Would love to see this feature! Being able to dynamically inject context into the system prompt would be super useful for custom agents. Just saw the discussion about templating with shell commands (like `!`command``) - that would be perfect for my use case with headless background agents. Looking forward to this! For now I'm working around it by putting dynamic instructions in ~/.config/opencode/AGENTS.md but per-agent dynamic prompts would be much cleaner. +1 for this feature!
Author
Owner

@towry commented on GitHub (Oct 25, 2025):

I like this feature too.

In claude, it can start with a custom system prompt, so i have a "kiro" workflow that in the system prompt i give the kiro spec doc location and instructions. The best part is after /compact run, this key context will always remain, so the agent will not forget what he is and where the kiro spec doc is.

But in opencode, currently I achieve this through the --prompt argument, in /compact or auto compact, this context will be lost!

@towry commented on GitHub (Oct 25, 2025): I like this feature too. In claude, it can start with a custom system prompt, so i have a "kiro" workflow that in the system prompt i give the kiro spec doc location and instructions. The best part is after `/compact` run, this key context will always remain, so the agent will not forget what he is and where the kiro spec doc is. But in opencode, currently I achieve this through the `--prompt` argument, in `/compact` or auto compact, this context will be lost!
Author
Owner

@dehidehidehi commented on GitHub (Nov 18, 2025):

!echo hello -> becomes -> "echo hello"
example for commands: https://opencode.ai/docs/commands/#shell-output

We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents

I use the 'instructions' field in the opencode.json configuration file, as documented here https://opencode.ai/docs/config/#instructions
For instance :

{
  "$schema": "https://opencode.ai/config.json",
  "instructions": [
    "~/.config/opencode/context/global/global-context.md",
    "~/.config/opencode/context/global/session-context.md"
  ]
}

Global context (exerpt)

# Current session metadata
- term.program: 
!`echo $TERM_PROGRAM`
- term.colors: 
!`echo $COLORTERM`
- term.type: 
!`echo $TERM`
- pwd: 
!`echo $PWD`
- shell: 
!`echo $SHELL`
- xdg.session.type: 
!`echo $XDG_SESSION_TYPE`

Yet, despite this being a rule, when I ask an primary agent in opencode what it's current session metadata is, it litterally replies with this (I did not edit the LLM answer):

Here is the session context I am currently operating with:

- Terminal program: (value from `echo $TERM_PROGRAM`)
- Terminal colors: (value from `echo $COLORTERM`)
- Terminal type: (value from `echo $TERM`)
...

So far I have not been successful in having shell commands executed as instructions, am I doing something wrong ?

Thank you,

@dehidehidehi commented on GitHub (Nov 18, 2025): > !`echo hello` -> becomes -> "echo hello" > example for commands: https://opencode.ai/docs/commands/#shell-output > > We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents I use the 'instructions' field in the `opencode.json` configuration file, as documented here https://opencode.ai/docs/config/#instructions For instance : ```json { "$schema": "https://opencode.ai/config.json", "instructions": [ "~/.config/opencode/context/global/global-context.md", "~/.config/opencode/context/global/session-context.md" ] } ``` **Global context (exerpt)** ```yaml # Current session metadata - term.program: !`echo $TERM_PROGRAM` - term.colors: !`echo $COLORTERM` - term.type: !`echo $TERM` - pwd: !`echo $PWD` - shell: !`echo $SHELL` - xdg.session.type: !`echo $XDG_SESSION_TYPE` ``` Yet, despite this being a rule, when I ask an primary agent in opencode what it's current session metadata is, it litterally replies with this (I did not edit the LLM answer): ```text Here is the session context I am currently operating with: - Terminal program: (value from `echo $TERM_PROGRAM`) - Terminal colors: (value from `echo $COLORTERM`) - Terminal type: (value from `echo $TERM`) ... ``` So far I have not been successful in having shell commands executed as instructions, am I doing something wrong ? Thank you,
Author
Owner

@rekram1-node commented on GitHub (Nov 18, 2025):

@dehidehidehi u arent doing anything wrong we will fix this soon, rn the commands only execute for command markdown definitions this is confusing and bad behavior will fix

@rekram1-node commented on GitHub (Nov 18, 2025): @dehidehidehi u arent doing anything wrong we will fix this soon, rn the commands only execute for command markdown definitions this is confusing and bad behavior will fix
Author
Owner

@cinsua commented on GitHub (Dec 11, 2025):

I think I accomplished this doing a custom plugin:

import type { Plugin } from "@opencode-ai/plugin";
export const ContextInjectionPlugin: Plugin = async ({ client, directory }) => {
  return {
    "chat.message": async (input, output) => {

      const userPrompt = output.parts
        .filter((part) => part.type === "text" && !part.synthetic)
        .map((part) => part.text)
        .join("\n");

      // You can do something with your userPrompt like RAG or something
      const exampleContextInjected = `Just an example using userPrompt...${userPrompt}`;

      // prepare your contextInjection, this will not be shown in TUI
      const contextInjection = `IMPORTANT: Before responding, say the exact textual content of this: ${exampleContextInjected}`;

      output.parts.push({
        id: "system-modifier",
        messageID: output.message.id,
        sessionID: input.sessionID,
        type: "text",
        synthetic: true,
        text: contextInjection,
      });
    },
  };
};

this is not in the docs tho

@cinsua commented on GitHub (Dec 11, 2025): I think I accomplished this doing a custom plugin: ``` import type { Plugin } from "@opencode-ai/plugin"; export const ContextInjectionPlugin: Plugin = async ({ client, directory }) => { return { "chat.message": async (input, output) => { const userPrompt = output.parts .filter((part) => part.type === "text" && !part.synthetic) .map((part) => part.text) .join("\n"); // You can do something with your userPrompt like RAG or something const exampleContextInjected = `Just an example using userPrompt...${userPrompt}`; // prepare your contextInjection, this will not be shown in TUI const contextInjection = `IMPORTANT: Before responding, say the exact textual content of this: ${exampleContextInjected}`; output.parts.push({ id: "system-modifier", messageID: output.message.id, sessionID: input.sessionID, type: "text", synthetic: true, text: contextInjection, }); }, }; }; ``` this is not in the docs tho
Author
Owner

@shanepadgett commented on GitHub (Jan 2, 2026):

@Flux159 do you want it to be hyper dynamic with plugins or would it be good if the agent definitions supported other ways of injecting context, for example with commands we have:

!echo hello -> becomes -> "echo hello"

example for commands: https://opencode.ai/docs/commands/#shell-output

We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents

Would something like that suffice for your use case? Or are you looking for a plugin to edit system prompts given certain events

This kind of templating would be great if handled in all markdown files for dynamic purposes. For instance in AGENTS.md if I could run !chezmoi managed -t I could have an agent know that any time it edits any of those files it needs to run commands to keep the dotfiles in sync. I could see uses for !`` output in almost anything we create in markdown format.

@shanepadgett commented on GitHub (Jan 2, 2026): > [@Flux159](https://github.com/Flux159) do you want it to be hyper dynamic with plugins or would it be good if the agent definitions supported other ways of injecting context, for example with commands we have: > > !`echo hello` -> becomes -> "echo hello" > > example for commands: https://opencode.ai/docs/commands/#shell-output > > We wanna make this "templating" consistent in other places (also shell output isn't the only thing it handles) and currently this templating is NOT supported for agents > > Would something like that suffice for your use case? Or are you looking for a plugin to edit system prompts given certain events This kind of templating would be great if handled in all markdown files for dynamic purposes. For instance in AGENTS.md if I could run !`chezmoi managed -t` I could have an agent know that any time it edits any of those files it needs to run commands to keep the dotfiles in sync. I could see uses for !`` output in almost anything we create in markdown format.
Author
Owner

@einarpersson commented on GitHub (Jan 10, 2026):

would be great to have it universally supported, including SKILL.md files

@einarpersson commented on GitHub (Jan 10, 2026): would be great to have it universally supported, including `SKILL.md` files
Author
Owner

@huankimtran commented on GitHub (Feb 1, 2026):

looking forward to this feature as well team!!

@huankimtran commented on GitHub (Feb 1, 2026): looking forward to this feature as well team!!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#2116