summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTyler Gannon <[email protected]>2026-02-03 15:18:41 -0600
committerGitHub <[email protected]>2026-02-03 15:18:41 -0600
commita30696f9bfefc58d640316c6a864c9bb255de690 (patch)
tree3c2579e57db3e5a7f235b9b20296a4a3bc01bb2e
parent25bdd77b1d765a9cac5d02ede93c075f25d6ca6f (diff)
downloadopencode-a30696f9bfefc58d640316c6a864c9bb255de690.tar.gz
opencode-a30696f9bfefc58d640316c6a864c9bb255de690.zip
feat(plugin): add shell.env hook for manipulating environment in tools and shell (#12012)
-rw-r--r--packages/opencode/src/pty/index.ts3
-rw-r--r--packages/opencode/src/session/prompt.ts5
-rw-r--r--packages/opencode/src/tool/bash.ts3
-rw-r--r--packages/plugin/src/index.ts1
-rw-r--r--packages/web/src/content/docs/plugins.mdx21
5 files changed, 32 insertions, 1 deletions
diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts
index d01b2b02e..a27ee9a74 100644
--- a/packages/opencode/src/pty/index.ts
+++ b/packages/opencode/src/pty/index.ts
@@ -8,6 +8,7 @@ import type { WSContext } from "hono/ws"
import { Instance } from "../project/instance"
import { lazy } from "@opencode-ai/util/lazy"
import { Shell } from "@/shell/shell"
+import { Plugin } from "@/plugin"
export namespace Pty {
const log = Log.create({ service: "pty" })
@@ -102,9 +103,11 @@ export namespace Pty {
}
const cwd = input.cwd || Instance.directory
+ const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} })
const env = {
...process.env,
...input.env,
+ ...shellEnv.env,
TERM: "xterm-256color",
OPENCODE_TERMINAL: "1",
} as Record<string, string>
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index b85a7c336..ab1eba5be 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -1500,12 +1500,15 @@ NOTE: At any point in time through this workflow you should feel free to ask the
const matchingInvocation = invocations[shellName] ?? invocations[""]
const args = matchingInvocation?.args
+ const cwd = Instance.directory
+ const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} })
const proc = spawn(shell, args, {
- cwd: Instance.directory,
+ cwd,
detached: process.platform !== "win32",
stdio: ["ignore", "pipe", "pipe"],
env: {
...process.env,
+ ...shellEnv.env,
TERM: "dumb",
},
})
diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts
index ff208ff3f..67559b78c 100644
--- a/packages/opencode/src/tool/bash.ts
+++ b/packages/opencode/src/tool/bash.ts
@@ -16,6 +16,7 @@ import { Shell } from "@/shell/shell"
import { BashArity } from "@/permission/arity"
import { Truncate } from "./truncation"
+import { Plugin } from "@/plugin"
const MAX_METADATA_LENGTH = 30_000
const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2 * 60 * 1000
@@ -162,11 +163,13 @@ export const BashTool = Tool.define("bash", async () => {
})
}
+ const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} })
const proc = spawn(params.command, {
shell,
cwd,
env: {
...process.env,
+ ...shellEnv.env,
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts
index 86e7ae934..4cc84a5f3 100644
--- a/packages/plugin/src/index.ts
+++ b/packages/plugin/src/index.ts
@@ -185,6 +185,7 @@ export interface Hooks {
input: { tool: string; sessionID: string; callID: string },
output: { args: any },
) => Promise<void>
+ "shell.env"?: (input: { cwd: string }, output: { env: Record<string, string> }) => Promise<void>
"tool.execute.after"?: (
input: { tool: string; sessionID: string; callID: string },
output: {
diff --git a/packages/web/src/content/docs/plugins.mdx b/packages/web/src/content/docs/plugins.mdx
index ba530a6d9..394fecc40 100644
--- a/packages/web/src/content/docs/plugins.mdx
+++ b/packages/web/src/content/docs/plugins.mdx
@@ -192,6 +192,10 @@ Plugins can subscribe to events as seen below in the Examples section. Here is a
- `todo.updated`
+#### Shell Events
+
+- `shell.env`
+
#### Tool Events
- `tool.execute.after`
@@ -254,6 +258,23 @@ export const EnvProtection = async ({ project, client, $, directory, worktree })
---
+### Inject environment variables
+
+Inject environment variables into all shell execution (AI tools and user terminals):
+
+```javascript title=".opencode/plugins/inject-env.js"
+export const InjectEnvPlugin = async () => {
+ return {
+ "shell.env": async (input, output) => {
+ output.env.MY_API_KEY = "secret"
+ output.env.PROJECT_ROOT = input.cwd
+ },
+ }
+}
+```
+
+---
+
### Custom tools
Plugins can also add custom tools to opencode: