Skip to content

Conversation

@buger
Copy link
Contributor

@buger buger commented Jan 22, 2026

Summary

  • add a new --tui flag to run the CLI with a clean, tabbed terminal UI
  • show chat-style responses and formatted output in the default tab; show logs in a dedicated logs tab
  • route logger output into the TUI via a sink (keeps stdout clean) and harden error handling
  • add a blessed-based TUI manager and dependencies
  • include the existing visor benefits doc + example from this branch
  • add unit tests for console capture + logger sink error behavior

UX

  • Tab / Shift+Tab or Left/Right to switch tabs, 1/2 to jump directly
  • q to exit after completion; Ctrl+C to abort
  • Chat pane hides when no chat content (output expands to full height)

Notes

  • TUI auto-disables for non-TTY, --debug-server, --slack, or NODE_ENV=test
  • TUI initialization errors fall back to standard output
  • TUI exit now has a safety timeout (override with VISOR_TUI_HOLD_MS)

Testing

  • npm test (timed out after 5m)
  • npx jest --runInBand (timed out after 5m; many suites passed before timeout)
  • npm run test:yaml
  • npx jest --runInBand tests/unit/logger-sink.test.ts tests/unit/tui.test.ts

@probelabs
Copy link
Contributor

probelabs bot commented Jan 22, 2026

PR Overview: CLI --tui Mode with Tabbed Interface

Summary

This PR introduces an interactive Terminal User Interface (TUI) mode for the Visor CLI, providing a clean, tabbed interface that separates chat/output from logs. The implementation uses the blessed library to create an ncurses-like terminal UI with keyboard navigation.

Key Changes

Core TUI Implementation (src/tui.ts - 604 lines)

TuiManager Class: A comprehensive terminal UI manager that handles:

  • Tabbed interface: Two tabs - "Chat/Output" (main) and "Logs" with keyboard navigation
  • Dynamic layout: Split-pane (60% chat, 40% output) when chat content exists; output expands to full height when no chat
  • Keyboard controls: Tab/Shift+Tab/arrow keys to switch tabs, number keys (1/2) for direct tab access, 'q' to quit after completion, Ctrl+C to abort
  • Console capture: Intercepts console.log/error/warn/info to redirect output to the logs pane
  • Status bar: Shows running state and keyboard hints
  • Pending content queue: Handles content updates before TUI initialization

Logger Integration (src/logger.ts)

Sink mechanism: Added setSink() method to redirect log messages to custom handlers

  • Passthrough control: Optional passthrough to preserve original console output (disabled when TUI active)
  • TUI integration: Logger sends formatted messages to TUI's log pane when active
  • Error handling: Configurable error modes (throw/warn/silent) for sink failures

CLI Integration (src/cli-main.ts, src/cli.ts)

--tui flag: New CLI option to enable TUI mode across all commands

  • Auto-detection: TUI only activates in TTY environments, not in test/debug-server/Slack modes
  • Chat transcript builder: New buildChatTranscript() function extracts and formats AI/chat outputs for the TUI chat pane
  • Lifecycle management: Proper startup/shutdown handling with waitForExit() for clean exit on 'q' keypress
  • Conditional output: Console output suppressed when TUI is active
  • Fallback handling: TUI initialization errors fall back to standard output with warning message

Timeout Increase

Default timeout: Increased from 10 minutes (600000ms) to 20 minutes (1200000ms) across all AI execution paths

  • Affected files: ai-review-service.ts, execution invokers, foreach-processor, on-init-handlers, level-dispatch
  • Rationale: Accommodates longer-running AI operations in interactive mode

Dependencies Added

  • blessed@^0.1.81: Terminal UI library for creating ncurses-like interfaces
  • @types/blessed@^0.1.27: TypeScript definitions

Architecture & Impact

Component Flow

graph TD
    A[CLI --tui flag] --> B{TUI Check}
    B -->|TTY + no conflicts| C[Initialize TuiManager]
    B -->|Conflicts| D[Standard CLI mode]
    C --> E[Capture Console]
    C --> F[Set Logger Sink]
    F --> G[Logs Pane]
    E --> G
    H[Workflow Execution] --> I[Chat Transcript Builder]
    I --> J[Chat Pane]
    H --> K[Output Formatter]
    K --> L[Output Pane]
    M[User Input] -->|Tab/1/2/q| N[Tab Switcher]
    N --> J
    N --> G
    N --> L
Loading

System Components Affected

  1. CLI Entry Points (cli.ts, cli-main.ts)

    • Added --tui option to all CLI commands
    • Conditional TUI activation logic with safety checks
    • TUI cleanup in both success and error paths
  2. Logging System (logger.ts)

    • New sink architecture for log routing
    • Maintains backward compatibility
    • Configurable error handling for sink failures
  3. Execution Engine (multiple dispatch files)

    • Timeout defaults increased for AI operations
    • No behavioral changes beyond timeout
  4. Output Processing (cli-main.ts)

    • New buildChatTranscript() function for chat pane content
    • Conditional console output (suppressed when TUI active)
    • Content extraction logic for AI, log, and chat group types

Files Changed Summary

  • 15 files changed with 953 additions and 16 deletions
  • New file: src/tui.ts (604 lines)
  • Modified: CLI integration, logger, multiple execution paths
  • Added dependencies: blessed, @types/blessed
  • Added tests: logger-sink.test.ts, tui.test.ts

Testing Notes

According to the PR description:

  • Unit tests added for console capture restoration
  • Unit tests added for logger sink error behavior
  • Manual testing recommended for TTY/non-TTY environments

Recommended additional testing:

  • TTY vs. non-TTY environments
  • Tab switching and keyboard navigation
  • Console output capture completeness
  • Clean shutdown on 'q' and Ctrl+C
  • Interaction with existing output formats
  • Terminal resize handling
  • Performance with high-volume logging
Metadata
  • Review Effort: 3 / 5
  • Primary Label: feature

Powered by Visor from Probelabs

Last updated: 2026-01-22T17:38:56.294Z | Triggered by: pr_updated | Commit: 934e657

💡 TIP: You can chat with Visor using /visor ask <your question>

@buger buger changed the title Add --tui mode for CLI CLI: add --tui mode with chat/output + logs tabs Jan 22, 2026
@probelabs
Copy link
Contributor

probelabs bot commented Jan 22, 2026

Security Issues (15)

Severity Location Issue
🟠 Error src/tui.ts:267
Global console methods are permanently modified without proper cleanup guarantees. If captureConsole() is called but stop() is never called (e.g., process crash, unhandled exception), the console remains redirected to the TUI, potentially causing data loss or incorrect logging in subsequent operations.
💡 SuggestionAdd a process-wide cleanup handler that restores console methods even if stop() is not called. Consider using process.on('beforeExit') or process.on('uncaughtException') to ensure cleanup.
🟠 Error src/tui.ts:415
The abortHandler directly calls process.exit(130) without allowing proper cleanup. This bypasses the finally block in cli-main.ts and may prevent proper resource cleanup, telemetry flushing, or TUI shutdown.
💡 SuggestionInstead of calling process.exit() directly in the abort handler, throw a special error that is caught by the main try-catch block, allowing the existing cleanup logic to execute.
🟠 Error src/tui.ts:415
The abortHandler contains async operations (flushNdjson, shutdownTelemetry) but is called from a synchronous signal handler. These async operations may not complete before process.exit(130) terminates the process.
💡 SuggestionRemove async operations from the abort handler or use a synchronous cleanup mechanism. Consider setting a flag and allowing the main event loop to handle cleanup.
🟠 Error src/cli-main.ts:711
The abortHandler in the TUI setup directly calls process.exit(130) which bypasses the finally block cleanup. This means resources like telemetry, file handles, or network connections may not be properly closed.
💡 SuggestionRefactor the abort handler to throw a special error type that is caught by the main try-catch block, allowing the existing cleanup logic in the finally block to execute properly.
🟢 Info src/cli-main.ts:711
TUI initialization errors are caught and logged, but the error message may contain sensitive information about the system state or environment. The error is logged to console.error which could be captured by logs.
💡 SuggestionSanitize error messages before logging them. Consider logging only a generic message in production while logging detailed errors in debug mode.
🟢 Info src/utils/json-text-extractor.ts:33
The escape sequence parsing in extractTextFieldFromMalformedJson manually handles escape sequences like \n, \r, \t, \". This manual parsing could be vulnerable to escape sequence injection if the input contains malformed escape sequences.
💡 SuggestionUse a proper JSON parser or validate escape sequences more strictly. Limit the length of escape sequences and reject invalid sequences.
🟢 Info src/tui.ts:530
When promptUser times out and a defaultValue is provided, the defaultValue is returned without user confirmation. This could lead to unintended actions if the default value is not what the user intended.
💡 SuggestionConsider requiring explicit user confirmation when using a default value after timeout, or log a warning that the default value was used.
🟡 Warning src/tui.ts:267
captureConsole() can be called multiple times, but only checks if this.consoleRestore exists. If called twice, the second call returns the existing restore function without creating a new one, but the console methods are already redirected. This creates an inconsistent state where the restore function may not match the current console state.
💡 SuggestionAdd a flag to track if console is already captured and throw an error or return early without modifying console methods again.
🟡 Warning src/tui.ts:233
process.once('exit', this.processExitHandler) registers an exit handler, but if start() is called multiple times or if stop() is not called, this handler may leak. The 'exit' event is only emitted once, but multiple registrations could cause issues.
💡 SuggestionEnsure the exit handler is properly cleaned up and prevent multiple registrations. Add a check in start() to avoid re-registering if already registered.
🟡 Warning src/logger.ts:113
When sink error mode is 'warn', error messages from sink failures are written to stderr. If the sink contains sensitive information (e.g., API keys, tokens), these could be exposed in error messages.
💡 SuggestionSanitize error messages before writing to stderr. Avoid including the full error object or message if it may contain sensitive data.
🟡 Warning src/cli-main.ts:711
When TUI initialization fails, the code falls back to standard output but may leave the system in an inconsistent state. The tuiConsoleRestore is called, but if TUI initialization failed partway through, some console methods may already be modified.
💡 SuggestionEnsure all console modifications are reverted even if TUI initialization fails. Consider using a try-finally block around the entire TUI initialization sequence.
🟡 Warning src/cli-main.ts:473
buildChatTranscript() directly concatenates result.content into the output without sanitization. If result.content contains ANSI escape sequences, terminal control codes, or specially crafted content, it could cause display issues or potential security issues in the TUI.
💡 SuggestionSanitize content before adding it to the chat transcript. Strip ANSI codes and validate that the content is safe for display in the TUI.
🟡 Warning src/utils/json-text-extractor.ts:33
The extractTextFieldFromMalformedJson function uses regex patterns to parse JSON-like content. While the patterns are hardcoded, the function processes untrusted input from AI models. Malformed input could potentially cause ReDoS (Regular Expression Denial of Service) if the input is specially crafted.
💡 SuggestionAdd input length limits and processing timeouts to prevent ReDoS. Consider using a more robust JSON parsing approach with explicit bounds checking.
🟡 Warning src/tui.ts:530
The promptUser method accepts user input without validation or sanitization. The input is directly returned and could contain malicious content if used in other contexts (e.g., command execution, file operations).
💡 SuggestionValidate and sanitize user input before returning it. Add length limits, character restrictions, and content validation based on how the input will be used.
🟡 Warning src/cli-main.ts:1561
The VISOR_TUI_HOLD_MS environment variable is parsed with parseInt() but not validated for reasonable bounds. An attacker could set this to an extremely large value to cause the process to hang indefinitely.
💡 SuggestionAdd upper and lower bounds validation for VISOR_TUI_HOLD_MS. Reject values that are too large (e.g., > 24 hours) or negative.

Architecture Issues (8)

Severity Location Issue
🟢 Info src/logger.ts:70-95
Logger sink mechanism only supports a single sink function. This prevents scenarios where logs might need to be routed to multiple destinations simultaneously (e.g., both TUI and a file, or multiple TUI panes).
💡 SuggestionConsider changing the sink to support multiple sinks: setSinks(sinks: Array<{sink: SinkFn, options?: SinkOptions}>) or use an event emitter pattern where logs are emitted as events and multiple listeners can subscribe.
🔧 Suggested Fix
Change to:
setSinks(sinks: Array<{sink: (msg: string, level: LogLevel) => void, opts?: SinkOptions}>): void {
  this.sinks = sinks;
}

private write(msg: string, level?: LogLevel): void {
for (const {sink, opts} of this.sinks) {
try { sink(msg, level || 'info'); }
catch (error) { /* handle based on opts */ }
}
}

🟢 Info src/cli-main.ts:714-730
TUI enablement logic combines 5 different boolean conditions (options.tui, isTTY, !debugServer, !slack, NODE_ENV !== 'test'). This creates a complex implicit contract that's easy to break and hard to debug.
💡 SuggestionExtract into a dedicated function with clear documentation: shouldEnableTui(options). Consider using a bitmask or enum for TUI disable reasons to make the logic more explicit and debuggable.
🔧 Suggested Fix
function shouldEnableTui(options: CliOptions): { enabled: boolean; disabledReason?: string } {
  const reasons = [];
  if (!options.tui) return { enabled: false };
  if (!process.stdout.isTTY || !process.stderr.isTTY) reasons.push('non-interactive TTY');
  if (options.debugServer) reasons.push('--debug-server');
  if (options.slack) reasons.push('--slack');
  if (process.env.NODE_ENV === 'test') reasons.push('test mode');
  return { enabled: reasons.length === 0, disabledReason: reasons.join(', ') };
}
🟢 Info src/tui.ts:553-604
updateLayout() contains hardcoded layout percentages (60% for chat, 40% for output) and magic numbers for input heights (6 for single-line, 10 for multi-line). These values are not configurable and may not work well on different terminal sizes.
💡 SuggestionExtract layout configuration to a const or config object. Consider making these ratios configurable via environment variables or CLI flags. Add logic to adjust ratios based on terminal size.
🔧 Suggested Fix
const TUI_LAYOUT_CONFIG = {
  CHAT_RATIO: 0.6,
  OUTPUT_RATIO: 0.4,
  INPUT_HEIGHT_SINGLE: 6,
  INPUT_HEIGHT_MULTI: 10,
  MIN_HEIGHT: 3,
} as const;

// Or make configurable:
interface TuiLayoutConfig {
chatRatio?: number;
inputHeightSingle?: number;
inputHeightMulti?: number;
}

constructor(private config: TuiLayoutConfig = {}) {}

private get chatRatio() { return this.config.chatRatio ?? 0.6; }

🟡 Warning src/cli-main.ts:473-530
buildChatTranscript() contains multiple special-case conditions for extracting text from different check types (AI, log, chat group). This creates a fragile, hard-to-maintain pattern matching approach that doesn't scale well with new check types or schemas.
💡 SuggestionConsider a more extensible strategy pattern where each check provider type registers its own content extractor. This would eliminate the cascading if-else logic and make it easier to add new check types without modifying this function.
🔧 Suggested Fix
Replace with a registry-based approach: checkProviders.registerTextExtractor('ai', (result) => ...); checkProviders.registerTextExtractor('log', (result) => ...); then use: const extractor = checkProviders.getTextExtractor(checkCfg.type); return extractor ? extractor(result) : extractTextFromJson(result.output);
🟡 Warning src/cli-main.ts:711-806
TUI cleanup logic is duplicated in both success and error paths (lines 1561-1590 and 1650-1679). This violates DRY and creates maintenance burden - any change to cleanup logic must be made in two places.
💡 SuggestionExtract TUI cleanup into a dedicated function cleanupTui(tui, tuiConsoleRestore) that handles all cleanup steps. Call this function from both the success and error paths.
🔧 Suggested Fix
Create a helper function:
async function cleanupTui(tui: TuiManager | null, tuiConsoleRestore: (() => void) | null): Promise<void> {
  if (!tui) return;
  try { tui.setRunning(false); } catch {}
  try { logger.setSink(undefined); } catch {}
  try { if (tuiConsoleRestore) tuiConsoleRestore(); } catch {}
  const holdMs = /* existing logic */;
  await tui.waitForExit(holdMs);
}

Then call: await cleanupTui(tui, tuiConsoleRestore);

🟡 Warning src/tui.ts:40-604
TuiManager class has 22 private fields and handles too many responsibilities: UI layout, keyboard input, console capture, user prompts, tab management, and lifecycle management. This violates Single Responsibility Principle and makes the class difficult to test and maintain.
💡 SuggestionSplit TuiManager into smaller, focused classes: TuiLayout (manages widgets and layout), TuiInputHandler (manages keyboard shortcuts and user input), TuiConsoleCapture (handles console interception), and TuiLifecycle (manages start/stop/exit). Use composition to coordinate these components.
🔧 Suggested Fix
Refactor into:
class TuiLayout { /* layout management */ }
class TuiInputHandler { /* keyboard and prompts */ }
class TuiConsoleCapture { /* console capture */ }
class TuiManager {
  private layout: TuiLayout;
  private input: TuiInputHandler;
  private console: TuiConsoleCapture;
  /* coordinate components */
}
🟡 Warning src/tui.ts:318-530
promptUser() method is 212 lines long and handles multiple concerns: UI creation, validation, timeout handling, keyboard binding, and cleanup. This makes the method difficult to understand, test, and modify.
💡 SuggestionExtract promptUser() into smaller methods: createInputPane(), setupInputHandlers(), validateAndSubmit(), and cleanupPrompt(). Consider a state machine for prompt lifecycle (idle, active, submitting, cancelled, timed out).
🔧 Suggested Fix
Break down into:
private createInputPane(): void { /* pane creation */ }
private setupInputHandlers(input: Textarea | Textbox, finish: FinishFn): void { /* keyboard bindings */ }
private validateAndSubmit(value: string): boolean { /* validation logic */ }
private cleanupPrompt(): void { /* cleanup */ }

async promptUser(options): Promise<string> {
this.createInputPane();
const input = /* create input widget /;
const finish = /
define finish callback /;
this.setupInputHandlers(input, finish);
/
return promise */
}

🟡 Warning src/tui.ts:233-280
captureConsole() directly mutates global console object (console.log, console.error, etc.). This creates hidden dependencies and makes testing difficult. Multiple TuiManager instances or overlapping capture/restore cycles could interfere with each other.
💡 SuggestionConsider a non-invasive approach: create a LoggerWrapper that intercepts calls without mutating global state, or use a dependency injection pattern where consumers explicitly use the captured logger instead of global console.
🔧 Suggested Fix
Instead of mutating global console:
captureConsole(): LoggerWrapper {
  const original = { ...console };
  const wrapper = new LoggerWrapper((...args) => this.appendLog(formatConsoleArgs(args)), original);
  return wrapper; // caller must explicitly use wrapper.log() instead of console.log()
}

Or use a proxy that can be explicitly enabled/disabled without global mutation.

Performance Issues (6)

Severity Location Issue
🟢 Info visor/src/cli-main.ts:473-517
buildChatTranscript() uses nested loops with string concatenation in a loop (messages.push() followed by messages.join()). While not severe, this could be optimized for large result sets.
💡 SuggestionThe current implementation is acceptable for typical use cases. However, for very large result sets (1000+ checks), consider using a StringBuilder pattern or array buffer with pre-allocation. The separator string is created on each call but could be a constant.
🟢 Info visor/src/logger.ts:113-145
The logger's write() method wraps every sink call in a try-catch block and performs multiple conditional checks. While necessary for error handling, this adds overhead to every log operation when a sink is active.
💡 SuggestionThe current implementation prioritizes robustness over raw performance, which is appropriate for a logging system. The overhead is minimal compared to the I/O operations. Consider caching the sinkErrorMode check result if the sink configuration never changes during runtime.
🟢 Info visor/src/tui.ts:260-271
appendLog() performs ANSI code stripping and line splitting on every log line, even when the logsBox is not yet initialized (pending to array). This processing happens synchronously on the main thread.
💡 SuggestionFor high-volume logging scenarios, consider deferring the ANSI stripping and line splitting until the logsBox is actually initialized. Store raw lines in pendingLogs and process them lazily when the TUI starts. This reduces CPU overhead during early execution phases.
🟡 Warning visor/src/tui.ts:260-271
appendLog() calls screen?.render() on every log line, which can cause excessive re-rendering during high-volume logging. Each render() call triggers a full screen redraw in blessed, which is expensive.
💡 SuggestionImplement render throttling or batching. Consider debouncing render calls or using a requestAnimationFrame-like pattern to batch multiple log updates into a single render. For example, use a flag to track if a render is pending and only call render() once per event loop tick using setImmediate() or process.nextTick().
🟡 Warning visor/src/tui.ts:232-243
Multiple methods (setStatus, setOutput, setChatContent) each call screen?.render() individually. When these are called in sequence, it causes multiple redundant screen renders.
💡 SuggestionIntroduce a render batching mechanism. Use a dirty flag and schedule a single render using setImmediate() or process.nextTick(). Alternatively, add a parameter to these methods to control whether to render immediately, allowing callers to batch updates and render once.
🟡 Warning visor/src/tui.ts:185-189
The resize event handler calls screen?.render() immediately. Terminal resize events can fire rapidly during window resizing, causing excessive renders.
💡 SuggestionDebounce the resize handler using a timer (e.g., 100ms delay) to avoid excessive renders during active resizing. Only render after the user has stopped resizing for a brief period.

Quality Issues (13)

Severity Location Issue
🟠 Error src/cli-main.ts:748
Abort handler uses void IIFE which swallows all errors silently. Multiple try-catch blocks with empty catch blocks hide potential failures during cleanup.
💡 SuggestionLog cleanup errors instead of silently ignoring them. At minimum, use logger.debug() to record cleanup failures for debugging.
🟠 Error tests/unit/tui.test.ts:7
TUI test only verifies console method restoration but doesn't test actual console capture behavior, TUI initialization, or error scenarios. Critical functionality like start(), stop(), setOutput(), appendLog() is untested.
💡 SuggestionAdd comprehensive tests for: TUI initialization with blessed mock, console output capture to logs pane, error handling in start(), cleanup in stop(), tab switching logic.
🟠 Error src/cli-main.ts:1435
buildChatTranscript() function is critical for TUI chat pane but has no unit tests. Complex logic for extracting text from different result types needs test coverage.
💡 SuggestionAdd unit tests for buildChatTranscript covering: AI results with simple schema, log results in chat group, chat group results, empty results, malformed JSON output.
🟡 Warning src/cli-main.ts:473
Magic number '60' used for separator line without semantic meaning. The separator uses '-'.repeat(60) but 60 is arbitrary - should be a named constant or derived from terminal width.
💡 SuggestionReplace with a named constant like const SEPARATOR_WIDTH = 60 or derive from process.stdout.columns
🟡 Warning src/cli-main.ts:473
buildChatTranscript function has complex nested conditionals with multiple type checks and fallback logic that's hard to follow. The logic for extracting text from different result types (isAi, isLog, isChatGroup, isSimpleSchema) is scattered across multiple if statements.
💡 SuggestionRefactor into smaller helper functions with clear responsibilities: extractAiContent(), extractLogContent(), extractChatContent(). Consider a strategy pattern for different content types.
🟡 Warning src/cli-main.ts:714
TUI initialization error handling catches all errors but doesn't distinguish between recoverable and fatal errors. The catch block restores console and stops TUI but may leave system in inconsistent state.
💡 SuggestionDistinguish between blessed initialization errors (recoverable) and other errors. Log specific error types for debugging.
🟡 Warning src/cli-main.ts:714
TUI cleanup code is duplicated in multiple places: success path (lines 1561-1580), error path (lines 1650-1669), and abort handler (lines 748-767). This violates DRY and increases maintenance burden.
💡 SuggestionExtract TUI cleanup into a single function cleanupTui() that handles all cleanup steps: setRunning(false), setSink(undefined), consoleRestore(), waitForExit().
🟡 Warning src/cli-main.ts:1561
TUI cleanup logic in finally block is identical to error path cleanup - exact duplication of 20 lines of cleanup code.
💡 SuggestionExtract to shared cleanupTui() function to eliminate duplication.
🟡 Warning src/logger.ts:70
Logger sink error handling has inconsistent behavior: 'warn' mode swallows errors after calling handler, 'throw' mode re-throws, but 'silent' mode does nothing. The 'warn' mode's nested try-catch with empty catch block hides secondary failures.
💡 SuggestionMake error handling more explicit: document that secondary failures are intentionally ignored, or log them at debug level.
🟡 Warning tests/unit/logger-sink.test.ts:28
Test uses magic string 'sink failed' for assertion without explaining why this exact string is expected. The assertion checks if stderr contains this substring but doesn't validate the full error message format.
💡 SuggestionUse a named constant like EXPECTED_ERROR_PREFIX = '[logger] sink failed' and add comment explaining the expected format. Consider asserting the full error message pattern.
🟡 Warning tests/unit/logger-sink.test.ts:28
Logger sink tests only cover error scenarios but don't test the 'silent' error mode or verify that passthrough works correctly when sink fails.
💡 SuggestionAdd test case for errorMode: 'silent' to verify no output is produced. Add test to verify passthrough=true still writes to stderr when sink fails.
🟡 Warning tests/unit/tui.test.ts:7
No tests for TUI error scenarios: what happens when blessed fails to initialize, when screen is destroyed, or when console restoration fails.
💡 SuggestionAdd negative test cases for: TUI initialization failure, console restoration failure, multiple captureConsole() calls, stop() without start().
🟡 Warning src/cli-main.ts:714
TUI initialization in main() is difficult to test because it creates real TuiManager instances. The code lacks dependency injection or factory pattern for testability.
💡 SuggestionConsider using a TUI factory or dependency injection to allow mocking TuiManager in tests. Alternatively, extract TUI initialization logic into separate testable function.

Powered by Visor from Probelabs

Last updated: 2026-01-22T17:38:59.792Z | Triggered by: pr_updated | Commit: 934e657

💡 TIP: You can chat with Visor using /visor ask <your question>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants