feat: OpenCode integration Phases 1-3 — types, CLI resolver, tmux spawn

Phase 1: Type system
- Add SessionMode = 'claude' | 'shell' | 'opencode' to types.ts (single source of truth)
- Add OpenCodeConfig interface (model, autoAllowTools, continueSession, etc.)
- Add openCodeConfig to SessionState
- Import SessionMode in session.ts, mux-interface.ts (remove duplicates)
- Refactor createSession/respawnPane to options objects (CreateSessionOptions, RespawnPaneOptions)

Phase 2: CLI resolver
- New opencode-cli-resolver.ts mirroring claude-cli-resolver.ts
- Searches ~/.opencode/bin, ~/.local/bin, /usr/local/bin, ~/go/bin, etc.
- Exported via utils/index.ts

Phase 3: Tmux spawn
- Extract buildSpawnCommand() shared helper (eliminates duplication)
- Add buildOpenCodeCommand() for opencode CLI flags
- Add setOpenCodeEnvVars() — API keys via tmux setenv (not visible in ps)
- Add setOpenCodeConfigContent() — JSON config via tmux setenv (prevents shell injection)
- Gate _processExpensiveParsers() for opencode mode (skip Claude-specific parsers)
- OpenCode-specific TUI ready detection (timeout-based, no ❯ prompt scanning)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-26 10:07:43 +01:00
co-authored by Claude Opus 4.6
parent 1adcb7656b
commit 0b60f2691f
6 changed files with 386 additions and 93 deletions
+31 -15
View File
@@ -7,7 +7,7 @@
*/
import type { EventEmitter } from 'node:events';
import type { ProcessStats, PersistedRespawnConfig, NiceConfig, ClaudeMode } from './types.js';
import type { ProcessStats, PersistedRespawnConfig, NiceConfig, ClaudeMode, SessionMode, OpenCodeConfig } from './types.js';
/**
* Multiplexer session metadata.
@@ -23,8 +23,8 @@ export interface MuxSession {
createdAt: number;
/** Working directory */
workingDir: string;
/** Session mode: claude or shell */
mode: 'claude' | 'shell';
/** Session mode */
mode: SessionMode;
/** Whether webserver is attached to this session */
attached: boolean;
/** Session display name (tab name) */
@@ -43,6 +43,31 @@ export interface MuxSessionWithStats extends MuxSession {
stats?: ProcessStats;
}
/** Options for creating a new multiplexer session. */
export interface CreateSessionOptions {
sessionId: string;
workingDir: string;
mode: SessionMode;
name?: string;
niceConfig?: NiceConfig;
model?: string;
claudeMode?: ClaudeMode;
allowedTools?: string;
openCodeConfig?: OpenCodeConfig;
}
/** Options for respawning a dead pane. */
export interface RespawnPaneOptions {
sessionId: string;
workingDir: string;
mode: SessionMode;
niceConfig?: NiceConfig;
model?: string;
claudeMode?: ClaudeMode;
allowedTools?: string;
openCodeConfig?: OpenCodeConfig;
}
/**
* Terminal multiplexer interface.
*
@@ -62,18 +87,9 @@ export interface TerminalMultiplexer extends EventEmitter {
/**
* Create a new multiplexer session.
* The session runs the appropriate command (claude or shell) in detached mode.
* The session runs the appropriate command (claude, opencode, or shell) in detached mode.
*/
createSession(
sessionId: string,
workingDir: string,
mode: 'claude' | 'shell',
name?: string,
niceConfig?: NiceConfig,
model?: string,
claudeMode?: ClaudeMode,
allowedTools?: string,
): Promise<MuxSession>;
createSession(options: CreateSessionOptions): Promise<MuxSession>;
/**
* Kill a session and all its child processes.
@@ -165,5 +181,5 @@ export interface TerminalMultiplexer extends EventEmitter {
isPaneDead(muxName: string): boolean;
/** Respawn a dead pane with a fresh command. Returns the new PID or null on failure. */
respawnPane(sessionId: string, workingDir: string, mode: 'claude' | 'shell', niceConfig?: NiceConfig, model?: string, claudeMode?: ClaudeMode, allowedTools?: string): Promise<number | null>;
respawnPane(options: RespawnPaneOptions): Promise<number | null>;
}