Skip to content

Commit aead4ae

Browse files
code-yeongyujustsisyphussisyphus-dev-aiSisyphus
authored
Add tmux pane management for background agent sessions (#1094)
* feat(config): add TmuxConfigSchema for tmux subagent pane management Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * feat(shared): add tmux module structure * feat(shared/tmux): implement tmux pane utilities Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * test(tmux-subagent): add TmuxSessionManager tests (TDD RED) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * feat(tmux-subagent): implement TmuxSessionManager Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * feat(integration): wire TmuxSessionManager with 500ms delay - Task 5: Add 500ms delay in BackgroundManager after session creation - Task 6: Wire TmuxSessionManager event handlers (session.created/deleted) - Both changes integrate tmux pane management into plugin lifecycle Co-authored-by: Sisyphus <ultrawork@oh-my-opencode> --------- Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Sisyphus <ultrawork@oh-my-opencode>
1 parent bccc943 commit aead4ae

File tree

16 files changed

+893
-39
lines changed

16 files changed

+893
-39
lines changed

assets/oh-my-opencode.schema.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,32 @@
21862186
]
21872187
}
21882188
}
2189+
},
2190+
"tmux": {
2191+
"type": "object",
2192+
"properties": {
2193+
"enabled": {
2194+
"default": false,
2195+
"type": "boolean"
2196+
},
2197+
"layout": {
2198+
"default": "main-vertical",
2199+
"type": "string",
2200+
"enum": [
2201+
"main-horizontal",
2202+
"main-vertical",
2203+
"tiled",
2204+
"even-horizontal",
2205+
"even-vertical"
2206+
]
2207+
},
2208+
"main_pane_size": {
2209+
"default": 60,
2210+
"type": "number",
2211+
"minimum": 20,
2212+
"maximum": 80
2213+
}
2214+
}
21892215
}
21902216
}
21912217
}

bun.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export {
99
SisyphusAgentConfigSchema,
1010
ExperimentalConfigSchema,
1111
RalphLoopConfigSchema,
12+
TmuxConfigSchema,
13+
TmuxLayoutSchema,
1214
} from "./schema"
1315

1416
export type {
@@ -23,4 +25,6 @@ export type {
2325
ExperimentalConfig,
2426
DynamicContextPruningConfig,
2527
RalphLoopConfig,
28+
TmuxConfig,
29+
TmuxLayout,
2630
} from "./schema"

src/config/schema.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,19 @@ export const BrowserAutomationConfigSchema = z.object({
310310
provider: BrowserAutomationProviderSchema.default("playwright"),
311311
})
312312

313+
export const TmuxLayoutSchema = z.enum([
314+
'main-horizontal', // main pane top, agent panes bottom stack
315+
'main-vertical', // main pane left, agent panes right stack (default)
316+
'tiled', // all panes same size grid
317+
'even-horizontal', // all panes horizontal row
318+
'even-vertical', // all panes vertical stack
319+
])
320+
321+
export const TmuxConfigSchema = z.object({
322+
enabled: z.boolean().default(false), // default: false (disabled)
323+
layout: TmuxLayoutSchema.default('main-vertical'), // default: main-vertical
324+
main_pane_size: z.number().min(20).max(80).default(60), // percentage, default: 60%
325+
})
313326
export const OhMyOpenCodeConfigSchema = z.object({
314327
$schema: z.string().optional(),
315328
disabled_mcps: z.array(AnyMcpNameSchema).optional(),
@@ -330,6 +343,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
330343
notification: NotificationConfigSchema.optional(),
331344
git_master: GitMasterConfigSchema.optional(),
332345
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
346+
tmux: TmuxConfigSchema.optional(),
333347
})
334348

335349
export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>
@@ -354,5 +368,7 @@ export type BuiltinCategoryName = z.infer<typeof BuiltinCategoryNameSchema>
354368
export type GitMasterConfig = z.infer<typeof GitMasterConfigSchema>
355369
export type BrowserAutomationProvider = z.infer<typeof BrowserAutomationProviderSchema>
356370
export type BrowserAutomationConfig = z.infer<typeof BrowserAutomationConfigSchema>
371+
export type TmuxConfig = z.infer<typeof TmuxConfigSchema>
372+
export type TmuxLayout = z.infer<typeof TmuxLayoutSchema>
357373

358374
export { AnyMcpNameSchema, type AnyMcpName, McpNameSchema, type McpName } from "../mcp/types"

src/features/background-agent/manager.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import type {
77
} from "./types"
88
import { log, getAgentToolRestrictions } from "../../shared"
99
import { ConcurrencyManager } from "./concurrency"
10-
import type { BackgroundTaskConfig } from "../../config/schema"
10+
import type { BackgroundTaskConfig, TmuxConfig } from "../../config/schema"
11+
import { isInsideTmux } from "../../shared/tmux"
1112

1213
import { subagentSessions } from "../claude-code-session-state"
1314
import { getTaskToastManager } from "../task-toast-manager"
@@ -68,19 +69,24 @@ export class BackgroundManager {
6869
private concurrencyManager: ConcurrencyManager
6970
private shutdownTriggered = false
7071
private config?: BackgroundTaskConfig
71-
72+
private tmuxEnabled: boolean
7273

7374
private queuesByKey: Map<string, QueueItem[]> = new Map()
7475
private processingKeys: Set<string> = new Set()
7576

76-
constructor(ctx: PluginInput, config?: BackgroundTaskConfig) {
77+
constructor(
78+
ctx: PluginInput,
79+
config?: BackgroundTaskConfig,
80+
tmuxConfig?: TmuxConfig
81+
) {
7782
this.tasks = new Map()
7883
this.notifications = new Map()
7984
this.pendingByParent = new Map()
8085
this.client = ctx.client
8186
this.directory = ctx.directory
8287
this.concurrencyManager = new ConcurrencyManager(config)
8388
this.config = config
89+
this.tmuxEnabled = tmuxConfig?.enabled ?? false
8490
this.registerProcessCleanup()
8591
}
8692

@@ -222,6 +228,11 @@ export class BackgroundManager {
222228
const sessionID = createResult.data.id
223229
subagentSessions.add(sessionID)
224230

231+
// Wait for TmuxSessionManager to spawn pane via event hook
232+
if (this.tmuxEnabled && isInsideTmux()) {
233+
await new Promise(r => setTimeout(r, 500))
234+
}
235+
225236
// Update task to running state
226237
task.status = "running"
227238
task.startedAt = new Date()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./manager"
2+
export * from "./types"

0 commit comments

Comments
 (0)