Ink CLI Setup
This skill provides the configuration and patterns needed to use ink (React for CLI) in a Dungeonmaster architecture package.
Critical Version Requirement
Use ink v3.2.0, NOT v4+
ink v4+ is ESM-only with top-level await. Jest's jest.mock() hoisting does NOT work in ESM mode:
"Since ESM evaluates static import statements before looking at the code, the hoisting of
jest.mockcalls that happens in CJS won't work for ESM." - Jest ESM docs
ink v3.2.0 has no "type": "module" field, defaulting to CJS. This enables:
- •Single unified Jest config
- •
jest.mock()hoisting works - •Proxy pattern works
- •No ESM/CJS contamination issues
When to Use
- •Setting up a new CLI package that uses ink
- •Troubleshooting Jest test failures with ink
- •Creating ink component adapters
- •Writing widget tests
Key Requirements
- •Use ink v3.2.0: CJS-compatible
- •Package is ESM:
"type": "module"(for runtime) - •Bundle the entry point: Use esbuild to resolve imports
- •Single Jest config: CJS mode with proxy transformer
- •Local test render utility: Replace ink-testing-library
Quick Reference
See the following bundled files for detailed configuration:
- •PACKAGE-CONFIG.md - package.json, tsconfig.json setup
- •JEST-CONFIG.md - Jest configuration (single unified config)
- •ADAPTERS.md - Ink adapter patterns
- •WIDGETS.md - Widget component patterns
- •STARTUP.md - Entry point and bin setup
- •TESTING.md - Testing patterns with local ink-test-render
- •TROUBLESHOOTING.md - Common issues and fixes
File Structure
code
packages/my-cli/ ├── bin/ │ └── my-cli.e2e.test.ts ├── src/ │ ├── adapters/ │ │ ├── ink/ │ │ │ ├── box/ │ │ │ │ ├── ink-box-adapter.ts │ │ │ │ ├── ink-box-adapter.proxy.ts # No-op proxy (real ink used) │ │ │ │ └── ink-box-adapter.test.ts │ │ │ ├── text/ │ │ │ │ ├── ink-text-adapter.ts │ │ │ │ ├── ink-text-adapter.proxy.ts │ │ │ │ └── ink-text-adapter.test.ts │ │ │ └── use-input/ │ │ │ ├── ink-use-input-adapter.ts │ │ │ └── ink-use-input-adapter.proxy.ts │ │ ├── ink-testing-library/ │ │ │ └── render/ │ │ │ ├── ink-test-render.ts # Local CJS-compatible render utility │ │ │ ├── ink-testing-library-render-adapter.ts │ │ │ ├── ink-testing-library-render-adapter.proxy.ts │ │ │ └── ink-testing-library-render-adapter.test.ts │ │ └── react/ │ │ ├── module/ │ │ │ ├── react-module-adapter.ts # Re-exports React for JSX namespace │ │ │ ├── react-module-adapter.proxy.ts │ │ │ └── react-module-adapter.test.ts │ │ └── use-state/ │ │ ├── react-use-state-adapter.ts │ │ └── react-use-state-adapter.proxy.ts │ ├── startup/ │ │ └── start-my-cli.ts │ └── widgets/ │ └── my-app/ │ ├── my-app-widget.tsx # Main orchestrator widget │ ├── my-app-widget.test.tsx │ ├── my-app-widget.proxy.ts │ ├── menu-screen-layer-widget.tsx # Layer widgets for screens │ ├── menu-screen-layer-widget.test.tsx │ ├── menu-screen-layer-widget.proxy.ts │ └── help-screen-layer-widget.tsx ├── jest.config.js # Single unified config (ESM format) ├── package.json └── tsconfig.json