Add tmux availability check for conditional interactive_bash tool registration

- Implement getTmuxPath() utility to detect tmux availability at plugin load time
- Add getCachedTmuxPath() for retrieving cached tmux path
- Add startBackgroundCheck() for asynchronous tmux detection
- Conditionally register interactive_bash tool only when tmux is available
- Silently skip registration without error messages if tmux not found
- Export utilities from tools/interactive-bash/index.ts

Tool now gracefully handles systems without tmux installed.

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2025-12-15 19:02:31 +09:00
parent 2d2834f8a7
commit 153fa844d4
5 changed files with 84 additions and 5 deletions

View File

@@ -44,7 +44,7 @@ import {
getCurrentSessionTitle,
} from "./features/claude-code-session-state";
import { updateTerminalTitle } from "./features/terminal";
import { builtinTools, createCallOmoAgent, createBackgroundTools, createLookAt } from "./tools";
import { builtinTools, createCallOmoAgent, createBackgroundTools, createLookAt, interactive_bash, getTmuxPath } from "./tools";
import { BackgroundManager } from "./features/background-agent";
import { createBuiltinMcps } from "./mcp";
import { OhMyOpenCodeConfigSchema, type OhMyOpenCodeConfig, type HookName } from "./config";
@@ -263,6 +263,8 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
? await createGoogleAntigravityAuthPlugin(ctx)
: null;
const tmuxAvailable = await getTmuxPath();
return {
...(googleAuthHooks ? { auth: googleAuthHooks.auth } : {}),
@@ -271,6 +273,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
...backgroundTools,
call_omo_agent: callOmoAgent,
look_at: lookAt,
...(tmuxAvailable ? { interactive_bash } : {}),
},
"chat.message": async (input, output) => {

View File

@@ -21,7 +21,9 @@ import { grep } from "./grep"
import { glob } from "./glob"
import { slashcommand } from "./slashcommand"
import { skill } from "./skill"
import { interactive_bash } from "./interactive-bash"
export { interactive_bash, startBackgroundCheck as startTmuxCheck } from "./interactive-bash"
export { getTmuxPath } from "./interactive-bash/utils"
import {
createBackgroundTask,
@@ -63,5 +65,4 @@ export const builtinTools = {
glob,
slashcommand,
skill,
interactive_bash,
}

View File

@@ -1,3 +1,4 @@
import { interactive_bash } from "./tools"
import { startBackgroundCheck } from "./utils"
export { interactive_bash }
export { interactive_bash, startBackgroundCheck }

View File

@@ -1,5 +1,6 @@
import { tool } from "@opencode-ai/plugin/tool"
import { DEFAULT_TIMEOUT_MS, INTERACTIVE_BASH_DESCRIPTION } from "./constants"
import { getCachedTmuxPath } from "./utils"
/**
* Quote-aware command tokenizer with escape handling
@@ -53,13 +54,15 @@ export const interactive_bash = tool({
},
execute: async (args) => {
try {
const tmuxPath = getCachedTmuxPath() ?? "tmux"
const parts = tokenizeCommand(args.tmux_command)
if (parts.length === 0) {
return "Error: Empty tmux command"
}
const proc = Bun.spawn(["tmux", ...parts], {
const proc = Bun.spawn([tmuxPath, ...parts], {
stdout: "pipe",
stderr: "pipe",
})

View File

@@ -0,0 +1,71 @@
import { spawn } from "bun"
let tmuxPath: string | null = null
let initPromise: Promise<string | null> | null = null
async function findTmuxPath(): Promise<string | null> {
const isWindows = process.platform === "win32"
const cmd = isWindows ? "where" : "which"
try {
const proc = spawn([cmd, "tmux"], {
stdout: "pipe",
stderr: "pipe",
})
const exitCode = await proc.exited
if (exitCode !== 0) {
return null
}
const stdout = await new Response(proc.stdout).text()
const path = stdout.trim().split("\n")[0]
if (!path) {
return null
}
const verifyProc = spawn([path, "-V"], {
stdout: "pipe",
stderr: "pipe",
})
const verifyExitCode = await verifyProc.exited
if (verifyExitCode !== 0) {
return null
}
return path
} catch {
return null
}
}
export async function getTmuxPath(): Promise<string | null> {
if (tmuxPath !== null) {
return tmuxPath
}
if (initPromise) {
return initPromise
}
initPromise = (async () => {
const path = await findTmuxPath()
tmuxPath = path
return path
})()
return initPromise
}
export function getCachedTmuxPath(): string | null {
return tmuxPath
}
export function startBackgroundCheck(): void {
if (!initPromise) {
initPromise = getTmuxPath()
initPromise.catch(() => {})
}
}