Skip to content

feat(cli): terminal art for login/logout/whoami - #3556

Open
aromeoes wants to merge 1 commit into
mainfrom
feat/cli-login-art
Open

feat(cli): terminal art for login/logout/whoami#3556
aromeoes wants to merge 1 commit into
mainfrom
feat/cli-login-art

Conversation

@aromeoes

@aromeoes aromeoes commented Aug 19, 2026

Copy link
Copy Markdown

Contribution path

  • Small, safe change that does not need a tracking issue

Problem

Terminal UI for dimos login, dimos logout and dimos whoami is plain typer.echo output. It works, but the UX could be better.

Demo

dimos loginwhoamilogout → a refused code.

dimos login

Solution

New dimos/cli/art.py: pure rendering, every function returns list[str]. cloud.py swaps seven prints for seven calls to it.

No behaviour change_post, _store, _forget, api_key, the polling loop and every exit code are byte-identical, and the seven existing tests pass unmodified.

No new librariesterminaltexteffects was already one; this drops the rich imports from cloud.py.

Four things to understand:

  • Live — the login card is redrawn in place each poll. pause() sleeps the server's interval but wakes every 100ms to animate, so the spinner turns without changing the poll cadence.
  • card() — the width ladder. Drops the art, then the border, then the two-column labels until it fits. Thresholds are derived from the copy, so the device code and URL survive to 30 columns.
  • enabled()isatty() and not NO_COLOR. When false there is no art, just the words, so nothing decorative reaches CI logs.
  • Colour is state — grey while waiting, red when refused, brand gradient once you are through.

Signing in lands on a single card: account and key at the top, then a short About DimOS with the community link and capability areas.

How to Test

dimos login          # then whoami, logout. Resize and re-run to see it degrade.
uv run pytest dimos/cli/test_art.py dimos/cli/test_cloud.py
uv run mypy dimos/cli/art.py

40 tests pass (33 new, 7 existing unmodified). The width sweep covers 30–120 columns and asserts the device code and URL are never truncated.

AI assistance

Claude Code with Opus 5, heavily involved — design exploration and implementation.

Checklist

  • I have read and approved the CLA.

Terminal UI for `dimos login`, `dimos logout` and `dimos whoami` is plain
`typer.echo` output. It works, but the UX could be considerably better —
especially during the browser-approval wait, which is the longest thing the
CLI asks anyone to sit through.

Adds `dimos/cli/art.py`, a pure rendering module where every function returns
`list[str]`. `cloud.py` swaps seven prints for seven calls to it. No new
dependencies: `terminaltexteffects` was already one, and this drops the `rich`
imports from `cloud.py`.

No behaviour change — `_post`, `_store`, `_forget`, `api_key`, the polling
loop and every exit code are byte-identical, and the seven existing tests pass
unmodified.

  - one card carries the whole wait: URL, device code, and a last line that
    ticks. `Live.pause()` sleeps the server's interval but wakes every 100ms
    to animate, so the spinner turns without changing the poll cadence.
  - colour is state: grey until something happens, red when a code is
    refused, and the brand gradient once you are through.
  - signing in reveals the wordmark and lands on a single card — account and
    key at the top, then a short About DimOS with the community link and
    capability areas. `dimos --help` remains the command list.
  - `whoami` stays a single line. It is a lookup people script against, not a
    moment, and its output format is unchanged.

Putting text in a fixed-width box adds two failure modes plain `echo` does not
have, and the tests pin both down:

  - nothing may exceed the terminal width. The layout drops the art, then the
    border, then the two-column labels; thresholds are derived from the copy
    rather than picked, so the device code and verification URL survive down
    to 30 columns — the width of the URL itself.
  - no art reaches non-terminal output. Off a TTY the cards render as plain
    lines, so CI logs and journald get the words alone.
@github-actions github-actions Bot added the first-time-contributor PR opened by an author who had not previously committed to this repository label Aug 19, 2026
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.40244% with 61 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/cli/art.py 76.13% 50 Missing and 8 partials ⚠️
dimos/cli/cloud.py 88.00% 2 Missing and 1 partial ⚠️
@@           Coverage Diff            @@
##             main    #3556    +/-   ##
========================================
  Coverage   76.13%   76.13%            
========================================
  Files        1228     1230     +2     
  Lines      119172   119485   +313     
  Branches    10684    10721    +37     
========================================
+ Hits        90726    90972   +246     
- Misses      25345    25402    +57     
- Partials     3101     3111    +10     
Flag Coverage Δ
OS-ubuntu-24.04-arm 70.57% <81.40%> (+0.02%) ⬆️
OS-ubuntu-latest 72.34% <81.40%> (+0.01%) ⬆️
Py-3.10 ?
Py-3.11 ?
Py-3.12 ?
Py-3.13 ?
Py-3.14 70.57% <81.40%> (-1.76%) ⬇️
Py-3.14t 72.34% <81.40%> (+0.01%) ⬆️
SelfHosted-Large 29.73% <24.69%> (-0.01%) ⬇️
SelfHosted-Linux 35.75% <24.69%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/cli/test_art.py 100.00% <100.00%> (ø)
dimos/cli/cloud.py 85.29% <88.00%> (+0.51%) ⬆️
dimos/cli/art.py 76.13% <76.13%> (ø)

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds adaptive terminal artwork and uses the new rendering helpers for cloud authentication output. Error output can still contain ANSI color sequences when stdout is interactive but stderr is redirected, which leaves escape codes in logs and error pipelines.

Confidence Score: 4/5

Not safe to merge as-is because redirected stderr can receive terminal escape sequences instead of plain text.

The mixed-output-stream behavior was exercised directly with interactive stdout and redirected stderr, and the captured stderr output retained ANSI escape codes.

Files Needing Attention: dimos/cli/art.py needs to determine terminal capability from the stream selected by show(..., err=True).

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a finding-comment-proof for a posted P1 finding.
  • T-Rex prepared the narrow test source for stdout-TTY and redirected-stderr behavior and captured the full destination-stream test source along with runtime output.
  • T-Rex executed the general-contract-validation-proof for the destination-stream test using the exact command and confirmed the observed result: exit code 0 with ANSI escapes in stdout and redirected stderr; the validation is confirmed.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 showerr=True preserves ANSI escapes when stderr is redirected

    • Bug
      • When stdout is a TTY and stderr is not, show() retains ESC color sequences in output written to stderr. Redirected error logs therefore receive decorative ANSI output rather than plain text.
    • Cause
      • At dimos/cli/art.py:446, the condition calls enabled(), which exclusively checks sys.stdout.isatty(), although err=True selects sys.stderr as the destination at line 447.
    • Fix
      • Select the output stream before deciding whether to strip escapes, then check that selected stream's isatty() status (while retaining the NO_COLOR behavior).

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(cli): terminal art for login/logout..." | Re-trigger Greptile

Comment thread dimos/cli/art.py
Comment on lines +446 to +447
out = "\n".join(rows if enabled() else [_ESC.sub("", r) for r in rows])
print(out, file=sys.stderr if err else sys.stdout)

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 Error output uses stdout terminal state

show(..., err=True) decides whether to preserve ANSI sequences through enabled(), which checks sys.stdout even though this call writes to sys.stderr. When stdout remains interactive but stderr is redirected to a log or error pipeline, color escapes are retained in that redirected output. Select the destination stream before checking terminal capability so redirected stderr receives plain text.

Artifacts

Narrow runnable test source for stdout-TTY and redirected-stderr behavior

  • Authored Python probe that replaces stdout and stderr with streams reporting True and False from isatty(), invokes both show destinations with ANSI rows, and asserts their captured contents. The test directly exercises the claimed path.

Captured full source of the narrow destination-stream test

  • Captured output of `cat trex-artifacts/art-show-destination-stream-test.py`, including command, working directory, exit code, and the full authored test source. The evidence records the exact runnable probe.

Runtime output showing ANSI escapes retained on redirected stderr

  • Captured execution of the narrow probe with stdout reporting TTY and stderr reporting non-TTY; the captured stderr representation contains `\x1b[31m` and `\x1b[0m`. The takeaway is that show(err=True) leaks ANSI escapes to redirected stderr.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment thread dimos/cli/art.py

# Sampled down the centre of the portal in the brand asset: pale yellow at the
# lintel, through cream and mint, to salmon at the threshold.
PORTAL: list[RGB] = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we have standardized theme in cli/theme and dimos/dimos/cli/dimos.tcss would be nice to unify more instead introduce a third place with colors

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

agree. Will look into this

@paul-nechifor

Copy link
Copy Markdown
Contributor

Personally, I dislike animations. On humancli, the animation hides the fact that setting up all the imports takes time. But here, what's the point of the animation?

This may be a bit controversial, but I think animations in TUIs are perceived as a software project not having its priorities right (like the infamous Ryan Dahl rant about unicode characters in terminals and the rest: https://tinyclouds.org/rant/ )

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

Labels

first-time-contributor PR opened by an author who had not previously committed to this repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants