Skip to content

fix: keep dashboard interactive on TTYs#23

Open
flyingrobots wants to merge 1 commit intomainfrom
fix/tui-no-color-interactive
Open

fix: keep dashboard interactive on TTYs#23
flyingrobots wants to merge 1 commit intomainfrom
fix/tui-no-color-interactive

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Mar 17, 2026

Summary

  • keep the vault dashboard interactive on real TTYs even when NO_COLOR=1
  • make launchDashboard() fall back to the static list for non-interactive CLI contexts instead of rendering a one-frame TUI
  • harden slow test timeouts and CLI integration time budgets so Node/Bun/Deno all stay green

Testing

  • npx eslint .
  • npm test
  • GIT_STUNTS_DOCKER=1 npx vitest run test/integration
  • bunx vitest run test/unit
  • GIT_STUNTS_DOCKER=1 bunx vitest run test/integration
  • deno run -A npm:vitest run test/unit
  • GIT_STUNTS_DOCKER=1 deno run -A npm:vitest run test/integration

Notes

  • I reproduced the dashboard bug locally: with NO_COLOR=1, the Bijou context downgraded to pipe mode and the dashboard rendered once before exiting.
  • Docker-based Bun verification became unreliable in this desktop session because the Docker daemon stalled during repeated image rebuilds, so I completed the Bun/Deno verification on the installed host runtimes as a fallback. The code paths are the same, but this PR should still rely on GitHub CI for the authoritative container-backed signal.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced CLI environment-aware execution modes (interactive, pipe, static, accessible) for adaptive command-line experiences.
    • Enabled dashboard to operate in non-interactive mode, outputting static vault listings when needed.
  • Tests

    • Increased test timeouts across multiple test suites for improved stability and reliability.

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

This pull request adds CLI TUI mode detection and context creation functionality (detectCliTuiMode and createCliTuiContext) to support both interactive and non-interactive dashboard execution modes. The dashboard launcher now accepts optional configuration parameters for dependency injection and routing between interactive and static output flows. Test infrastructure updates extend timeout thresholds for performance-intensive test cases.

Changes

Cohort / File(s) Summary
CLI Context & Mode Detection
bin/ui/context.js
Added detectCliTuiMode(runtime) to classify runtime environment as 'accessible', 'pipe', 'static', or 'interactive' based on TTY and environment variables. Added createCliTuiContext(options) to instantiate Bijou context for CLI flows with mode derivation and NO_COLOR preservation.
Dashboard Launch & DI
bin/ui/dashboard.js
Updated launchDashboard(cas, options) and printStaticList() to accept options parameter enabling dependency injection (ctx, runApp, output). Routes to static listing for non-interactive contexts instead of interactive flow. Replaced createNodeContext with createCliTuiContext.
CLI Context Unit Tests
test/unit/cli/context.test.js
New test suite for detectCliTuiMode with helper makeRuntime to mock runtime environment, covering interactive mode with NO_COLOR, pipe fallback, and CI static mode detection.
Dashboard Launch Unit Tests
test/unit/cli/dashboard.launch.test.js
New test suite verifying interactive vs. non-interactive dashboard paths, including mocked vault-cas integration and tab-separated output validation for static mode.
Dashboard Mock Updates
test/unit/cli/dashboard.test.js
Added mock export of createCliTuiContext() to match production interface.
Integration Test Timeouts
test/integration/round-trip.test.js, test/integration/vault-cli.test.js
Set global test configuration with 15000 ms test timeout and 30000 ms hook timeout. Increased CLI subprocess timeout from 30 to 90 seconds.
Performance Test Timeouts
test/unit/domain/services/CasService...test.js, test/unit/domain/services/rotateVaultPassphrase.test.js, test/unit/facade/ContentAddressableStore.rotation.test.js, test/unit/vault/VaultService.test.js
Added/updated timeout constants (SLOW_COMPRESSION_TEST_TIMEOUT_MS, SLOW_EMPTY_FILE_TEST_TIMEOUT_MS, SLOW_ENVELOPE_TEST_TIMEOUT_MS, SLOW_KDF_TEST_TIMEOUT_MS, LONG_TEST_TIMEOUT_MS) ranging from 15000 to 60000 ms to accommodate slow compression, KDF, and rotation operations.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/CLI
    participant Detector as Mode Detector
    participant CtxFactory as Context Factory
    participant Dashboard as Dashboard Launcher
    participant Interactive as Interactive TUI
    participant Static as Static Listing

    rect rgba(100, 150, 200, 0.5)
    Note over Client,Static: Interactive Path
    Client->>Detector: detectCliTuiMode(runtime)
    Detector-->>Client: mode = 'interactive'
    Client->>CtxFactory: createCliTuiContext(options)
    CtxFactory-->>Client: ctx (interactive)
    Client->>Dashboard: launchDashboard(cas, {ctx, runApp})
    Dashboard->>Interactive: runApp(ctx)
    Interactive-->>Dashboard: TUI rendered
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Client,Static: Non-Interactive Path
    Client->>Detector: detectCliTuiMode(runtime)
    Detector-->>Client: mode = 'pipe' or 'static'
    Client->>CtxFactory: createCliTuiContext(options)
    CtxFactory-->>Client: ctx (non-interactive)
    Client->>Dashboard: launchDashboard(cas, {ctx, output})
    Dashboard->>Static: printStaticList(cas, output)
    Static->>Static: listVault → tab-separated rows
    Static->>Client: write to output stream
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 Hops of joy through mode detection bright,
Interactive dances or static lines just right,
Context flows where TUI dreams align,
Dashboard sings in every mode divine!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix: ensuring the dashboard remains interactive on TTYs despite NO_COLOR settings, which is the core objective addressed across the changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tui-no-color-interactive
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/unit/cli/context.test.js (1)

15-40: Add explicit tests for BIJOU_ACCESSIBLE and TERM=dumb branches.

Current coverage is good, but these two branches are part of mode precedence and worth locking with direct tests to prevent regressions.

Suggested test additions
 describe('detectCliTuiMode', () => {
+  it('uses accessible mode when BIJOU_ACCESSIBLE=1', () => {
+    const mode = detectCliTuiMode(makeRuntime({
+      env: { BIJOU_ACCESSIBLE: '1', TERM: 'xterm-256color' },
+    }));
+    expect(mode).toBe('accessible');
+  });
+
+  it('falls back to pipe when TERM is dumb', () => {
+    const mode = detectCliTuiMode(makeRuntime({
+      env: { TERM: 'dumb' },
+    }));
+    expect(mode).toBe('pipe');
+  });
+
   it('stays interactive on a TTY when NO_COLOR is set', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/cli/context.test.js` around lines 15 - 40, Add two explicit unit
tests for detectCliTuiMode: one that sets env BIJOU_ACCESSIBLE='1' (with TERM
present) and asserts the returned mode is the accessible mode (use
detectCliTuiMode with makeRuntime and expect mode === 'accessible' to lock that
branch), and another that sets TERM='dumb' (with a TTY stdout) and asserts the
returned mode is the static fallback (expect mode === 'static'); place them
alongside the existing tests so the BIJOU_ACCESSIBLE and TERM=dumb branches are
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/ui/dashboard.js`:
- Around line 285-291: The JSDoc above printStaticList is incorrect—it's
describing an options object but the function signature is async function
printStaticList(cas, output = process.stdout). Update the JSDoc for
printStaticList to match the actual parameters by documenting the first
parameter (cas) and the second parameter (output, defaulting to process.stdout
and of type Pick<NodeJS.WriteStream,'write'> or a WriteStream), remove the
erroneous options block, and ensure types and brief descriptions reference the
exact symbol printStaticList and its parameters.

---

Nitpick comments:
In `@test/unit/cli/context.test.js`:
- Around line 15-40: Add two explicit unit tests for detectCliTuiMode: one that
sets env BIJOU_ACCESSIBLE='1' (with TERM present) and asserts the returned mode
is the accessible mode (use detectCliTuiMode with makeRuntime and expect mode
=== 'accessible' to lock that branch), and another that sets TERM='dumb' (with a
TTY stdout) and asserts the returned mode is the static fallback (expect mode
=== 'static'); place them alongside the existing tests so the BIJOU_ACCESSIBLE
and TERM=dumb branches are covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0d224d0-e36b-4355-baf1-ba32305914fc

📥 Commits

Reviewing files that changed from the base of the PR and between bedba6a and 6509987.

📒 Files selected for processing (14)
  • bin/ui/context.js
  • bin/ui/dashboard.js
  • test/integration/round-trip.test.js
  • test/integration/vault-cli.test.js
  • test/unit/cli/context.test.js
  • test/unit/cli/dashboard.launch.test.js
  • test/unit/cli/dashboard.test.js
  • test/unit/domain/services/CasService.compression.test.js
  • test/unit/domain/services/CasService.empty-file.test.js
  • test/unit/domain/services/CasService.envelope.test.js
  • test/unit/domain/services/CasService.kdf.test.js
  • test/unit/domain/services/rotateVaultPassphrase.test.js
  • test/unit/facade/ContentAddressableStore.rotation.test.js
  • test/unit/vault/VaultService.test.js

Comment on lines +285 to +291
* @param {{
* ctx?: BijouContext,
* runApp?: typeof run,
* output?: Pick<NodeJS.WriteStream, 'write'>,
* }} [options]
*/
async function printStaticList(cas) {
async function printStaticList(cas, output = process.stdout) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

JSDoc for printStaticList does not match its actual parameter.

Line 285 documents an options object, but Line 291 accepts output directly. Please align the doc block with the function signature.

Doc fix
 /**
  * Print static list for non-TTY environments.
  *
  * `@param` {ContentAddressableStore} cas
- * `@param` {{
- *   ctx?: BijouContext,
- *   runApp?: typeof run,
- *   output?: Pick<NodeJS.WriteStream, 'write'>,
- * }} [options]
+ * `@param` {Pick<NodeJS.WriteStream, 'write'>} [output]
  */
 async function printStaticList(cas, output = process.stdout) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @param {{
* ctx?: BijouContext,
* runApp?: typeof run,
* output?: Pick<NodeJS.WriteStream, 'write'>,
* }} [options]
*/
async function printStaticList(cas) {
async function printStaticList(cas, output = process.stdout) {
/**
* Print static list for non-TTY environments.
*
* `@param` {ContentAddressableStore} cas
* `@param` {Pick<NodeJS.WriteStream, 'write'>} [output]
*/
async function printStaticList(cas, output = process.stdout) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ui/dashboard.js` around lines 285 - 291, The JSDoc above printStaticList
is incorrect—it's describing an options object but the function signature is
async function printStaticList(cas, output = process.stdout). Update the JSDoc
for printStaticList to match the actual parameters by documenting the first
parameter (cas) and the second parameter (output, defaulting to process.stdout
and of type Pick<NodeJS.WriteStream,'write'> or a WriteStream), remove the
erroneous options block, and ensure types and brief descriptions reference the
exact symbol printStaticList and its parameters.

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.

1 participant