feat(plugin): add shell.env hook for manipulating environment in tools and shell (#12012)

This commit is contained in:
Tyler Gannon
2026-02-03 15:18:41 -06:00
committed by GitHub
parent 25bdd77b1d
commit a30696f9bf
5 changed files with 32 additions and 1 deletions

View File

@@ -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>

View File

@@ -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",
},
})

View File

@@ -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",

View File

@@ -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: {

View File

@@ -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: