Skip to content

[HDX-4769] feat(cli): terminal charting — dashboards TUI page and hdx chart command#2629

Open
wrn14897 wants to merge 1 commit into
mainfrom
warren/support-cli-charting
Open

[HDX-4769] feat(cli): terminal charting — dashboards TUI page and hdx chart command#2629
wrn14897 wants to merge 1 commit into
mainfrom
warren/support-cli-charting

Conversation

@wrn14897

@wrn14897 wrn14897 commented Jul 10, 2026

Copy link
Copy Markdown
Member

Linear: HDX-4769

Why

When troubleshooting from the terminal — especially agent-driven investigations — there was no way to see the data. The CLI could search and tail events, but visualizing a metric ("did errors spike at deploy time?", "what does p95 latency look like?") required switching to the web UI. This PR brings dashboard tiles and ad-hoc charts into the terminal so an investigation loop (chart → spot the anomaly → narrow with --where / hdx query / hdx stream) can happen entirely in the CLI.

What

hdx chart — terminal charts for CLI/agent troubleshooting

Three modes on one command, all flowing through the same renderChartConfig SQL pipeline the web dashboards use (via queryChartConfig inherited from common-utils), so SQL and results match the web UI exactly — including metric-source query splitting/joining:

# 1. Saved dashboard tiles (name or ID)
hdx chart -d "Service Health" -t "P95 Latency" --since 24h

# 2. Ad-hoc builder charts
hdx chart -s Logs --where 'SeverityText:error' --group-by ServiceName --since 3h
hdx chart -s Traces --agg quantile --level 0.95 --value Duration
hdx chart -s Metrics --metric-type sum --metric-name otelcol_exporter_sent_spans

# 3. Ad-hoc raw SQL (supports $__timeFilter / $__timeInterval macros)
hdx chart -s Logs --sql 'SELECT $__timeInterval(TimestampTime) AS ts, count()
  FROM default.otel_logs WHERE $__timeFilter(TimestampTime) GROUP BY ts'
  • Display types: line, stacked bar, number, table, bar, pie, markdown (heatmap/search/patterns/PromQL show a placeholder)
  • Time ranges: --since 1h, or --from/--to accepting ISO 8601, date-only, and relative (now-24h) — same parser as the TUI time-range editor
  • Agent-friendly: ANSI colors auto-stripped when stdout is not a TTY (--color auto|always|never), --json emits raw rows + column metadata, actionable errors with exit 1, help text written as agent documentation

TUI Dashboards page (d key)

Dashboard picker → tiles in grid order with j/k navigation, Enter for fullscreen, t time-range editor, r refresh. Only tiles scrolled into view are mounted/queried (mirrors the web's viewport-gated fetching).

Architecture

Chart renderers are pure functions (data + dimensions → ANSI string) shared by the Ink TUI and the stdout command. Ports from the web app carry @source comments and follow the existing CLI web-alignment convention:

CLI Web
shared/tileConfig.ts DBDashboardPage Tile config resolution + ChartUtils transforms
shared/tileRender.ts renderChartContent dispatch
shared/chartData.ts formatResponseForTimeChart / categorical shaping
shared/formatNumber.ts formatNumber (numbro) + number-format resolution

Fixes along the way

  • Silenced the @clickhouse/client internal logger (ClickHouseLogLevel.OFF): Ink's patchConsole re-enabled console output that silenceLogs had suppressed, spewing abort stack traces over the TUI whenever an in-flight tile query was legitimately cancelled
  • Memoized ClickHouse client/metadata in App so re-renders don't churn data-fetch effects
  • Aligned the pre-commit knip invocation with CI (exclude namespace-only findings CI doesn't count) — the hook failed on a clean checkout of main

Testing

  • 40 unit tests (tileConfig resolution incl. metric/raw-SQL/trace sampling paths, granularity parity with the web's 80-bucket cap, ad-hoc flag → config building, ANSI stripping)
  • Live-verified against a real deployment: builder/metric/raw-SQL/quantile charts (incl. auto duration formatting), all display types, dashboard + tile lookup by name and ID, --json, all three color modes, error paths, and the TUI page via pty
  • tsc --noEmit, yarn ci:lint, changeset included

New deps (CLI devDependencies, bundled): asciichart (zero-dep line plots), numbro (number formatting parity with the web).

Demo

image

…command

Render dashboard tiles and ad-hoc queries as ANSI charts in the terminal,
using the same renderChartConfig SQL pipeline as the web dashboards.

- TUI Dashboards page (d key): picker, tile navigation, fullscreen,
  time-range editing, refresh; viewport-windowed tile fetching
- hdx chart command for CLI/agent troubleshooting with three modes:
  saved tiles (-d/-t), ad-hoc builder (-s + --agg/--value/--where/
  --group-by/--series), and ad-hoc raw SQL (--sql with $__timeFilter /
  $__timeInterval macros)
- Display types: line, stacked bar, number, table, bar, pie, markdown
- Time ranges: --since durations plus --from/--to (ISO, date-only, now-1h)
- Agent-friendly output: --json rows+meta, ANSI colors auto-stripped when
  piping (--color auto|always|never), actionable errors with exit 1
- Silence @clickhouse/client logger (Ink patchConsole re-enabled it,
  spewing abort stacks over the TUI) and memoize client/metadata in App
  so re-renders don't abort in-flight tile queries
- Align pre-commit knip with CI (exclude namespace-only findings that the
  CI workflow does not count) so clean commits are not blocked
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c221319

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hyperdx/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
hyperdx-oss Ignored Ignored Jul 10, 2026 11:54pm
hyperdx-storybook Ignored Ignored Jul 10, 2026 11:54pm

Request Review

@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Large diff: 3668 production lines changed (threshold: 1000)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 21
  • Production lines changed: 3668 (+ 542 in test files, excluded from tier calculation)
  • Branch: warren/support-cli-charting
  • Author: wrn14897

To override this classification, remove the review/tier-4 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds terminal charting support to the CLI. The main changes are:

  • New hdx chart command for dashboard tiles, ad-hoc builder charts, and raw SQL charts.
  • New dashboard TUI page with tile navigation, fullscreen view, refresh, and time-range editing.
  • Shared chart config, query, shaping, rendering, and number-format helpers.
  • CLI docs, tests, dependency updates, and ClickHouse logger silencing.

Confidence Score: 4/5

The charting path needs fixes for nullable numeric values and timestamp-field parity before merging.

  • Nullable numeric cells can render as real zero points in terminal line charts.
  • Builder charts can use a different timestamp field than the web-aligned source mapping.
  • Dashboard navigation, tile fetch cleanup, and command mode validation otherwise look consistent with the changed code.

packages/cli/src/shared/chartData.ts; packages/cli/src/shared/tileConfig.ts

Important Files Changed

Filename Overview
packages/cli/src/cli.tsx Adds the hdx chart command, chart mode validation, time parsing, dashboard lookup, ad-hoc config wiring, rendering, and JSON output.
packages/cli/src/shared/chartData.ts Adds chart response shaping, with a nullable numeric value path that can produce misleading zero points.
packages/cli/src/shared/ansiChart.ts Adds pure ANSI renderers for the supported terminal chart types.
packages/cli/src/shared/tileConfig.ts Adds tile config resolution and display-type query transforms, with a timestamp-source mismatch risk for web parity.
packages/cli/src/shared/adhocChart.ts Adds ad-hoc chart flag parsing, source lookup, raw SQL config creation, and builder config creation.
packages/cli/src/components/DashboardPage.tsx Adds dashboard picker, tile list, fullscreen tile view, time-range editing, and refresh behavior.
packages/cli/src/components/Tile/useTileData.ts Adds abortable tile data fetching and refresh dependency handling.
packages/cli/src/App.tsx Adds dashboard screen routing and memoized ClickHouse client and metadata instances.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[hdx chart or DashboardPage] --> B[SavedChartConfig]
  B --> C[fetchTileData]
  C --> D[resolveTileConfig]
  D --> E[convertTileConfigForQuery]
  E --> F[queryChartConfig]
  F --> G[chartData shaping]
  G --> H[tileRender]
  H --> I[ANSI chart output]
  H --> J[JSON output]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
  A[hdx chart or DashboardPage] --> B[SavedChartConfig]
  B --> C[fetchTileData]
  C --> D[resolveTileConfig]
  D --> E[convertTileConfigForQuery]
  E --> F[queryChartConfig]
  F --> G[chartData shaping]
  G --> H[tileRender]
  H --> I[ANSI chart output]
  H --> J[JSON output]
Loading

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "feat(cli): add terminal charting — dashb..." | Re-trigger Greptile

Comment on lines +301 to +306
const value =
typeof rawValue === 'number'
? rawValue
: Number.parseFloat(String(rawValue));

tsBucket[keyName] = value;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Null Values Become Zeroes

When a chart query returns NULL for a numeric cell, this parses it as NaN and stores it in the time bucket. The line renderer later turns non-finite points into 0, so nullable averages, quantiles, or raw SQL series can show a real drop to zero instead of missing data.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

connection: source.connection,
dateRange,
granularity,
timestampValueExpression: source.timestampValueExpression ?? '',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Charts Use Coarser Timestamps

Builder charts resolve their query timestamp from timestampValueExpression, while the CLI guide says web-aligned source mapping should use displayedTimestampValueExpression for timestamp fields. On sources where those differ, the same dashboard tile or ad-hoc chart can bucket and filter on the lower-precision timestamp, producing results that do not match the web dashboard.

Context Used: packages/cli/AGENTS.md (source)

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

Terminal charting for the CLI: new hdx chart command (dashboard tiles / ad-hoc builder / raw SQL), a TUI Dashboards page, and shared pure-function chart renderers ported from the web app. The port is careful about scale (row/column caps before rendering, viewport-gated tile mounting, AbortController cancellation, memoized ClickHouse client/metadata). No SQL-injection, auth-bypass, secret-leak, or data-loss issues were found — ad-hoc SQL runs against the user's own team-scoped, server-authorized connection, so injecting SQL there is by design, not a vulnerability.

✅ No critical issues found.

🟡 P2 — recommended

  • packages/cli/src/components/Tile/TileChart.tsx:99renderTileContent is called directly in React render, but formatResponseForTimeChart throws when a Line/StackedBar result has no Date or no numeric column; useTileData catches fetch errors but not render errors, so a single non-conforming tile crashes the entire Dashboards TUI instead of showing a per-tile error.
    • Fix: wrap the renderTileContent call in a try/catch and render an error box for that tile, mirroring the renderOne handling in the stdout command.
  • packages/cli/src/shared/chartData.ts:229 — the PR's advertised core modules (chartData.ts, ansiChart.ts, formatNumber.ts, tileRender.ts) have no unit tests; the 40 added tests cover only config building/resolution, leaving the data-shaping, ANSI rendering, and number/duration math unverified.
    • Fix: add unit tests for empty/NaN/single-bucket/unicode inputs across the pivot, resample, categorical truncation, and formatDurationMs boundary paths.
    • testing, performance
  • packages/cli/src/shared/ansiChart.ts:33stripAnsi matches only SGR color sequences (\x1b[...m), so query-result values containing other terminal control/escape sequences pass through even on the --color never / non-TTY path, weakening the "clean output for agents" guarantee and leaving a minor terminal-manipulation surface for adversarial log content.
    • Fix: strip the full range of ANSI/control escape sequences (or sanitize rendered cell/label values) on the non-color path.
    • security
🔵 P3 nitpicks (3)
  • packages/cli/src/cli.tsx:1303--width and --height are passed through Number() without NaN validation, so --width abc yields NaN dimensions and a broken (though caught, non-crashing) chart, unlike the other flags which error out with a message.
    • Fix: validate parsed width/height are finite positive integers and exit 1 with a hint otherwise.
  • packages/cli/src/components/Tile/useTileData.ts:99config and source are read via refs and excluded from the effect dependency array, so a config change with no other dep change would not refetch; currently masked only by the key={tile.id} remount, which is fragile.
    • Fix: include a stable config identity key in the deps or document the remount invariant at the ref assignment.
  • packages/cli/src/components/DashboardPage.tsx:162 — picker down-navigation computes Math.min(dashboards.length - 1, prev + 1), which yields -1 when there are zero dashboards; harmless due to the render guard but should floor at 0.
    • Fix: clamp the index with Math.max(0, …).

Reviewers (12): correctness, testing, maintainability, project-standards, agent-native, learnings-researcher, security, performance, reliability, adversarial, kieran-typescript, julik-frontend-races.

Testing gaps:

  • No coverage for chartData.ts, ansiChart.ts, formatNumber.ts, or tileRender.ts (empty results, NaN/non-finite values, single-bucket resampling, unicode/wide-character truncation, formatDurationMs boundaries).
  • No test for the raw-SQL macro ($__timeFilter / $__timeInterval) expansion path end-to-end, nor for the parseDuration / --from/--to/from >= to validation branches in the chart action.
  • No test asserting agent-facing output is free of residual terminal escape sequences on the non-TTY path.

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 235 passed • 3 skipped • 1476s

Status Count
✅ Passed 235
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@wrn14897 wrn14897 changed the title feat(cli): terminal charting — dashboards TUI page and hdx chart command [HDX-4769] feat(cli): terminal charting — dashboards TUI page and hdx chart command Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant