[WIP] Symphony: Improved Terminal Experience (#160)#486
[WIP] Symphony: Improved Terminal Experience (#160)#486pedramamini wants to merge 215 commits intomainfrom
Conversation
…r, and Encore feature flag - Register maestroCue as an Encore Feature flag (EncoreFeatureFlags, DEFAULT_ENCORE_FEATURES) - Create src/main/cue/cue-types.ts with CueEventType, CueSubscription, CueSettings, CueConfig, CueEvent, CueRunStatus, CueRunResult, CueSessionStatus, and related constants - Add 'CUE' to HistoryEntryType across shared types, global.d.ts, preload, IPC handlers, and hooks - Add cueTriggerName, cueEventType, cueSourceSession optional fields to HistoryEntry - Add 'cue' log level to MainLogLevel, LOG_LEVEL_PRIORITY, logger switch/case, and LogViewer with teal color (#06b6d4), always-enabled filter, and agent name pill - Add 10 Cue-specific template variables (CUE_EVENT_TYPE, CUE_TRIGGER_NAME, etc.) with cueOnly flag - Extend TemplateContext with cue? field and substituteTemplateVariables with Cue replacements - Update TEMPLATE_VARIABLES_GENERAL filter to exclude cueOnly variables
…ovider Implements the three core modules for the Cue event-driven automation engine: - cue-yaml-loader.ts: Discovers and parses maestro-cue.yaml files with js-yaml, validates config structure, watches for file changes via chokidar with 1-second debounce - cue-file-watcher.ts: Wraps chokidar for file.changed subscriptions with per-file debouncing (5s default), constructs CueEvent instances with full file metadata payloads - cue-engine.ts: Main coordinator class with dependency injection, manages time.interval timers (fires immediately then on interval), file watchers, agent.completed listeners with fan-in tracking, activity log ring buffer (max 500), and run lifecycle management Added js-yaml and @types/js-yaml dependencies. 57 tests across 3 test files.
…story recording Implements the Cue executor module that spawns background agent processes when Cue triggers fire, following the same spawn pattern as Auto Run's process:spawn IPC handler. Key exports: - executeCuePrompt(): Full 10-step pipeline (prompt resolution, template substitution, agent arg building, SSH wrapping, process spawn with stdout/stderr capture, timeout enforcement with SIGTERM→SIGKILL) - stopCueRun(): Graceful process termination by runId - recordCueHistoryEntry(): Constructs HistoryEntry with type 'CUE' and all Cue-specific fields (trigger name, event type, source session) - getActiveProcesses(): Monitor running Cue processes Test coverage: 31 tests in cue-executor.test.ts covering execution paths, SSH remote, timeout escalation, history entry construction, and edge cases. Full suite: 21,635 tests passing across 512 files, zero regressions.
…zation for Maestro Cue
Add CUE entry support across all History components: - HistoryFilterToggle: CUE filter button with teal (#06b6d4) color and Zap icon - HistoryEntryItem: CUE pill, success/failure badges, and trigger metadata subtitle - HistoryPanel & UnifiedHistoryTab: CUE included in default activeFilters - HistoryDetailModal: CUE pill color, icon, success/failure indicator, trigger metadata display - Comprehensive test coverage for all CUE rendering paths (205 new/updated tests pass)
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a persistent PTY-backed Terminal Tab System: new xterm.js-based components (XTerminal, TerminalView), terminal-tab types/helpers/store/actions/hooks, IPC/preload and ProcessManager spawn support, unified-tab integration and migration, ANSI theming, many UI pieces (TabBar/MainPanel/Modals), and extensive test updates and deps. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant App as App (renderer)
participant TabBar as TabBar/MainPanel
participant TerminalView as TerminalView/XTerminal
participant Preload as Preload (renderer->main)
participant Main as Main IPC Handler
participant PTY as PTY Process
User->>App: Ctrl+Shift+` (create terminal tab)
App->>TabBar: useTerminalTabHandlers.createTerminalTab()
TabBar->>App: update session (terminalTabs + unified order)
User->>TabBar: select terminal tab
App->>TerminalView: mount active terminal
TerminalView->>Preload: ipc.invoke('process:spawnTerminalTab', config)
Preload->>Main: forward IPC
Main->>PTY: spawn PTY (cwd, shell, cols, rows, env)
PTY-->>Main: spawn result { pid, success }
Main-->>Preload: result
Preload-->>TerminalView: result
User->>TerminalView: types input
TerminalView->>Preload: write input -> PTY
PTY-->>TerminalView: stdout/stderr data
TerminalView->>App: render output via XTerminal
sequenceDiagram
participant Store as Session Store
participant Persist as Debounced Persistence
participant Disk as Persistence Layer
participant Restore as Session Restoration
Store->>Persist: session change (includes terminalTabs)
Persist->>Persist: cleanTerminalTabs (pid=0,state='idle',exitCode=undef)
Persist->>Disk: write cleaned session
Restore->>Disk: read persisted session
Restore->>Store: restore session
Restore->>Store: ensure terminalTabs non-empty (create default if missing)
Store->>UI: session available with terminal metadata
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
Adds full xterm.js terminal emulator foundation: - Install @xterm/xterm, addon-fit, addon-webgl, addon-web-links, addon-search, addon-unicode11 - Create XTerminal.tsx: forwardRef component with XTerminalHandle interface - WebGL renderer with canvas fallback on context loss - FitAddon with debounced ResizeObserver (100ms) - SearchAddon, WebLinksAddon, Unicode11Addon - IPC integration via window.maestro.process.onData/write/resize - mapThemeToXterm() helper for Maestro Theme → xterm.js ITheme - Dark/light ANSI color defaults (Phase 11 will add proper theme fields) - scrollback: 10000 lines
Adds TerminalTab interface and extends the unified tab system to support
terminal tabs as a third tab type alongside AI and file tabs.
Changes:
- src/renderer/types/index.ts:
- Add TerminalTab interface (id, name, shellType, pid, cwd, createdAt,
state, exitCode, scrollTop, searchQuery)
- Extend UnifiedTabRef type to 'ai' | 'file' | 'terminal'
- Extend UnifiedTab discriminated union with terminal variant
- Extend ClosedTabEntry with terminal variant
- Add terminalTabs: TerminalTab[] and activeTerminalTabId: string | null
to Session interface
- Add DEPRECATED comments to shellLogs and terminalPid
- src/renderer/utils/tabHelpers.ts:
- Update buildUnifiedTabs to handle terminal refs separately
- Update getRepairedUnifiedTabOrder to track terminal IDs in their own set
- Update ensureInUnifiedTabOrder type parameter to include 'terminal'
- Update NavigateToUnifiedTabResult.type to include 'terminal'
- Update ReopenUnifiedClosedTabResult.tabType to include 'terminal'
- Update navigateToUnifiedTabByIndex with explicit 'file' branch and
terminal placeholder (returns null until Phase 3)
- Update getCurrentUnifiedTabIndex to check activeTerminalTabId first
- Update showUnreadOnly loops to treat terminal tabs like file tabs
- Update restoreClosedUnifiedTab with explicit 'file' branch and
terminal placeholder (returns null until Phase 3)
- Add terminalTabs/activeTerminalTabId to session creation in createSessionFromHistoryEntry
- src/renderer/components/TabBar.tsx:
- Change file tab else branch to explicit else-if (type === 'file')
- Add terminal else branch returning null (implemented in Phase 8)
- Session creation sites (add terminalTabs: [], activeTerminalTabId: null):
- src/renderer/hooks/session/useSessionCrud.ts
- src/renderer/hooks/symphony/useSymphonyContribution.ts
- src/renderer/hooks/wizard/useWizardHandlers.ts
- src/renderer/utils/worktreeSession.ts
No runtime behavior changes — purely type additions and structural
preparation for the terminal tab system.
Creates src/renderer/utils/terminalTabHelpers.ts with 14 pure functions for terminal tab CRUD operations, following the established tabHelpers.ts pattern. Functions: createTerminalTab, getActiveTerminalTab, getTerminalTabDisplayName, getTerminalSessionId, parseTerminalSessionId, addTerminalTab, closeTerminalTab, selectTerminalTab, renameTerminalTab, reorderTerminalTabs, updateTerminalTabState, updateTerminalTabPid, updateTerminalTabCwd, hasRunningTerminalProcess. All functions are pure (take Session, return new Session). closeTerminalTab adds to unifiedClosedTabHistory for Cmd+Shift+T undo support and refuses to close the last terminal tab. selectTerminalTab nulls activeFileTabId to enforce single active non-AI tab invariant.
… for terminal tabs - Initialize sessions with a default terminal tab (using user's defaultShell setting) in all 5 session creation sites: useSessionCrud, useWizardHandlers, useSymphonyContribution, worktreeSession, and tabHelpers buildMergeSession - Add initial terminal tab to unifiedTabOrder at creation time - Add migration in restoreSession: sessions without terminalTabs get a default tab created and added to unifiedTabOrder; activeTerminalTabId undefined defaults to null - Reset terminal tab runtime state (pid, state, exitCode) on restore so dead PTYs aren't referenced after restart - Strip terminal tab runtime state in prepareSessionForPersistence to keep sessions.json clean across restarts
…nd activity log Add the Maestro Cue dashboard modal with full Encore Feature gating: - CueModal component with sessions table, active runs list, and activity log - useCue hook for state management, event subscriptions, and 10s polling - Settings toggle in Encore tab, command palette entry, keyboard shortcut (Cmd+Shift+U) - SessionList hamburger menu entry, modal store integration, lazy loading - 30 tests covering hook behavior and modal rendering
- PtySpawner: bypass stripControlSequences for session IDs containing
-terminal- (xterm.js handles its own rendering; filtering breaks it)
- ProcessConfig: add optional cols/rows fields for PTY dimensions
- PtySpawner: use config.cols/rows in pty.spawn() (defaults 100/30)
- ProcessManager: add spawnTerminalTab() convenience wrapper
- IPC handler: process:spawnTerminalTab with SSH remote support
- Preload API: expose spawnTerminalTab on window.maestro.process
- global.d.ts: add TypeScript type for spawnTerminalTab
- Verified exit listener correctly handles {id}-terminal-{tabId} format
- Create TerminalView.tsx as a memo(forwardRef) component with TerminalViewHandle exposing clearActiveTerminal, focusActiveTerminal, searchActiveTerminal, searchNext, searchPrevious via useImperativeHandle - Maintain Map<string, XTerminalHandle> for per-tab terminal instances - Lazy PTY spawn via useEffect when active tab has pid === 0 - Subscribe to process.onExit() to update tab state when PTY exits - Render all terminal tabs in absolute-positioned stack; only active tab visible - Show exit overlay with exit code when terminal process ends - Export createTabStateChangeHandler/createTabPidChangeHandler factory functions to wire session store updates from MainPanel - Update MainPanel.tsx to render TerminalView when inputMode === 'terminal' instead of TerminalOutput; hide InputArea in terminal mode (xterm.js handles input) - Subscribe to defaultShell and fontSize from settingsStore - Create terminalViewRef for future imperative calls (search, clear, focus)
- Add createTerminalTab, closeTerminalTab, selectTerminalTab, renameTerminalTab actions to tabStore.ts with PTY kill on close - Add selectActiveTerminalTab and selectTerminalTabs selectors to tabStore.ts - Implement terminal tab restore in reopenUnifiedClosedTab (creates fresh PTY) - Add useTerminalTabHandlers hook to useTabHandlers.ts for component integration - Update useInputMode toggleInputMode to activate first terminal tab on switch
Add CueYamlEditor component for creating and editing maestro-cue.yaml files. Features split-view layout with AI assist (left panel for description + clipboard copy) and YAML editor (right panel with line numbers, debounced validation, Tab indentation). Integrates into CueModal via Edit YAML button on each session row.
…yaml Task 1: CueHelpModal component with 7 content sections (What is Maestro Cue, Getting Started, Event Types, Template Variables, Multi-Agent Orchestration, Timeouts & Failure Handling, AI YAML Editor). Wired to CueModal ? button. Registered with layer stack at MODAL_PRIORITIES.CUE_HELP (465). Task 2: useCueAutoDiscovery hook that calls cue:refreshSession when sessions are created/restored/removed, gated by encoreFeatures.maestroCue. Full scan on feature enable, engine disable on feature off. Tests: 38 CueHelpModal tests + 10 useCueAutoDiscovery tests, all passing. Lint clean. No existing test regressions (21,778 tests pass).
- Add TerminalTabItem component to TabBar with Terminal icon, state colors, exit code badge, hover overlay (Rename/Close/Close Others/Close Left/Close Right), and drag-and-drop support - Add terminal tab props to TabBarProps and wire through MainPanel - Show TabBar in both AI and terminal modes (not just AI mode) - Add handleRequestTerminalTabRename wrapper in App.tsx to open rename modal (1-arg interface) - Extend handleRenameTab in useSessionLifecycle to delegate to renameTerminalTabHelper for terminal tabs - Export useTerminalTabHandlers and TerminalTabHandlersReturn from hooks/tabs/index.ts - Add terminal tab handler deps to useMainPanelProps
- Add TERMINAL_SHORTCUTS export to shortcuts.ts with newTerminalTab (Ctrl+Shift+`) and clearTerminal (Cmd+K) - Extend MainPanelHandle with clearActiveTerminal(), focusActiveTerminal(), openTerminalSearch() methods - Add terminalSearchOpen state in MainPanel; wire searchOpen/onSearchClose props to TerminalView - Add handleOpenTerminalTab and mainPanelRef to keyboardHandlerRef context in App.tsx - Guard Ctrl+[A-Z] sequences in terminal mode so xterm.js receives them (Ctrl+C, Ctrl+D, etc.) - Route Cmd+W/Cmd+Shift+[]/Cmd+1-9/Cmd+0 to terminal tab navigation when inputMode === 'terminal' - Override Cmd+K to clear terminal (instead of Quick Actions) when in terminal mode - Add Cmd+F handler to open terminal search overlay when in terminal mode - Add Ctrl+Shift+` handler to create new terminal tab from any mode (switches to terminal mode if needed) - Focus active terminal after Cmd+J mode toggle to terminal mode
- Add TerminalSearchBar component (absolute top-right overlay) with incremental search, previous/next navigation, "No results" indicator, LayerStack registration for Escape handling, and auto-focus on open - Wire TerminalSearchBar into TerminalView: close on tab switch, return focus to terminal on close, delegate search calls to XTerminalHandle - Add useEffect in MainPanel to reset terminalSearchOpen when inputMode leaves 'terminal' mode
- Extend ThemeColors interface with 17 optional ANSI fields (16-color palette + selection) for terminal emulation, maintaining backward compatibility with custom themes - Add curated ANSI palettes to all 16 themes using official palettes where available (Dracula, Nord, Catppuccin, Gruvbox, Solarized, etc.) - Update mapThemeToXterm() in XTerminal.tsx to use per-theme ANSI colors with mode-appropriate defaults (dark/light) as fallback - Update TerminalOutput.tsx ansiConverter to use all 16 ANSI theme colors (expanded from 8 to 16 entries) with theme fallbacks - Fix cssCustomProperties.ts and CustomThemeBuilder.tsx to handle the new optional ANSI fields without type errors
… session bridging Implement agent completion event chaining in the Cue engine: - Fan-out: subscriptions dispatch prompts to multiple target sessions simultaneously - Fan-in: subscriptions wait for all source sessions to complete before firing, with timeout handling (break clears tracker, continue fires with partial data) - Session bridging: user session completions trigger Cue subscriptions via exit listener - Add AgentCompletionData type for rich completion event payloads - Add hasCompletionSubscribers() optimization to skip unneeded notifications - Wire getCueEngine/isCueEnabled into ProcessListenerDependencies
Create TerminalTabRenameModal component and wire it into AppSessionModals.
The modal is shown when double-clicking a terminal tab or using the hover
overlay rename action. It displays the terminal-specific title, helper text
('Leave empty to use the default name'), and pre-fills with the current
custom name. Saving an empty string restores the auto-generated 'Terminal N'
display. Reuses the existing renameTab modal store state and handleRenameTab
lifecycle hook, which already handles terminal tab vs AI tab dispatch.
…ure gated) Add teal Zap icon next to session names in the Left Bar for sessions with active Maestro Cue subscriptions. The indicator is gated behind the maestroCue Encore Feature flag and shows a tooltip with the subscription count on hover. - Add cueSubscriptionCount prop to SessionItem with Zap icon rendering - Add lightweight Cue status fetching in SessionListInner via cue:getStatus IPC, refreshed on cue:activityUpdate events - Add cue namespace to global test setup mock - 6 unit tests + 3 integration tests; all 21,815 tests pass; lint clean
Add Maestro Cue entries across all developer documentation: - CLAUDE.md: Key Files table (4 entries), Architecture tree (cue/ dir), Standardized Vernacular (Cue + Cue Modal terms) - CLAUDE-PATTERNS.md: Encore Feature section lists maestroCue as second reference implementation alongside directorNotes - CLAUDE-IPC.md: cue namespace in Automation section, full Cue API reference with all endpoints and event documentation
- TerminalOutput.tsx: remove all inputMode==='terminal' branches; terminal display is now exclusively handled by TerminalView/XTerminal. Remove unused ElapsedTimeDisplay component definition. - TerminalView.tsx: add shared spawnPtyForTab useCallback (used for both initial spawn and Retry); add spawn-failure overlay (AlertCircle + Retry button); write ANSI shell-exit message to xterm buffer on state transition; write dim "Starting terminal..." loading indicator on mount; remove stale eslint-disable-next-line directives. - process.ts: add TODO comment and runtime deprecation warning to process:runCommand (callers should migrate to process:spawnTerminalTab). - useBatchedSessionUpdates.ts: guard shellLogs append behind !(session.terminalTabs?.length) legacy fallback with TODO comment. - TabBar.tsx: add shell-type/cwd tooltip to terminal tab items; widen name truncation limit from 120px to 150px. - New test: terminalTabHelpers.test.ts — 34 unit tests covering all pure helper functions (createTerminalTab, getTerminalTabDisplayName, getTerminalSessionId/parseTerminalSessionId, addTerminalTab, closeTerminalTab, selectTerminalTab, updateTerminalTabState, updateTerminalTabPid, renameTerminalTab, reorderTerminalTabs). - Test fixes: add terminalTabs/activeTerminalTabId to createMockSession in agentStore.test.ts and tabHelpers.test.ts; add process:spawnTerminalTab to process.test.ts expected channels list. - CLAUDE.md: add terminal feature rows to Key Files table.
…t journal - Add cue-db.ts: SQLite-backed event journal (cue_events table) and single-row heartbeat table (cue_heartbeat) using better-sqlite3 with WAL mode - Add cue-reconciler.ts: time event catch-up logic that fires exactly one reconciliation event per missed subscription (no flooding), with payload.reconciled and payload.missedCount metadata - Update cue-engine.ts: heartbeat writer (30s interval), sleep detection (2-minute gap threshold), database pruning (7 days), and clean shutdown - Update CueHelpModal: new "Sleep & Recovery" section with Moon icon - Update CueModal: amber "catch-up" badge on reconciled activity log entries - Tests: 41 new tests across cue-db (17), cue-reconciler (11), cue-sleep-wake (13)
…eline editor Add collapsible TriggerDrawer (left) and AgentDrawer (right) that let users drag triggers and agents onto the React Flow canvas. Integrate drop handling, connection validation, and node repositioning into the main CuePipelineEditor component.
…lor utilities, and CRUD operations
- NodeConfigPanel: event-specific trigger config (interval, glob, repo, etc.) and agent prompt textarea with pipeline membership display - EdgeConfigPanel: mode selector (pass/debate/autorun) with debate settings (max rounds, timeout) and autorun explanation - Selection handling: node/edge click, pane click to dismiss, Delete/Backspace keyboard shortcut to remove selected elements - All config changes update pipeline state immediately (debounced text inputs)
Create pipelineToYaml.ts that converts visual pipeline graph state into CueSubscription objects and YAML strings. Handles linear chains, fan-out, fan-in, and edge mode annotations (debate/autorun). Includes 13 passing tests.
Converts CueSubscription objects back into visual CuePipeline structures, enabling round-trip fidelity between YAML config and the graph editor. Supports chain grouping, fan-out, fan-in, deduplication, and auto-layout.
…d/validation - Load existing YAML subscriptions on mount via graphSessionsToPipelines - Add Save button that converts pipelines to YAML, writes via IPC, and refreshes sessions - Add dirty state tracking with visual indicator dot on Save button - Add graph validation before save (triggers, agents, cycles, disconnected nodes) - Add Discard Changes button to reload from YAML - Add validation error banner with inline error display - Pass projectRoot from CueModal to pipeline editor sessions - Update yamlToPipeline to use minimal interface types for flexibility
…alive display:none collapses the container to 0×0, which either causes the WebGL context to be lost (Chromium reclaims GPU memory) or clears the canvas when dimensions change. Either way, term.refresh() cannot restore content after the transition back to display:flex. Switch the terminal session overlay to visibility:hidden + pointer-events:none. This keeps the canvas at its correct non-zero dimensions at all times, so the WebGL context is never invalidated and the framebuffer always holds the previous output. No explicit refresh/refit is needed on return — the content is simply un-hidden. This is consistent with how TerminalView already hides inactive terminal tabs (Tailwind's invisible class = visibility:hidden).
Add save/load IPC handlers for persisting pipeline graph layout (node positions, viewport zoom/pan, selected pipeline) to userData JSON file. Integrates with CuePipelineEditor to load saved positions on mount, merge with live graph data, and debounce-save on node drag end.
PtySpawner was ignoring command/args for toolType=terminal, always falling back to bash -l -i. SSH terminal spawns pass command='ssh' but no shell, so they silently spawned a local shell instead. Fix: when isTerminal && no shell is provided, use the explicit command/args directly (the ssh case). Shell -l/-i handling is now inside the else branch and only applies when shell is set.
…move YAML editor UI - Rename 'Graph' tab to 'Pipeline Editor' and make it the default tab - Replace 'Edit YAML' button with 'View in Pipeline' in Dashboard sessions table - Remove CueYamlEditor rendering (kept as commented import for future use) - Update CueHelpContent to describe visual pipeline editor workflow - Update all related tests to reflect new default tab and UI changes
- Sessions table: new "Pipelines" column with colored dots showing which pipelines each session belongs to (with tooltip on hover) - Active Runs: pipeline color dot next to each subscription name - Activity Log: pipeline color dot replaces generic Zap icon when subscription maps to a known pipeline - Added utility functions for subscription-to-pipeline name mapping - Graph data now fetched for both Dashboard and Pipeline tabs
The terminal width setting was dead code since the xterm upgrade — XTerminal uses FitAddon to auto-fit to its container via ResizeObserver, so a fixed column count no longer applies. Also clarifies the Maximum Log Buffer description to explain it controls the System Log Viewer, not xterm terminals.
- Keyboard shortcuts: Escape (deselect/close drawers), Cmd+S (save), TODO for Cmd+Z undo - Empty state message with directional arrows when no nodes exist - Connection validation via isValidConnection (prevent self-connections, trigger-to-trigger, duplicates) - Right-click context menu on nodes (Configure, Delete, Duplicate for triggers) - Visual feedback for running pipelines with animated dash edges - MiniMap node coloring based on event type and pipeline colors
…aths Covers the key behavior: when shell is provided, use shell+-l+-i flags; when shell is absent (SSH case), use command/args directly without injecting -l/-i. Also tests AI agent PTY and process registration.
Syntax highlighting used vscDarkPlus (dark theme) unconditionally, making code illegible on light themes. Now uses a shared getSyntaxStyle() helper that selects vs (light) or vscDarkPlus (dark/vibe) based on theme mode, matching the pattern mobile code already used. Also adjusted all 6 light theme color palettes to meet WCAG AA contrast (4.5:1) for text on backgrounds. Worst offender was Solarized Light where even textMain failed. Darkened textDim, accent, accentText, success, warning, and error values while preserving original hues.
The mountedTerminalSessionIds.map() was inside the else branch of a ternary that also handles loading state and file preview. When a file preview tab became active, React rendered the file-preview branch instead of the else branch, unmounting XTerminal entirely. On return, XTerminal remounted fresh with an empty buffer — all previous output was gone. Fix: wrap the entire content-area ternary in a permanent relative div and move the terminal sessions map outside the ternary so it is always rendered. The absolute inset-0 overlay is visibility:hidden when not in terminal mode, so it never interferes with file preview or AI output. This ensures XTerminal stays mounted regardless of which view is active.
Working agents should remain visible when filtering by unread, so users can see both active work and unread output at a glance.
- Fix graphSessions dependency in loadLayout effect so YAML data populates the graph (was running once on mount before async fetch) - Add fuzzy filter to TriggerDrawer (search by label, event type, description) matching AgentDrawer's existing search - Add descriptions to trigger items for better discoverability - Theme-aware drawers: both TriggerDrawer and AgentDrawer now use theme.colors instead of hardcoded dark colors - Add comprehensive tests for both drawers (21 tests)
…factored InlineWizard from main)
- Export mapThemeToXterm from XTerminal.tsx and add 8 tests covering dark/light fallback palettes, selection color, ANSI override precedence, and full 16-color output completeness - Add tests for three previously untested terminalTabHelpers exports: getActiveTerminalTab (4 cases), hasRunningTerminalProcess (4 cases), updateTerminalTabCwd (3 cases) - Also wraps the XTerminal terminal container in a paddingLeft:8px div (uncommitted cosmetic change is included in this commit)
Removes the floating ↓ button that appeared in the AI terminal output when the user scrolled up or had auto-scroll pinned. Also cleans up the now-unused hasNewMessages/newMessageCount state and associated dead code. Updates auto-scroll tests to remove button-specific cases.
- fix(security): prevent shell command injection in SSH terminal spawn;
use single-quoting with escaped apostrophes instead of JSON.stringify,
which did not suppress $(...) substitution inside double-quoted strings
- fix(test): remove duplicate vi.hoisted/vi.mock block in
UnifiedHistoryTab.test.tsx (second was silently shadowing the first)
- fix(style): correct indentation regressions in MainPanel.tsx and
TerminalView.tsx (merge artifacts — missing leading tabs)
- fix(log): gate process:runCommand deprecation warn behind a one-time
flag so it does not spam logs on every invocation
- fix(react): add eslint-disable comment for intentionally omitted
onTabStateChange dep in TerminalView onExit useEffect (callback uses
getState() internally so the reference is irrelevant)
- fix(state): add mountedTerminalTabStateKey Zustand subscription and
live store lookup so non-active TerminalView components receive fresh
session data when a background PTY exits (exit message was silently
dropped when mountedTerminalSessionsRef held a stale snapshot)
- fix(store): add .catch(() => {}) to process.kill() in
closeTerminalTab to handle the expected race where PTY exits before
the tab is removed
- fix(test): add missing trailing newline to TabSwitcherModal.test.tsx
- fix(shell): use process.env.SHELL (with /bin/bash fallback) instead of hard-coded 'zsh' in ProcessManager.spawnTerminalTab — prevents failure on Linux systems without zsh installed - fix(ui): guard useInputMode terminal toggle against empty terminalTabs; switching to terminal mode with no tabs produced inputMode='terminal' with activeTerminalTabId=null and nothing to render - fix(persist): move terminal tab runtime cleanup (pid/state/exitCode reset) before the early-return in prepareSessionForPersistence so terminal-only sessions (aiTabs=[]) also have their stale PTY state cleared on persist - fix(log): remove terminalTabs.length gate in useAgentListeners; the gate prevented runCommand exit codes from being recorded in shellLogs whenever the session happened to have any terminal tabs open - fix(perf): memoize createTabStateChangeHandler/createTabPidChangeHandler per session ID in MainPanel so TerminalView's React.memo() wrapper sees stable callback references across renders - fix(deps): add session.projectRoot to spawnPtyForTab useCallback dep array in TerminalView — omission caused stale CWD fallback for new PTY spawns - fix(test): add terminalTabs/activeTerminalTabId to createMergedSession mock in useMergeSession.test.ts to match updated Session shape - fix(test): assert session.activeTerminalTabId is null in useSessionCrud - fix(test): add real terminalTabs entries to navigateToUnifiedTabByIndex fixtures in tabHelpers.test.ts that use terminal mode + activeTerminalTabId
Maestro Symphony Contribution
Closes #160
Contributed via Maestro Symphony.
Status: In Progress
Started: 2026-03-01T08:46:08.940Z
This PR will be updated automatically when the Auto Run completes.
Summary by CodeRabbit
New Features
Documentation