Repository navigation
fix(tui): bidirectional Tab, Ctrl+Q detaches, remove broken Ctrl+T #925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,39 +110,6 @@ function isTmuxServerRunning(socket: string, conf: string): boolean { | |
| } | ||
| } | ||
|
|
||
| /** Start the genie agent tmux server and create sessions for all agents */ | ||
| function startAgentTmuxServer(agents: string[]): void { | ||
| const conf = genieTmuxConf(); | ||
|
|
||
| // Start server with a bootstrap session (tmux needs at least one) | ||
| if (!isTmuxServerRunning(GENIE_SOCKET, conf)) { | ||
| execSync(genieTmux('new-session -d -s _bootstrap'), { stdio: 'ignore' }); | ||
| } | ||
|
|
||
| // Create a session per agent | ||
| for (const agent of agents) { | ||
| try { | ||
| execSync(genieTmux(`has-session -t '${agent}'`), { stdio: 'ignore' }); | ||
| } catch { | ||
| // Session doesn't exist — create it | ||
| try { | ||
| execSync(genieTmux(`new-session -d -s '${agent}'`), { stdio: 'ignore' }); | ||
| } catch { | ||
| // race or naming conflict | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Kill the bootstrap session if we have real agent sessions | ||
| if (agents.length > 0) { | ||
| try { | ||
| execSync(genieTmux('kill-session -t _bootstrap'), { stdio: 'ignore' }); | ||
| } catch { | ||
| // already gone or was the only session | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const NAV_WIDTH = 30; | ||
| const KEY_TABLE = 'genie-tui'; | ||
|
|
||
|
|
@@ -170,32 +137,22 @@ function applyTuiStyle(): void { | |
|
|
||
| /** Set up keybindings in a dedicated key table for TUI */ | ||
| function setupTuiKeybindings(): void { | ||
| try { | ||
| execSync( | ||
| tuiTmux(`bind-key -T ${KEY_TABLE} Tab select-pane -t ${TUI_SESSION}:0.1 \\; switch-client -T ${KEY_TABLE}`), | ||
| { stdio: 'ignore' }, | ||
| ); | ||
| execSync( | ||
| tuiTmux( | ||
| `bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux -L ${TUI_SOCKET} display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`, | ||
| ), | ||
| { stdio: 'ignore' }, | ||
| ); | ||
| execSync( | ||
| tuiTmux(`bind-key -T ${KEY_TABLE} C-t send-keys -t ${TUI_SESSION}:0.1 C-b c \\; switch-client -T ${KEY_TABLE}`), | ||
| { stdio: 'ignore' }, | ||
| ); | ||
| execSync(tuiTmux(`bind-key -T ${KEY_TABLE} C-q run-shell "tmux -L ${TUI_SOCKET} kill-session -t ${TUI_SESSION}"`), { | ||
| stdio: 'ignore', | ||
| }); | ||
| execSync( | ||
| tuiTmux(`bind-key -T ${KEY_TABLE} 'C-\\\\' run-shell "tmux -L ${TUI_SOCKET} kill-session -t ${TUI_SESSION}"`), | ||
| { stdio: 'ignore' }, | ||
| ); | ||
| execSync(tuiTmux(`set-hook -t ${TUI_SESSION} client-session-changed "switch-client -T ${KEY_TABLE}"`), { | ||
| stdio: 'ignore', | ||
| }); | ||
| } catch {} | ||
| const bindings = [ | ||
| // Tab: toggle focus between left nav (pane 0) and right terminal (pane 1) | ||
| `bind-key -T ${KEY_TABLE} Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${TUI_SESSION}:0.1" "select-pane -t ${TUI_SESSION}:0.0" \\; switch-client -T ${KEY_TABLE}`, | ||
| // Ctrl+B: toggle sidebar width (collapse/expand) | ||
| `bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux -L ${TUI_SOCKET} display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`, | ||
| // Ctrl+Q: detach from TUI (don't kill — serve owns the session) | ||
| `bind-key -T ${KEY_TABLE} C-q detach-client`, | ||
| `bind-key -T ${KEY_TABLE} 'C-\\\\' detach-client`, | ||
| // Activate key table on session attach | ||
| `set-hook -t ${TUI_SESSION} client-session-changed "switch-client -T ${KEY_TABLE}"`, | ||
| ]; | ||
| for (const cmd of bindings) { | ||
| try { | ||
| execSync(tuiTmux(cmd), { stdio: 'ignore' }); | ||
| } catch {} | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -343,18 +300,6 @@ export function ensureTuiSession(workspaceRoot?: string): void { | |
| // Workspace agent scanning | ||
| // ============================================================================ | ||
|
|
||
| /** Scan for agents from workspace.json and filesystem */ | ||
| function discoverAgents(): string[] { | ||
| try { | ||
| const { findWorkspace, scanAgents } = require('../lib/workspace.js') as typeof import('../lib/workspace.js'); | ||
| const ws = findWorkspace(); | ||
| if (!ws) return []; | ||
| return scanAgents(ws.root); | ||
| } catch { | ||
| return []; | ||
| } | ||
| } | ||
|
|
||
| // ============================================================================ | ||
| // Service management | ||
| // ============================================================================ | ||
|
|
@@ -421,12 +366,14 @@ async function startForeground(): Promise<void> { | |
| console.error(` pgserve failed: ${msg}`); | ||
| } | ||
|
|
||
| // 2. Start genie agent tmux server + agent sessions | ||
| const agents = discoverAgents(); | ||
| console.log(` Starting tmux -L ${GENIE_SOCKET} server...`); | ||
| startAgentTmuxServer(agents); | ||
| // 2. Report agent tmux server state (don't create empty sessions — | ||
| // sessions are created on-demand by `genie spawn`). | ||
| const sessions = listAgentSessions(); | ||
|
Comment on lines
+369
to
371
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change stops initializing Useful? React with 👍 / 👎. |
||
| console.log(` Agent sessions: ${sessions.length > 0 ? sessions.join(', ') : '(none)'}`); | ||
| if (sessions.length > 0) { | ||
| console.log(` Agent server (-L ${GENIE_SOCKET}): ${sessions.length} sessions`); | ||
| } else { | ||
| console.log(` Agent server (-L ${GENIE_SOCKET}): no sessions yet (created on first spawn)`); | ||
| } | ||
|
|
||
| // 2b. Sync agent directory + start watcher | ||
| handles.agentWatcher = await startAgentSync(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silently catching and ignoring errors can make debugging difficult if a
tmuxcommand fails unexpectedly. Consider logging the error at a debug level or adding a comment explaining why the error is intentionally ignored here.