feat(tools): add interactive_bash tool for tmux session management

Add a new tool for managing tmux sessions with automatic tracking and cleanup:

- interactive_bash tool: Accepts tmux commands via tmux_command parameter
- Session tracking hook: Tracks omo-* prefixed tmux sessions per OpenCode session
- System reminder: Appends active session list after create/delete operations
- Auto cleanup: Kills all tracked tmux sessions on OpenCode session deletion
- Output truncation: Registered in TRUNCATABLE_TOOLS for long capture-pane outputs

🤖 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 2524c90850
commit 5cbef252a3
13 changed files with 304 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ import { grep } from "./grep"
import { glob } from "./glob"
import { slashcommand } from "./slashcommand"
import { skill } from "./skill"
import { interactive_bash } from "./interactive-bash"
import {
createBackgroundTask,
@@ -62,4 +63,5 @@ export const builtinTools = {
glob,
slashcommand,
skill,
interactive_bash,
}

View File

@@ -0,0 +1,21 @@
export const DEFAULT_TIMEOUT_MS = 60_000
export const INTERACTIVE_BASH_DESCRIPTION = `Execute tmux commands for interactive terminal session management.
This tool provides access to tmux for creating and managing persistent terminal sessions.
Use it to run interactive CLI applications, maintain long-running processes, or work with multiple terminal sessions.
Parameters:
- tmux_command: The tmux command to execute (e.g., "new-session -d -s omo-dev", "send-keys -t omo-dev 'ls' Enter")
Examples:
- Create session: "new-session -d -s omo-test"
- Send keys: "send-keys -t omo-test 'npm run dev' Enter"
- Capture output: "capture-pane -t omo-test -p"
- List sessions: "list-sessions"
- Kill session: "kill-session -t omo-test"
Notes:
- Session names should follow the pattern "omo-{name}" for automatic tracking
- Use -d flag with new-session to create detached sessions
- Use capture-pane -p to retrieve terminal output`

View File

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

View File

@@ -0,0 +1,43 @@
import { tool } from "@opencode-ai/plugin/tool"
import { DEFAULT_TIMEOUT_MS, INTERACTIVE_BASH_DESCRIPTION } from "./constants"
export const interactive_bash = tool({
description: INTERACTIVE_BASH_DESCRIPTION,
args: {
tmux_command: tool.schema.string().describe("The tmux command to execute (without 'tmux' prefix)"),
},
execute: async (args) => {
try {
const parts = args.tmux_command.split(/\s+/).filter((p) => p.length > 0)
if (parts.length === 0) {
return "Error: Empty tmux command"
}
const proc = Bun.spawn(["tmux", ...parts], {
stdout: "pipe",
stderr: "pipe",
})
const timeoutPromise = new Promise<never>((_, reject) => {
const id = setTimeout(() => {
proc.kill()
reject(new Error(`Timeout after ${DEFAULT_TIMEOUT_MS}ms`))
}, DEFAULT_TIMEOUT_MS)
proc.exited.then(() => clearTimeout(id))
})
const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise])
const stderr = await new Response(proc.stderr).text()
const exitCode = await proc.exited
if (exitCode !== 0 && stderr.trim()) {
return `Error: ${stderr.trim()}`
}
return stdout || "(no output)"
} catch (e) {
return `Error: ${e instanceof Error ? e.message : String(e)}`
}
},
})

View File

@@ -0,0 +1,3 @@
export interface InteractiveBashArgs {
tmux_command: string
}