Skip to content

[WIP] Symphony: Improved Terminal Experience (#160)#486

Open
pedramamini wants to merge 215 commits intomainfrom
symphony/issue-160-mm7i9hl2
Open

[WIP] Symphony: Improved Terminal Experience (#160)#486
pedramamini wants to merge 215 commits intomainfrom
symphony/issue-160-mm7i9hl2

Conversation

@pedramamini
Copy link
Collaborator

@pedramamini pedramamini commented Mar 1, 2026

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

    • Persistent PTY-backed terminal tabs: create, switch, close, rename, search, and restore across sessions (desktop & mobile). Keyboard shortcuts for new/clear/navigate/jump-to-index; terminal search overlay with prev/next.
    • Remote tab actions: star, reorder, and toggle bookmark sync across clients.
    • Improved ANSI/theme support and terminal UX (exit messages, PID/state persistence).
  • Documentation

    • Added user-facing Terminal Tab System docs and TOC entry.

…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.
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)
@coderabbitai
Copy link

coderabbitai bot commented Mar 1, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Core Terminal UI
src/renderer/components/XTerminal.tsx, src/renderer/components/TerminalView.tsx, src/renderer/components/TerminalSearchBar.tsx, src/renderer/components/TerminalTabRenameModal.tsx
New xterm.js wrapper and TerminalView with multi-tab PTY lifecycle, search overlay, rename modal, imperative handles, spawn/retry/exit handling, and resize/search integrations.
Tab system & UI wiring
src/renderer/components/TabBar.tsx, src/renderer/components/MainPanel.tsx, src/renderer/components/TerminalOutput.tsx, src/renderer/App.tsx, src/renderer/components/AppModals.tsx
Unified-tab support for terminal tabs wired into TabBar/MainPanel/App and modals; TerminalView mounts in terminal mode; TerminalOutput simplified and error-detail APIs updated.
Tab helpers & store
src/renderer/utils/terminalTabHelpers.ts, src/renderer/utils/tabHelpers.ts, src/renderer/stores/tabStore.ts
Pure helpers for TerminalTab creation/CRUD, integration of terminal entries into unified ordering, reopen/repair/navigation updates, and tabStore actions/selectors for terminal tab operations.
Hooks & handlers
src/renderer/hooks/tabs/useTabHandlers.ts, src/renderer/hooks/tabs/index.ts, src/renderer/hooks/props/useMainPanelProps.ts, src/renderer/hooks/keyboard/useMainKeyboardHandler.ts
New useTerminalTabHandlers hook, re-exports, main-panel prop wiring, and keyboard shortcuts/terminal-mode behaviors added.
IPC / Main / PTY
src/main/ipc/handlers/process.ts, src/main/preload/process.ts, src/main/process-manager/ProcessManager.ts, src/main/process-manager/spawners/PtySpawner.ts, src/main/process-manager/types.ts
Added process:spawnTerminalTab IPC and preload.spawnTerminalTab, ProcessManager.spawnTerminalTab, PTY spawner changes (cols/rows, raw passthrough for terminal tabs), and ProcessConfig cols/rows.
Types & API surface
src/renderer/types/index.ts, src/renderer/global.d.ts, src/renderer/constants/shortcuts.ts, src/shared/theme-types.ts
Added TerminalTab type and session fields (terminalTabs, activeTerminalTabId, optional terminalDraft/terminalScrollTop), unified tab unions extended, MaestroAPI.spawnTerminalTab and remote tab events typed, new terminal shortcuts, and ANSI theme color fields.
Theming & CSS
src/shared/themes.ts, src/web/utils/cssCustomProperties.ts
Added ANSI palettes merged into themes and adjusted CSS property generation to skip ANSI-only fields.
Persistence & migration
src/renderer/hooks/utils/useDebouncedPersistence.ts, src/renderer/hooks/session/useSessionRestoration.ts, src/renderer/hooks/session/useSessionCrud.ts
Clean terminal runtime state before persist (pid/state/exitCode reset), ensure terminalTabs/default created on restore, preserve terminal metadata while resetting runtime fields.
Remote & WebServer
src/main/web-server/*, src/renderer/hooks/remote/*
WebServer and message handlers extended for star/reorder/toggle-bookmark remote tab actions; renderer remote handlers subscribe and update session/tab state.
Mobile & long-press actions
src/web/hooks/useLongPress.ts, src/web/mobile/TabBar.tsx, src/web/mobile/App.tsx, src/web/mobile/SessionPillBar.tsx
Added useLongPress hook and mobile tab long-press UI for rename/star/reorder/bookmark with mobile handlers wired through.
Xterm deps, docs & misc
package.json, ARCHITECTURE.md, CLAUDE.md, .gitignore, .prettierignore
Added xterm and addons dependencies; inserted Terminal Tab System docs (duplicated in ARCHITECTURE.md); minor docs/ignore updates.
Tests & mocks
src/__tests__/**/* (many files), src/__tests__/renderer/utils/terminalTabHelpers.test.ts, src/__tests__/renderer/hooks/utils/useDebouncedPersistence.test.ts
Large test updates: add terminalTabs/activeTerminalTabId to many mocks, new unit/integration tests for terminal helpers, persistence, tabStore behaviors, and IPC registration tests; some terminal-mode-specific tests removed/adjusted.
Platform detection refactor (widespread)
src/main/**, src/cli/**, src/renderer/**
Replaced ad-hoc process/navigator platform checks with shared platformDetection helpers across many modules (isWindows/isMacOS/isLinux/getWhichCommand).

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
Loading
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
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch symphony/issue-160-mm7i9hl2

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.
- 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)
- 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
@pedramamini pedramamini self-assigned this Mar 7, 2026
@pedramamini pedramamini added the RC Getting soak time in RC branch now label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RC Getting soak time in RC branch now

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improved Terminal Experience

6 participants