Impeccable
bestofjs/bestofjs
A skill your agent uses when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a…
Guide for developing the OpenShell TUI — a ratatui-based terminal UI for the OpenShell platform.
$ npx skills add NVIDIA/OpenShell --skill tui-development -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install NVIDIA/OpenShell tui-development --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/tui-development .claude/skills/tui-development && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .claude/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-developmentType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add NVIDIA/OpenShell --skill tui-development -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install NVIDIA/OpenShell tui-development --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/tui-development .agents/skills/tui-development && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .agents/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add NVIDIA/OpenShell --skill tui-development -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install NVIDIA/OpenShell tui-development --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/tui-development .cursor/skills/tui-development && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .cursor/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/NVIDIA/OpenShell.git --path .agents/skills/tui-development--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add NVIDIA/OpenShell --skill tui-development -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install NVIDIA/OpenShell tui-development --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/tui-development .gemini/skills/tui-development && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .gemini/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install NVIDIA/OpenShell tui-developmentInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add NVIDIA/OpenShell --skill tui-development -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/tui-development .github/skills/tui-development && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .github/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add NVIDIA/OpenShell --skill tui-development -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install NVIDIA/OpenShell tui-development --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/NVIDIA/OpenShell.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/tui-development .opencode/skills/tui-development && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "tui-development" agent skill from https://github.com/NVIDIA/OpenShell/tree/main/.agents/skills/tui-development into .opencode/skills/tui-development/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tui-development", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
tui-developmentGuide for developing the OpenShell TUI — a ratatui-based terminal UI for the OpenShell platform.
Tui Development is an agent skill from NVIDIA/OpenShell, published by the product's own GitHub organization. Guide for developing the OpenShell TUI — a ratatui-based terminal UI for the OpenShell platform. Covers architecture, navigation, data fetching, theming, UX conventions, and development workflow. Trigger keywords - term, TUI, terminal UI, ratatui, openshell-tui, tui development, tui feature, tui bug.
Its SKILL.md is about 8.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Frontend & Design, covering Theming and dark mode. It works with NVIDIA AI Platform. The repository describes itself as: OpenShell is the safe, private runtime for autonomous AI agents. The licence is Apache-2.0.
9 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 834b79a. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
misecargohelmkubectlshFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use helm and kubectl, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Tui Development loads about 8.5k tokens when it runs. Until then it costs about 79 tokens; SKILL.md has 3,392 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from NVIDIA/OpenShell at commit 834b79a, republished under its Apache-2.0 licence (© NVIDIA). 3,392 words, ~8,478 tokens.
.claude/skills/tui-development/SKILL.md (or your agent's skills folder).Comprehensive reference for any agent working on the OpenShell TUI.
The OpenShell TUI is a ratatui-based terminal UI for the OpenShell platform. It provides a keyboard-driven interface for managing gateways, sandboxes, and logs — the same operations available via the openshell CLI, but with a live, interactive dashboard.
openshell term or mise run termcrates/openshell-tui/ratatui (workspace version) — uses frame.area() for the drawable terminal areacrossterm (workspace version) — terminal backend and event pollingtonic with TLS — gRPC client for the OpenShell gatewaytokio — async runtime for event loop, spawned tasks, and mpsc channelsopenshell-core — proto-generated types (OpenShellClient, request/response structs)openshell-bootstrap — gateway discovery (list_gateways())Theme struct — NVIDIA-branded green accents. Controlled by --theme flag, OPENSHELL_THEME env var, or auto-detection.The data model follows a strict hierarchy: Gateway > Workspace > Sandboxes/Providers/Settings > Logs.
Gateway (discovered via openshell_bootstrap::list_gateways())
├── Global Settings (fetched via GetGatewayConfig)
├── Global Policy indicator (fetched via ListSandboxPolicies global=true)
├── Workspaces (fetched via ListWorkspaces)
├── Provider Profiles (fetched via ListProviderProfiles, workspace-scoped)
├── Providers (fetched via ListProviders, workspace-scoped)
│ └── cached ProviderProfile (matched by type + workspace)
└── Sandboxes (fetched via ListSandboxes, workspace-scoped)
├── Policy (fetched via GetSandboxConfig)
├── Settings (effective settings with scope, from GetSandboxConfig)
├── Draft recommendations (fetched via GetDraftPolicy)
└── Logs (fetched via GetSandboxLogs + streamed via WatchSandbox)openshell_bootstrap::list_gateways(). Each gateway has a name, endpoint, local/remote flag, and source label.ListWorkspaces. The user cycles through workspaces with [w], or views all workspaces at once. The current workspace scopes provider and sandbox lists.ListProviderProfiles. Profiles are cached in a ProviderProfileCache keyed by (workspace, profile_id) and matched to providers by type. They provide category, credential metadata, endpoint/binary counts, and inference capability.ListProviders scoped to the current workspace. Each ProviderListEntry pairs a provider with its optional cached profile. The TUI supports profile-backed create, update, and delete operations.GetGatewayConfig and displayed in a tabbed pane alongside providers on the dashboard. Each setting is a registered key with a typed value (bool/int/string). Platform-admin access is required; PermissionDenied disables the pane.ListSandboxes with a periodic tick refresh.GetSandboxConfig, each with a scope (sandbox, global, or unset). Globally-managed settings are blocked from sandbox-level edits.GetSandboxLogs (500 lines), then live-tailed via WatchSandbox with follow_logs: true.The title bar always reflects this hierarchy, reading left-to-right from general to specific:
OpenShell v<version> │ Current Gateway: <name> [source] (<status>) │ Workspace: <name|all> │ <screen/context>Screen enum)Top-level layouts that own the full content area. Each has its own nav bar hints.
| Screen | Description | Module |
|---|---|---|
Splash | Boot screen shown on startup, auto-dismissed after 3 seconds | ui/splash.rs |
Dashboard | Gateway list (top) + providers/settings (middle) + sandbox table (bottom) | ui/dashboard.rs |
Sandbox | Single-sandbox view — metadata (top) + policy/settings/logs/drafts (bottom) | ui/sandbox_detail.rs, ui/sandbox_policy.rs, ui/sandbox_settings.rs, ui/sandbox_logs.rs, ui/sandbox_draft.rs |
Focus enum)Tracks which panel currently receives keyboard input.
| Focus | Screen | Description |
|---|---|---|
Gateways | Dashboard | Gateway list panel has input focus |
Providers | Dashboard | Provider list or global settings pane (depends on MiddlePaneTab) |
Sandboxes | Dashboard | Sandbox table panel has input focus |
SandboxPolicy | Sandbox | Policy viewer or settings table (depends on SandboxPolicyTab) |
SandboxLogs | Sandbox | Log viewer with structured rendering |
SandboxDraft | Sandbox | Draft policy recommendations list |
Two tab enums control which sub-view renders within a focus area:
MiddlePaneTab (Providers | GlobalSettings): toggles the middle dashboard pane between the provider list and the global settings table. Switched with [h/l].SandboxPolicyTab (Policy | Settings): toggles the sandbox bottom pane between the policy viewer and the sandbox settings table. Switched with [h].The top-level ui::draw() function (ui/mod.rs) handles the chrome (title bar, nav bar, command bar) and dispatches to the correct screen module:
match app.screen {
Screen::Splash => unreachable!(),
Screen::Dashboard => dashboard::draw(frame, app, chunks[1]),
Screen::Sandbox => draw_sandbox_screen(frame, app, chunks[1]),
}Within the Sandbox screen, sandbox_detail::required_height sizes the metadata and restart status pane to its contents. The remaining area dispatches based on focus and tab state:
match app.focus {
Focus::SandboxLogs => sandbox_logs::draw(frame, app, chunks[1]),
Focus::SandboxDraft => sandbox_draft::draw(frame, app, chunks[1]),
_ => match app.sandbox_policy_tab {
SandboxPolicyTab::Settings => sandbox_settings::draw(frame, app, chunks[1]),
SandboxPolicyTab::Policy => sandbox_policy::draw(frame, app, chunks[1]),
},
}On the dashboard, the middle pane dispatches by MiddlePaneTab:
match app.middle_pane_tab {
MiddlePaneTab::Providers => providers::draw(frame, app, chunks[1], mid_focused),
MiddlePaneTab::GlobalSettings => global_settings::draw(frame, app, chunks[1], mid_focused),
}Every frame renders four vertical regions:
┌─────────────────────────────────────────────┐
│ Title bar (1 row) — brand + gateway + context│
├─────────────────────────────────────────────┤
│ │
│ Main content (flexible) │
│ │
├─────────────────────────────────────────────┤
│ Nav bar (1 row) — context-sensitive key hints│
├─────────────────────────────────────────────┤
│ Command bar (1 row) — `:` command input │
└─────────────────────────────────────────────┘ >_ OpenShell v<version> | Current Gateway: openshell [local] (Healthy) | Workspace: default | Dashboard >_ OpenShell v<version> | Current Gateway: openshell [local] (Healthy) | Workspace: team-a | Sandbox: my-sandboxScreen in app.rs.src/ui/ with a pub fn draw(frame, app, area).ui/mod.rs.ui::draw() to dispatch to the new module.Focus variants if the screen has multiple panels.App for the new focus states.draw_nav_bar() for the new screen/focus combinations.Always grab a batch of initial data so the UI has content immediately, then attach streaming for live updates.
Logs example (spawn_log_stream in lib.rs):
Phase 1: GetSandboxLogs → 500 initial lines → send via Event::LogLines
Phase 2: WatchSandbox(follow_logs: true) → live tail → send via Event::LogLinesSandboxes: Fetched via ListSandboxes in a background collection-refresh task scheduled from the 2-second tick, scoped to the current workspace (or all workspaces). Follow next_page_token until empty so the dashboard reflects the complete collection. The NOTES column summarizes active ConfigurationInvalid readiness conditions as Invalid config before port forwards and clears the note on refresh after repair. Full diagnostics remain available through openshell sandbox get <name> -o json. Timed-out provisioning attempts show Provisioning timed out with cleanup pending or compute reclaimed, preserving port forwards. The sandbox detail pane wraps the full configuration error in its Notes field.
Providers: Fetched via ListProviders in the background collection-refresh task. Provider profiles are fetched per-workspace via ListProviderProfiles and cached in a ProviderProfileCache keyed by (workspace, profile_id). Follow each list RPC's next_page_token until empty.
Settings: Global settings are fetched via GetGatewayConfig on each tick. Sandbox settings are fetched alongside the sandbox policy via GetSandboxConfig and refreshed on each tick when viewing a sandbox.
Workspaces: The workspace list is fetched via ListWorkspaces in the background collection-refresh task, following next_page_token until empty.
Only one collection-refresh task may run at a time. Workspace and gateway changes abort the active task, and refresh results carry their gateway/workspace context so stale results are discarded.
All network calls must be spawned as async tasks via tokio::spawn. The event loop in lib.rs must remain responsive to keyboard input and rendering at all times.
Pattern:
// Background task sends data back via mpsc channel
let handle = tokio::spawn(async move {
let result = client.some_rpc(request).await;
let _ = tx.send(Event::SomeData(result));
});Show "Loading..." while async data is in flight (see sandbox_logs.rs — renders a loading message when filtered is empty and sandbox_log_lines is also empty).
Background tasks communicate with the event loop via mpsc::UnboundedSender<Event>. The EventHandler provides a sender() method to clone the transmit handle. There are many Event variants for different async results (log lines, create results, provider CRUD results, setting CRUD results, draft action results, forward warnings):
// In lib.rs
spawn_log_stream(&mut app, events.sender());
// In the spawned task
let _ = tx.send(Event::LogLines(lines));Global settings and global policy queries may return PermissionDenied when the user lacks platform-admin access. The TUI sets global_settings_access_denied / global_policy_access_denied flags to stop retrying these calls on subsequent ticks, and clears the corresponding UI state.
All gRPC calls use a 5-second timeout via tokio::time::timeout:
tokio::time::timeout(Duration::from_secs(5), client.health(req)).awaittheme.rs)Colors and styles are defined in crates/openshell-tui/src/theme.rs via the Theme struct. The TUI supports dark and light terminal backgrounds.
Theme mode is controlled by three mechanisms (highest priority first):
--theme dark|light|auto CLI flag on openshell termOPENSHELL_THEME environment variableCOLORFGBG env var (falls back to dark)The ThemeMode enum (Auto, Dark, Light) is resolved at startup via theme::detect() before entering raw mode.
theme::brand)| Constant | Value | Usage |
|---|---|---|
NVIDIA_GREEN | Color::Rgb(118, 185, 0) | Primary accent (dark theme) |
NVIDIA_GREEN_DARK | Color::Rgb(80, 140, 0) | Primary accent (light theme — darker for contrast) |
EVERGLADE | Color::Rgb(18, 49, 35) | Dark green — borders, title bar bg (dark theme) |
MAROON | Color::Rgb(128, 0, 0) | Pacman chase animation |
The Theme struct has 16 Style fields, accessed at runtime via app.theme:
| Field | Dark value | Light value | Usage |
|---|---|---|---|
text | White fg | Near-black fg | Default body text |
muted | White + DIM | Gray fg | Secondary info, separators |
heading | White + BOLD | Near-black + BOLD | Panel titles, names |
accent | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Selected row marker, source labels |
accent_bold | NVIDIA_GREEN + BOLD | NVIDIA_GREEN_DARK + BOLD | Brand text, command prompt |
selected | BOLD only | BOLD only | Selected row emphasis |
border | EVERGLADE fg | Light sage fg | Unfocused panel borders |
border_focused | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Focused panel borders |
status_ok | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Healthy, INFO, Ready |
status_warn | Yellow fg | Dark yellow fg | Degraded, WARN, Provisioning, Starting |
status_err | Red fg | Dark red fg | Unhealthy, ERROR |
key_hint | NVIDIA_GREEN fg | NVIDIA_GREEN_DARK fg | Keyboard shortcut labels |
log_cursor | EVERGLADE bg | Light green bg | Selected log line highlight |
claw | MAROON + BOLD | MAROON + BOLD | Pacman animation |
title_bar | White on EVERGLADE + BOLD | Near-black on light green + BOLD | Title bar strip |
badge | Black on NVIDIA_GREEN + BOLD | White on NVIDIA_GREEN_DARK + BOLD | Notification badges |
The Theme is stored on App and accessed via a local alias:
fn draw_my_widget(frame: &mut Frame<'_>, app: &App, area: Rect) {
let t = &app.theme;
frame.render_widget(
Paragraph::new(Span::styled("Hello", t.text)),
area,
);
}For functions that don't take &App (e.g., detail popups, helpers), pass &Theme as a parameter:
fn draw_detail_popup(frame: &mut Frame<'_>, data: &MyData, area: Rect, theme: &Theme) {
let t = theme;
// ...
}▌ left-border marker on the selected row. Active gateway also gets a green ● dot.border to border_focused style.│ characters between title bar segments and nav bar sections."sandbox" source renders in accent (green), "gateway" in muted.Always show a y/n confirm dialog before delete, stop, or other irreversible operations.
Delete sandbox 'my-sandbox'? [y] Confirm [Esc] CancelThe confirm_delete flag in App gates destructive key handling — while true, only y, n, and Esc are processed.
TUI actions should parallel openshell CLI commands so users have familiar mental models:
| CLI Command | TUI Equivalent |
|---|---|
openshell sandbox list | Sandbox table on Dashboard |
openshell sandbox delete <name> | [d] on sandbox detail, then [y] to confirm |
openshell sandbox create | [c] on sandbox panel to open create form |
openshell sandbox connect | [s] on sandbox policy view to launch SSH shell |
openshell logs <name> | [l] on sandbox detail to open log viewer |
openshell provider list | Provider table on Dashboard (middle pane) |
openshell provider create | [c] on provider panel |
openshell status | Status in title bar + gateway list |
When adding new TUI features, check what the CLI offers and maintain consistency.
The create form parses the optional Command field as shell words before starting creation. Preserve the parsed argument vector through post-create execution and shell-escape each argument at the SSH boundary. Quoting groups arguments; expansions and operators remain literal unless the user explicitly invokes a shell such as sh -c. Invalid quoting must leave the form open with an error and must not queue sandbox creation.
Any scrollable content (logs, future long lists) should follow the k9s autoscroll pattern:
f or G re-enables — jump to bottom and resume following● FOLLOWING (green) or ○ PAUSED (yellow) in the panel footerScrollUp/ScrollDown events move by 3 lines and respect autoscroll state[current/total] in the panel footerState is tracked via log_autoscroll: bool on App. The scroll_logs(delta) method handles both keyboard and mouse input uniformly.
When content can exceed the viewport width (log lines, field lists, etc.):
…. This keeps density high and avoids wrapping that breaks the 1-line-per-entry model.Esc or Enter closes it. Track the open state via Option<usize> index.This pattern should be reused for any future view with potentially long entries.
| Key | Action |
|---|---|
j / Down | Move selection down |
k / Up | Move selection up |
g | Jump to top (logs), disables autoscroll |
G | Jump to bottom (logs), re-enables autoscroll |
f | Follow / re-enable autoscroll (logs) |
Tab / BackTab | Switch between panels on Dashboard |
Enter | Select / drill into item; open detail popup in logs |
Esc | Go back one level |
q | Quit (from any screen) |
Ctrl+C | Force quit |
All actions are accessible via keyboard shortcuts displayed in the nav bar. The nav bar is context-sensitive — it shows different hints depending on the current screen and focus state. Mouse scrolling is supported as a convenience but never required — every action must have a keyboard equivalent.
: enters command mode (like vim). The command bar renders at the bottom with a green : prompt and a block cursor. Currently supports:
:q / :quit — exit the applicationEsc returns to normal mode. Enter executes the command.
Dashboard (Gateways focus):
[Tab] Switch Panel [Enter] Select [j/k] Navigate │ [:] Command [q] Quit
Dashboard (Providers focus):
[Tab] Switch Panel [h/l] Switch Tab [j/k] Navigate [Enter] Detail [c] Create [u] Update [d] Delete [w] Workspace │ [:] Command [q] Quit
Dashboard (Global Settings focus):
[Tab] Switch Panel [h/l] Switch Tab [j/k] Navigate [Enter] Edit [d] Delete │ [:] Command [q] Quit
Dashboard (Sandboxes focus):
[Tab] Switch Panel [j/k] Navigate [Enter] Select [c] Create Sandbox [w] Workspace │ [:] Command [q] Quit
Sandbox (Policy focus):
[h] Switch Tab [j/k] Scroll [g/G] Top/Bottom [s] Shell [l] Logs [r] Rules [d] Delete │ [Esc] Back [q] Quit
Sandbox (Settings focus):
[h/l] Switch Tab [j/k] Navigate [Enter] Edit [d] Delete │ [Esc] Back [q] Quit
Sandbox (Logs focus):
[j/k] Navigate [Enter] Detail [g/G] Top/Bottom [f] Follow [s] Source: <filter> [y] Copy [Y] Copy All [v] Select [r] Rules │ [Esc] Policy [q] Quit
Sandbox (Draft focus):
[j/k] Navigate [Enter] Detail [a] Approve [x] Reject [A] Approve All [p] Policy [l] Logs │ [Esc] Back [q] Quit
| File | Purpose |
|---|---|
crates/openshell-tui/Cargo.toml | Crate manifest — dependencies on openshell-core, openshell-bootstrap, ratatui, crossterm, tonic, tokio |
crates/openshell-tui/src/lib.rs | Entry point. Event loop, background collection refresh (spawn_list_refresh), gRPC calls (refresh_global_settings, spawn_log_stream, handle_sandbox_delete), gateway switching, mTLS channel building, provider CRUD spawners, settings CRUD spawners, draft approval spawners |
crates/openshell-tui/src/app.rs | App state struct, Screen/Focus/InputMode/LogSourceFilter/MiddlePaneTab/SandboxPolicyTab enums, LogLine/GatewayEntry/GlobalSettingEntry/SandboxSettingEntry/ProviderListEntry/ProviderDetailView structs, create sandbox/provider form state, all key handling logic |
crates/openshell-tui/src/event.rs | Event enum (Key, Mouse, Tick, Redraw, Resize, LogLines, ListRefreshCompleted, CreateResult, ProviderCreateResult, ProviderDetailFetched, ProviderUpdateResult, ProviderDeleteResult, DraftActionResult, GlobalSettingsFetched, GlobalSettingSetResult, GlobalSettingDeleteResult, SandboxSettingSetResult, SandboxSettingDeleteResult, ForwardWarnings), EventHandler with mpsc channels and crossterm polling |
crates/openshell-tui/src/theme.rs | colors module (NVIDIA_GREEN, EVERGLADE, BG, FG) and styles module (all Style constants) |
crates/openshell-tui/src/clipboard.rs | Clipboard copy support for log lines |
crates/openshell-tui/src/ui/mod.rs | Top-level draw() dispatcher, draw_title_bar (with workspace display), draw_nav_bar, draw_command_bar, screen routing, shared setting-edit overlay, modal helpers |
crates/openshell-tui/src/ui/dashboard.rs | Dashboard screen — 3-pane vertical layout: gateway list (25%) + provider/settings middle pane (25%) + sandbox table (50%) |
crates/openshell-tui/src/ui/providers.rs | Provider list table with profile-aware columns: Name, Category, Type, Credentials, Workspace |
crates/openshell-tui/src/ui/global_settings.rs | Global settings table: Key, Type, Value. Includes edit overlay, confirm-set, and confirm-delete popups |
crates/openshell-tui/src/ui/sandboxes.rs | Reusable sandbox table widget with columns: Name, Status, Created, Age, Image, Workspace, Notes |
crates/openshell-tui/src/ui/sandbox_detail.rs | Sandbox metadata view — name, status, image, created, age, restart policy/status, providers, policy version |
crates/openshell-tui/src/ui/sandbox_policy.rs | Policy viewer — rendered policy lines with scroll support, tab title |
crates/openshell-tui/src/ui/sandbox_settings.rs | Sandbox settings table: Key, Type, Value, Scope. Includes edit overlay and confirm popups |
crates/openshell-tui/src/ui/sandbox_logs.rs | Structured log viewer — timestamp, source, level, target, message, key=value fields, scroll position, source filter, visual selection mode, clipboard copy |
crates/openshell-tui/src/ui/sandbox_draft.rs | Draft policy recommendations — chunk list, detail popup, approve/reject/approve-all flows |
crates/openshell-tui/src/ui/create_sandbox.rs | Create sandbox modal form with name, image, command, providers, ports |
crates/openshell-tui/src/ui/create_provider.rs | Create provider modal, provider detail popup, update provider form |
crates/openshell-tui/src/ui/splash.rs | Splash/boot screen |
lib.rs (event loop, gRPC, async tasks, capability fetch)
├── app.rs (state + key handling + tab/workspace logic)
├── event.rs (Event enum + EventHandler)
├── clipboard.rs (copy support)
├── theme.rs (colors + styles)
└── ui/
├── mod.rs (draw dispatcher, chrome, shared overlays)
├── splash.rs (boot screen)
├── dashboard.rs (3-pane layout: gateways + middle + sandboxes)
├── providers.rs (provider list with profile awareness)
├── global_settings.rs (settings table + edit/confirm overlays)
├── sandboxes.rs (sandbox table widget)
├── sandbox_detail.rs (metadata view)
├── sandbox_policy.rs (policy viewer)
├── sandbox_settings.rs (sandbox settings table + overlays)
├── sandbox_logs.rs (log viewer + visual selection)
├── sandbox_draft.rs (draft recommendations)
├── create_sandbox.rs (create sandbox modal)
└── create_provider.rs (create/detail/update provider modals)openshell-tui cannot depend on openshell-cli — this would create a circular dependency. TLS channel building for gateway switching is done directly in lib.rs using tonic::transport primitives (Certificate, Identity, ClientTlsConfig, Endpoint).connect_to_gateway() reads gateway metadata to determine the auth mode, then builds an EdgeAuthInterceptor (bearer token for OIDC, noop for mTLS).~/.config/openshell/gateways/<name>/mtls/ (ca.crt, tls.crt, tls.key).openshell_bootstrap::oidc_token::load_oidc_token() and checked for expiry.Proto types come from openshell-core which generates them from OUT_DIR via include!. They are not checked into the repo. Import paths look like:
use openshell_core::proto::openshell_client::OpenShellClient;
use openshell_core::proto::{
all_workspaces_selector, workspace_selector, GetSandboxLogsRequest,
ListSandboxesRequest, ...
};DeleteSandboxRequest uses name for the primary sandbox and an explicit
workspace selector:let req = openshell_core::proto::DeleteSandboxRequest {
name: sandbox_name,
workspace_scope: Some(workspace_selector(workspace)),
allow_missing: true,
..Default::default()
};DeletionOutcome: distinguish Accepted (cleanup
pending), Completed, and AlreadyAbsent. Treat unspecified or unknown
outcomes as unconfirmed, not completed.WatchSandboxRequest has extra fields beyond what you might need — always use ..Default::default():let req = openshell_core::proto::WatchSandboxRequest {
sandbox: sandbox_name,
follow_status: false,
follow_logs: true,
follow_events: false,
log_tail_lines: 0,
workspace_scope: Some(workspace_selector(workspace)),
..Default::default()
};SandboxLogLine proto fields: sandbox_id, event_time (Option<prost_types::Timestamp>), level, target, message, source, fields (HashMap<String, String>).workspace_scope: Option<WorkspaceSelector>. Select one workspace with
Some(workspace_selector(name)). Collection list requests that explicitly
support cross-workspace access also accept
Some(all_workspaces_selector()); do not use that marker on other requests.GetSandboxLogsRequest fields: sandbox, lines (u32), since_time (Option<prost_types::Timestamp>),
sources (Vec<String>), min_level (String), workspace_scope.ListSandboxesRequest fields: page_size (i32), page_token (String),
label_selector (String), workspace_scope.ListProvidersRequest fields: page_size (i32), page_token (String),
workspace_scope.ListWorkspacesRequest fields: page_size (i32), page_token (String),
label_selector (String).next_page_token. Continue with the same
request parameters and that token until it is empty; changing filters or
scope invalidates the token.UpdateConfigRequest fields include sandbox (String, canonical sandbox name),
setting_key, setting_value, delete_setting (bool), global (bool), and
workspace_scope. Sandbox-scoped updates require canonical sandbox and a
named selector; gateway-global updates leave sandbox empty and
workspace_scope as None.default workspace. An omitted selector is not an implicit default.All gRPC calls use a 5-second timeout:
tokio::time::timeout(Duration::from_secs(5), client.health(req)).awaitThe connect timeout for gateway switching is 10 seconds with HTTP/2 keepalive at 10-second intervals.
[l] on sandbox detail → pending_log_fetch = truespawn_log_stream()cancel_log_stream()tokio::spawn task: fetches initial 500 lines, then streams via WatchSandboxEvent::LogLines and are appended to app.sandbox_log_linesEsc or navigates away (handle is .abort()ed)Enter → pending_gateway_switch = Some(name)handle_gateway_switch()connect_to_gateway() (mTLS or OIDC depending on gateway metadata)app.client is replaced with a new intercepted clientreset_sandbox_state() clears all sandbox/log/draft/policy dataspawn_list_refresh() starts the cancellable workspace/provider/sandbox refresh taskstatus_text shows the errorOn launch, before the event loop starts:
refresh_gateway_list() — discover gateways from diskspawn_list_refresh() for workspaces, providers, and sandboxes[w] on the providers or sandboxes panel → cycle_workspace() advances through discovered workspace names, then "all"pending_workspace_refresh = true is set, cursor indices are resetspawn_list_refresh() with the new workspace scope[Enter] on a setting → edit overlay opens (bool types toggle inline and jump to confirmation)[Enter] opens a confirmation popup → [y] fires the pending flagspawn_set_global_setting() or spawn_set_sandbox_setting() → UpdateConfig RPC[d] on a setting with a value → confirmation popup → spawn_delete_*_setting() → UpdateConfig with delete_setting: trueFor sandbox settings, globally-managed entries (scope = global) are blocked from editing or deletion at the sandbox level.
# Build the crate
cargo build -p openshell-tui
# Run the TUI against the active gateway
mise run term
# Run with cargo-watch for hot-reload during development
mise run term:dev
# Format
cargo fmt -p openshell-tui
# Lint
cargo clippy -p openshell-tuiFollow Choose Verification for the Change in CONTRIBUTING.md. Select format, lint, and tests for the affected TUI behavior and its dependencies. Use mise run pre-commit when its broader scope is warranted.
If you change sandbox or server code that affects the backend, restart or redeploy the gateway for the compute platform you are using.
For Docker-backed local development:
mise run gateway:dockerFor Kubernetes Helm deployments:
helm upgrade --install openshell deploy/helm/openshell --namespace openshellFor Kubernetes, pick up new sandbox images after changing sandbox code by deleting the pod manually so it gets recreated:
kubectl delete pod <pod-name> -n <namespace>openshell-core for available RPCs and message types.lib.rs following the existing pattern (timeout wrapper, error handling, state update).pending_* flag to App and handle it in the event loop.Event variants.Event in event.rs.match events.next().await block in lib.rs.App state as needed from the event data.© NVIDIA, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/tui-development of NVIDIA/OpenShell.
Open the folder on GitHubat commit 834b79a
Tui Development next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Tui Development this skillNVIDIA/OpenShell | 15k | — | ~8.5k | Automated safety check: Pass | Apache-2.0 | |
| Impeccablebestofjs/bestofjs | 3.1k | 27 repos | ~2.6k | Automated safety check: Pass | MIT | |
| Figma Design System Builderwarpdotdev/warp | 65k | 2 repos | ~4.4k | Automated safety check: Pass | AGPL-3.0 | |
| Tailwindcss Developmentanonaddy/anonaddy | 4.9k | 10 repos | ~865 | Automated safety check: Pass | MIT | |
| UI StylingOhh-889/skyroc | 795 | 13 repos | ~2.5k | Automated safety check: Pass | MIT | |
| MCP Developmentcoollabsio/coolify | 63k | 1 repos | ~949 | Automated safety check: Pass | MIT |
bestofjs/bestofjs
A skill your agent uses when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a…
warpdotdev/warp
Builds or updates a design system in Figma from a codebase in ordered phases: discovery, variables and tokens, components, theming and documentation, with checkpoints.
anonaddy/anonaddy
Always invoke when the user's message includes 'tailwind' in any form.
Ohh-889/skyroc
Create beautiful, accessible user interfaces with shadcn/ui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs.
coollabsio/coolify
A skill your agent uses for Laravel MCP development. An agent skill from coollabsio/coolify.
saoudi-h/solar-icons
UI/UX design intelligence for web and mobile. An agent skill from saoudi-h/solar-icons.
NVIDIA/OpenShell
Maintain and validate OpenShell's build-only Windows MSVC lane for x64 and ARM64.
NVIDIA/OpenShell
Create GitHub issues using the gh CLI. An agent skill from NVIDIA/OpenShell.
NVIDIA/OpenShell
Create GitHub pull requests using the gh CLI. An agent skill from NVIDIA/OpenShell.
NVIDIA/OpenShell
Debug inference clients that use an attached provider and its native endpoint, including hosted APIs and host-local Ollama, vLLM, SGLang, TRT-LLM, LM Studio, or NIM.
NVIDIA/OpenShell
Debug why an OpenShell gateway deployment is unhealthy, unreachable, or unable to create sandboxes.
NVIDIA/OpenShell
Validate and monitor OpenShell GitHub issues and PRs using the gator: state machine.
Works with
Categories
Guide for developing the OpenShell TUI — a ratatui-based terminal UI for the OpenShell platform. Tui Development is an agent skill from NVIDIA/OpenShell, published by the product's own GitHub organization. Guide for developing the OpenShell TUI — a ratatui-based terminal UI for the OpenShell platform.
Tui Development fits situations like: keywords - term; tui development.
Run `npx skills add NVIDIA/OpenShell --skill tui-development -a claude-code`. Or copy the skill folder (.agents/skills/tui-development in NVIDIA/OpenShell) into .claude/skills/tui-development in your project. Claude Code loads it when a task matches its description.
Run `npx skills add NVIDIA/OpenShell --skill tui-development -a codex`. Or copy the skill folder (.agents/skills/tui-development in NVIDIA/OpenShell) into .agents/skills/tui-development in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add NVIDIA/OpenShell --skill tui-development -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/tui-development, .gemini/skills/tui-development, .github/skills/tui-development and .opencode/skills/tui-development in your project.
Going by SKILL.md and its folder, Tui Development needs the command-line tools its instructions call (mise, cargo, helm, kubectl and sh).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Tui Development is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.5k tokens (SKILL.md is roughly 34k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Tui Development: Impeccable (bestofjs/bestofjs, 3.1k stars), Figma Design System Builder (warpdotdev/warp, 65k stars), Tailwindcss Development (anonaddy/anonaddy, 4.9k stars) and UI Styling (Ohh-889/skyroc, 795 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
NVIDIA (a GitHub organization, an official publisher) maintains it in NVIDIA/OpenShell, which has 15,188 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 7, 2026.
Source: NVIDIA/OpenShell on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.