Clara-CLI.hub Reference

Clara-CLI.hub is a static Next.js application that renders interactive command builders for CLI harnesses. All data lives in data/registry.json — a single JSON file that describes every supported CLI, its commands, and their parameters. No runtime API calls. No backend. No authentication layer.

The playground simulates terminal output locally — no CLI binary is invoked at any point. Output strings are defined per-command in the registry and rendered through a typed colorizer pipeline in components/playground/Terminal.tsx.

Key invariants

Every slug in the registry is globally unique. Command names within a single entry are unique. Parameter flags follow POSIX long-form convention (--flag-name) and must be globally unique within a command. The registry is imported at build time via TypeScript's resolveJsonModule — changes require a rebuild.

Registry Schema

data/registry.json is an array of CLIEntry objects. The file is the single source of truth for all catalog and playground data. Schema follows the TypeScript types defined in lib/types.ts.

Minimal valid entry

{ "slug": "gimp", "name": "GIMP", "emoji": "🎨", "domain": "Image Editing", "category": "creative", "command": "cli-anything-gimp", "testCount": 107, "commandCount": 38, "commands": [ { "name": "project new", "description": "Create a new GIMP project", "params": [ { "flag": "--width", "label": "Width", "type": "number", "required": true, "defaultValue": "1920", "placeholder": "e.g. 1920" } ], "simulatedOutput": "✓ Created project: poster.json\n size: 1920 × 1080 px" } ] }

Validation rules

The Vitest suite in __tests__/registry.test.ts enforces these constraints at CI time. Any entry that violates them will cause tests to fail.

✓ slug — kebab-case, globally unique, no spaces ✓ name — human-readable, title-case preferred ✓ emoji — single Unicode emoji (rendered in catalog card) ✓ domain — short descriptor, ≤ 28 chars ✓ category — must be one of the Category union (see Type Reference) ✓ command — the base CLI-Anything binary name, no args ✓ testCount — integer ≥ 0 ✓ commandCount — integer ≥ 1 (must have at least one command) ✓ commands — non-empty array; each command has ≥ 0 params ✓ param.flag — POSIX --long-form; unique within parent command ✓ param.type — "string" | "number" | "boolean" ✓ simulatedOutput — newline-separated string; supports colorizer tokens

Type Reference

All types are defined in lib/types.ts and imported by consumers.

CLIEntry

FieldTypeDescription
slug*stringURL-safe identifier. Used as the ?cli= query param in playground URLs.
name*stringDisplay name shown in catalog cards and playground sidebar.
emoji*stringSingle emoji rendered in the colored icon square on catalog cards.
domain*stringShort category descriptor shown below the name (e.g. 'Image Editing').
category*CategoryEnum value for filter pills. Must match the Category union exactly.
command*stringBase binary name — prepended to all assembled command strings.
testCount*numberDisplayed on catalog card (green badge). Purely informational.
commandCount*numberDisplayed on catalog card (indigo badge). Purely informational.
commands*CLICommand[]Ordered list of supported commands. First item is selected by default in playground.

CLICommand

FieldTypeDescription
name*stringCommand group + verb, e.g. 'project new'. Shown in the command selector dropdown.
description*stringOne-line description shown below the command selector in the builder.
params*CLIParam[]Ordered list of parameters rendered as form inputs. Can be empty.
simulatedOutput*stringNewline-separated string rendered in the terminal panel. See Simulation Protocol.

CLIParam

FieldTypeDescription
flag*stringPOSIX long flag, e.g. '--width'. Prepended to the value in the assembled command.
label*stringHuman-readable input label shown in the form.
type*"string" | "number" | "boolean"Controls input rendering: text, number, or checkbox.
required*booleanRequired params are always included. Optional params are omitted if empty.
defaultValuestringPre-filled value. Included in command string if user leaves input unchanged.
placeholderstringInput placeholder text. Does not affect the assembled command.

Category

type Category = | 'creative' // GIMP, Blender, Inkscape, Audacity | 'productivity' // LibreOffice, OBS Studio | 'ai-ml' // Ollama, ComfyUI | 'data' | 'network' | 'communication' | 'diagramming'

Simulation Protocol

The terminal panel renders output through a two-stage pipeline: a timing sequencer inhooks/usePlayground.ts and a line colorizer in components/playground/Terminal.tsx.

Timing sequencer

When the user clicks Run, the hook constructs a synthetic preamble of init lines followed by a hard freeze, then streams the simulatedOutput lines. Total runtime is enforced to a minimum of 7 seconds.

Phase 1 — Command display ~50ms Phase 2 — Init lines ~420–740ms per line → initializing <harness>... → runtime loaded · awaiting execution → running: <command name> Phase 3 — FREEZE 2400ms (silent, cursor blinks) Phase 4 — Output lines ~70–220ms per line Phase 5 — Completion hold max(0, 7000 - elapsed) ms

Line colorizer rules

Each line of simulatedOutput is classified by its prefix and rendered in a distinct color. The rules are applied in order — first match wins.

Prefix / pattern CSS class Color ───────────────────────────────────────────────── Starts with "$" .term-cmd #93c5fd (blue) Starts with "✓" .term-ok #4ade80 (terminal green) Starts with " " (2sp) .term-muted #64748b (muted gray) Starts with "{" .term-json #fbbf24 (amber) All other lines .term-text #e2e8f0 (off-white)

Command assembly

The preview string and the echoed $ command line are assembled by buildCommand() in the hook:

function buildCommand(cli, cmd, params): string { const parts = [cli.command, cmd.name] cmd.params.forEach(p => { const val = params[p.flag] ?? p.defaultValue ?? '' if (val) parts.push(`${p.flag} "${val}"`) }) return parts.join(' ') } // Example output: // cli-anything-gimp project new --width "1920" --height "1080" --output "poster.json"

Harness Spec

Clara-CLI.hub does not execute any binary. A "harness" in this context is purely a data entry: a structured description of what a real CLI tool can do, enough to assemble valid command strings and display plausible output.

What makes a good harness entry

✓ Cover the most-used 4–8 commands, not all edge cases ✓ Use real flag names from the tool's actual --help output ✓ simulatedOutput should mirror real terminal output format exactly ✓ Required params should have sensible defaultValues ✓ testCount / commandCount should reflect the real harness if known ✗ Do not invent flags that don't exist in the real tool ✗ Do not use abbreviations for flags (--w instead of --width) ✗ Do not include runtime paths or machine-specific values

simulatedOutput conventions

# Success output (starts with ✓) ✓ Created project: poster.json size: 1920 × 1080 px color-mode: RGB # JSON output (starts with {) {"status":"ok","file":"poster.json","width":1920} # Progress lines (leading spaces → muted) → loading assets... → processing 3 layers

Extending the Registry

Add a new CLI

Edit data/registry.json directly. Add a new object to the root array following the schema above. The catalog, filter pills, and playground sidebar update automatically at next build.

# 1. Add your entry to data/registry.json # 2. Run the test suite — it validates schema integrity pnpm test # 3. Start dev server to visually verify pnpm dev # 4. Build to confirm no TypeScript errors pnpm build

Add a command to an existing CLI

Locate the entry by slug and append to its commands array. The command selector dropdown in the playground populates directly from this array in order — place frequently-used commands first.

Updating testCount / commandCount

These fields are informational badges. They are not derived from the registry itself and must be set manually. If you add commands, increment commandCount.testCount reflects the real upstream harness test suite count if known; otherwise use 0.

Architecture

Data flow

data/registry.json ↓ (static import, resolveJsonModule) lib/registry.ts → getRegistry(): CLIEntry[] → getCLIBySlug(slug): CLIEntry | undefined ↓ app/page.tsx (Server Component — passes clis to CLIGrid) app/catalog/page.tsx (Server Component — passes clis to CLIGrid) app/playground/page.tsx (Server Component — passes clis to PlaygroundClient)

Component boundaries

Server Components (no 'use client'): app/layout.tsx Root layout, fonts, metadata app/page.tsx Landing page shell app/catalog/page.tsx Catalog page shell app/playground/page.tsx Suspense wrapper app/docs/page.tsx Docs shell Client Components ('use client'): components/nav/Navbar.tsx Scroll state, active link components/catalog/CLIGrid.tsx Filter + search state components/catalog/FilterBar.tsx Category pill state components/playground/ PlaygroundClient.tsx ?cli= param, layout SoftwareSidebar.tsx CLI selection CommandBuilder.tsx Command + param inputs Terminal.tsx Line colorizer, cursor hooks/usePlayground.ts All playground state components/InteractiveBg.tsx rAF mouse + orb parallax components/TerminalParticles.tsx Canvas scroll particles

Rendering strategy

All routes → static export (○) / pre-rendered at build time /catalog pre-rendered at build time /playground pre-rendered (Suspense boundary for client) /docs pre-rendered at build time No dynamic routes. No API routes. No server-side rendering. ISR is available but unused in Phase 1 (all data is bundled).

File size targets

Page First Load JS ───────────────────────────── / ~100 kB shared /catalog +catalog chunk /playground +playground chunk /docs +docs chunk Shared chunk includes: React, Next router, fonts, globals