termcast

star 323

Build TUIs with a Raycast-like React API using termcast. Implements @raycast/api components (List, Detail, Form, Action) rendered to the terminal via opentui.

remorses By remorses schedule Updated 6/5/2026

name: termcast repo: https://github.com/remorses/termcast description: Build TUIs with a Raycast-like React API using termcast. Implements @raycast/api components (List, Detail, Form, Action) rendered to the terminal via opentui.

termcast

Every time you work with termcast, you MUST fetch the latest README:

curl -s https://raw.githubusercontent.com/remorses/termcast/main/README.md # NEVER pipe to head/tail, read the full output

The README contains all component APIs, data fetching patterns, real-world examples, and porting guides. Read it in full before writing any termcast code.

Also read the opentui docs before editing .tsx files:

curl -s https://raw.githubusercontent.com/sst/opentui/refs/heads/main/packages/react/README.md

Imports

For new code, import from termcast and @termcast/utils. @raycast/api imports still work for porting existing extensions.

import { List, Detail, Action, ActionPanel, showToast, Toast, Icon, Color } from 'termcast'
import { useCachedPromise, useCachedState } from '@termcast/utils'

Agent rules

  • Use logger.log instead of console.log. Logs go to app.log in the extension directory.
  • Never use setTimeout for scheduling React state updates.
  • Never pass functions to useEffect dependencies. Causes infinite loops.
  • Minimize useState. Compute derived state inline when possible.
  • Always use .tsx extension for files with JSX.
  • useEffect is discouraged. Colocate logic in event handlers when possible.
  • Never use as any. Find proper types, import them, or use @ts-expect-error with explanation.
  • Shortcuts: use ctrl/alt + letter keys only (not digits).
  • showFailureToast(error, { title }) is the standard way to handle errors in actions.
  • revalidate() after every mutation to refresh data.
  • Never embed icons or checkmarks in title text. Use the icon prop on Action, List.Item, List.Dropdown.Item, etc. instead. Do not write title={isSelected ? "✓ Item" : "Item"}. Write title="Item" icon={isSelected ? Icon.CheckCircle : Icon.Circle}.
  • useCachedPromise serializes through JSON. Map objects become plain objects after cache round-tripping. Use plain objects or arrays instead of Maps.
  • Bun resolves modules differently from pnpm. If your project uses pnpm but the TUI runs under Bun, Bun may pick up a globally installed React instead of the local one. This causes useSyncExternalStore is not a function errors. Fix: add react as an explicit dependency in the package that imports termcast.
  • Use Cache (sync) over LocalStorage (async) for zustand persistence. Cache is SQLite-backed and synchronous, so persisted state can be loaded at module scope as the zustand initial value.
  • Every List item must have the same number of accessories in the same order. If items have different accessory counts, alignment breaks. Use { tag: '' } or { text: '' } for conditionally absent accessories. Use ternaries (condition ? { tag: value } : { tag: '' }) instead of conditional .push().
  • accessoryTagsLayout maps to all accessories by position (tags, text, date), not just tags. Include widths for text/date accessories too, or variable-width text will shift the entire block.

Profiling

See the full profiling guide: https://termcast.app/profiling

Two profiling approaches:

  • V8 CPU profiling for general performance: BUN_OPTIONS="--cpu-prof --cpu-prof-dir=./tmp/cpu-profiles" termcast dev ./my-extension
  • React render profiling for component timing: TERMCAST_REACT_PROFILE=1 termcast dev ./my-extension

Both produce .cpuprofile files. Analyze with bunx profano ./tmp/*.cpuprofile --sort self.

Testing extensions

Interactive experimentation with tuistory CLI

tuistory is a CLI tool for driving terminal applications from the shell, like Playwright but for TUIs.

Always run tuistory --help first to see the latest commands and options.

# Launch the extension in a managed terminal session
tuistory launch "termcast dev" -s my-ext --cols 120 --rows 36

# See current terminal state
tuistory -s my-ext snapshot --trim

# Interact
tuistory -s my-ext type "search query"
tuistory -s my-ext press enter
tuistory -s my-ext press ctrl k        # open action panel
tuistory -s my-ext press tab           # next form field
tuistory -s my-ext press esc           # go back

# Take a screenshot as image
tuistory -s my-ext screenshot -o ./tmp/screenshot.jpg --pixel-ratio 2

# Cleanup
tuistory -s my-ext close

Automated tests with vitest + tuistory JS API

tuistory provides a Playwright-style JS API for writing automated TUI tests. The workflow is observe-act-observe: take a snapshot, interact, take another snapshot.

import { test, expect } from 'vitest'
import { launchTerminal } from 'tuistory'

test('extension shows items and navigates to detail', async () => {
  const session = await launchTerminal({
    command: 'termcast',
    args: ['dev'],
    cols: 120,
    rows: 36,
    cwd: '/path/to/my-extension',
  })

  // Wait for the list to render
  await session.waitForText('Search', { timeout: 10000 })

  // Observe initial state
  const initial = await session.text({ trimEnd: true })
  expect(initial).toMatchInlineSnapshot()

  // Type a search query
  await session.type('project')
  const filtered = await session.text({ trimEnd: true })
  expect(filtered).toMatchInlineSnapshot()

  // Press Enter to trigger primary action
  await session.press('enter')
  await session.waitForText('Detail', { timeout: 5000 })
  const detail = await session.text({ trimEnd: true })
  expect(detail).toMatchInlineSnapshot()

  // Go back
  await session.press('esc')

  session.close()
}, 30000)

Always leave toMatchInlineSnapshot() empty the first time, run with -u to fill them, then read back the test file to verify the captured output is correct.

Install via CLI
npx skills add https://github.com/remorses/termcast --skill termcast
Repository Details
star Stars 323
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator