Source profileQuality 91/100

PostHog/wizard/.claude/skills/ink-tui/SKILL.md

ink-tui-wizard

Build terminal user interfaces (TUIs) using Ink (React for CLIs) and @inkjs/ui with a reactive, session-driven wizard pattern. Use when creating interactive CLI installation wizards, setup flows, or multi-step terminal applications in Node.js/TypeScript. Covers reactive screen resolution, declarative flow pipelines, overlay interrupts, session state management, Ink components, Flexbox terminal layout, and graceful degradation across terminal environments.

Source repository stars
178
Declared platforms
1
Static risk flags
1
Last source update
2026-08-06
Source checked
2026-08-06

Decision brief

What it does—and where it fits

Build beautiful, interactive terminal wizard interfaces using Ink (React for CLIs).

Best for

  • Creating multi-step CLI installation or setup wizards
  • Building reactive, session-driven terminal interfaces
  • Adding real-time progress, spinners, or status displays to CLI tools

Not for

  • Tasks that require unconfirmed production actions or broad system permissions.
  • Environments where the pinned source and install steps cannot be inspected.

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeDeclaredSource recordInstall path and trigger
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/PostHog/wizard --skill ".claude/skills/ink-tui"
Safe inspection promptEditorial

Inspect the Agent Skill "ink-tui-wizard" from https://github.com/PostHog/wizard/blob/00ae80a9898909cdce1ff45964fcaedaa836dd90/.claude/skills/ink-tui/SKILL.md at commit 00ae80a9898909cdce1ff45964fcaedaa836dd90. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    When to use this skill

    Creating multi-step CLI installation or setup wizards

    Creating multi-step CLI installation or setup wizardsBuilding reactive, session-driven terminal interfacesAdding real-time progress, spinners, or status displays to CLI tools
  2. 02

    Core architecture

    This skill follows a reactive session-driven pattern: the rendered screen is a pure function of session state. Business logic sets state through store setters. The router derives which screen should be active. Nobody imperatively pushes screens around.

    WizardSession (src/lib/wizard-session.ts) — single source of truth for all wizard decisionsWizardRouter (src/ui/tui/router.ts) — declarative flow pipelines with isComplete predicates per screenWizardStore (src/ui/tui/store.ts) — nanostores-backed reactive store with explicit setters that trigger React re-renders via useSyncExternalStore
  3. 03

    Key concepts

    WizardSession (src/lib/wizard-session.ts) — single source of truth for all wizard decisions

    WizardSession (src/lib/wizard-session.ts) — single source of truth for all wizard decisionsWizardRouter (src/ui/tui/router.ts) — declarative flow pipelines with isComplete predicates per screenWizardStore (src/ui/tui/store.ts) — nanostores-backed reactive store with explicit setters that trigger React re-renders via useSyncExternalStore
  4. 04

    Adding a screen

    1. Create the component in src/ui/tui/screens/ 2. Add to Screen enum in router.ts 3. Add a FlowEntry to the flow array with an isComplete predicate 4. Register in screen-registry.tsx

    Create the component in src/ui/tui/screens/Add to Screen enum in router.tsAdd a FlowEntry to the flow array with an isComplete predicate
  5. 05

    Adding store state

    Two patterns depending on the data:

    Session state (affects screen resolution): add field to WizardSession, add setter to WizardStore that calls emitChange(), add method to WizardUI interface + both implementationsObservation state (display-only, e.g., agent progress): add private atom to WizardStore, add getter + setter, add method to WizardUI interface + both implementationsTwo patterns depending on the data:

Permission review

Static risk signals and limitations

Reads files

low · line 60

The documentation asks the agent to read local files, directories, or repositories.

Read each primitive's source file for its current props interface.

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score91/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars178SourceRepository attention, not individual Skill quality
Compatibility1 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
PostHog/wizard
Skill path
.claude/skills/ink-tui/SKILL.md
Commit
00ae80a9898909cdce1ff45964fcaedaa836dd90
License
MIT
Collected
2026-08-06
Default branch
main
View the original SKILL.md

Ink TUI Wizard Skill

Build beautiful, interactive terminal wizard interfaces using Ink (React for CLIs).

Ink is the dominant Node.js TUI framework — used by Claude Code (Anthropic), Gemini CLI (Google), GitHub Copilot CLI, Cloudflare Wrangler, Shopify CLI, Prisma, and many others.

When to use this skill

  • Creating multi-step CLI installation or setup wizards
  • Building reactive, session-driven terminal interfaces
  • Adding real-time progress, spinners, or status displays to CLI tools
  • Any Node.js/TypeScript CLI that needs more than sequential prompts

Core architecture

This skill follows a reactive session-driven pattern: the rendered screen is a pure function of session state. Business logic sets state through store setters. The router derives which screen should be active. Nobody imperatively pushes screens around.

See references/ARCHITECTURE.md for the full reactive architecture: session, router, store, screen resolution, overlays, and data flow.

Key concepts

  • WizardSession (src/lib/wizard-session.ts) — single source of truth for all wizard decisions
  • WizardRouter (src/ui/tui/router.ts) — declarative flow pipelines with isComplete predicates per screen
  • WizardStore (src/ui/tui/store.ts) — nanostores-backed reactive store with explicit setters that trigger React re-renders via useSyncExternalStore
  • WizardUI (src/ui/wizard-ui.ts) — interface bridging business logic to store; implemented by InkUI (TUI) and LoggingUI (CI)
  • Screen registry (src/ui/tui/screen-registry.tsx) — factory function mapping screen names to components (App.tsx never changes)
  • Services (src/ui/tui/services/) — injected into screens via props (no dynamic imports in React components)
  • Overlays — interrupt stack for outage/error modals, orthogonal to flows

Adding a screen

  1. Create the component in src/ui/tui/screens/
  2. Add to Screen enum in router.ts
  3. Add a FlowEntry to the flow array with an isComplete predicate
  4. Register in screen-registry.tsx

No other files change.

Adding store state

Two patterns depending on the data:

  • Session state (affects screen resolution): add field to WizardSession, add setter to WizardStore that calls emitChange(), add method to WizardUI interface + both implementations
  • Observation state (display-only, e.g., agent progress): add private atom to WizardStore, add getter + setter, add method to WizardUI interface + both implementations

Read store.ts for examples of both patterns.

Layout primitives

The project has reusable layout primitives in src/ui/tui/primitives/. Always use these instead of building from scratch.

All primitives are barrel-exported from src/ui/tui/primitives/index.ts. See references/PRIMITIVES.md for the catalog. Read each primitive's source file for its current props interface.

Shared style constants (Colors, Icons, HAlign, VAlign) live in src/ui/tui/styles.ts.

Playground: Run pnpm try --playground to see all primitives in action. When you add a new primitive, also add a demo for it under src/ui/tui/playground/demos/ and register it in PlaygroundApp.tsx. The playground is the single place a contributor (or future agent) can see every primitive rendered with realistic props — a primitive that's not in the playground is invisible to anyone who didn't write it, and the next person solving the same UI problem will build a parallel component instead of reusing yours.

Enums everywhere

All state comparisons use TypeScript enums — no string literals. See the source files for current values:

  • Screen, Overlay, Flow — in router.ts
  • RunPhase, OutroKind — in wizard-session.ts
  • TaskStatus — in wizard-ui.ts

Key dependencies

ink                   # Core: React renderer for terminals (uses Yoga for Flexbox)
react                 # Peer dependency
@inkjs/ui             # Official component library: Select, TextInput, Spinner,
                      # ProgressBar, ConfirmInput, MultiSelect, Badge,
                      # StatusMessage, Alert, OrderedList, UnorderedList
figures               # Unicode/ASCII symbol fallbacks (cross-platform)

Do NOT use the older standalone packages (ink-text-input, ink-select-input, ink-spinner). The @inkjs/ui package supersedes them.

Project structure

src/ui/tui/
├── App.tsx                    # Thin shell — calls screen registry factory
├── store.ts                   # WizardStore: nanostores + session setters
├── router.ts                  # WizardRouter: flow pipelines + overlay stack
├── ink-ui.ts                  # InkUI: bridges getUI() calls to store setters
├── start-tui.ts               # TUI startup: dark mode, store, renderer
├── screen-registry.tsx         # Maps screen names to components + services
├── styles.ts                  # Colors, Icons, alignment enums
├── screens/                   # One file per screen — read for current set
├── primitives/                # Reusable layout components — read index.ts for exports
├── services/                  # Injectable service interfaces
└── components/
    └── TitleBar.tsx           # Top bar with version + feedback email

Ink rendering model

Ink is react-dom but for terminals. It uses Yoga (Facebook's Flexbox engine) for layout. Every <Box> is a flex container. All visible text MUST be inside <Text>.

BrowserInk
<div><Box>
<span><Text>
CSS / classNameProps directly on <Box> and <Text>
onClickuseInput() hook
window.innerWidthuseStdout().stdout.columns
scroll<Box overflow="hidden"> + manual offset
display: block<Box flexDirection="column">
display: flexDefault — every <Box> is already flex

Terminal compatibility

  • Small terminals: Check useStdout().stdout.columns and .rows
  • Piped input: Detect !process.stdin.isTTY and fall back to LoggingUI
  • CI environments: --ci flag uses LoggingUI (no TUI, no prompts)
  • Dark mode: start-tui.ts forces black background via ANSI escape codes
  • True black text: Use color="#000000" not color="black" (terminals render ANSI black as grey)
  • Ctrl+C: Ink handles via useApp().exit()

Reference files

Alternatives

Compare before choosing