@capitalthought/flicker
v0.1.2
Published
Agent-first terminal text animations — library + MCP server + CLI
Maintainers
Readme
flicker
Agent-first terminal text animations — library + MCP server + CLI.
Home: flicker.sh
Why
Agents now run inside terminals. They want decorative output — a typewriter intro, a progress bar, a spinner — without:
- Corrupting MCP stdio. Any escape on a server's stdout breaks JSON-RPC; the host drops the connection. flicker's renderer writes to
/dev/tty(or stderr, or nothing) — never to MCP stdout. - Burning context. A 2-sentence model reply that ran a spinner during the call has been measured at ~15KB of escape noise to ~200 bytes of content. flicker reports the exact ANSI tax up-front via
estimate, so an unattended agent can choose whether to spend. - Opening a Terminal-DiLLMa surface. LLM-generated output can embed CSI / OSC / DCS sequences that hijack a terminal. flicker emits only an allowlisted set of escapes and rejects caller text that smuggles its own.
Three rendering tiers — full, reduced, plain — auto-selected. Agent / CI / MCP context falls to plain by default; a human in a truecolor TTY gets full.
Install
npm install -g @capitalthought/flickerOr use without installing:
npx @capitalthought/flicker typewriter "hello world"As an MCP server
claude mcp add flicker npx @capitalthought/flicker mcpThe MCP server is the primary surface. Five verb-first tools: play_typewriter, play_spinner, play_progress, estimate, overview.
Quick start
# Typewriter
flicker typewriter "hello, flicker"
# Spinner
flicker spinner "Connecting" --duration 1500 --kind dots
# Progress bar
flicker progress "Downloading" --total 100 --steps 20
# Estimate cost without rendering
flicker typewriter "expensive paragraph" --costEffects
| Effect | CLI | Behavior |
|---|---|---|
| typewriter | flicker typewriter <text> [--cps N] | Chars appear at N cps. |
| spinner | flicker spinner <label> [--duration ms] [--kind dots\|bar\|pulse] | Animated glyph + label. Omit --duration for indeterminate (capped at 60s). |
| progress | flicker progress <label> [--total N] [--steps M] | Determinate bar (--total) or bouncing indeterminate. |
MCP tools
| Tool | Args | Returns |
|---|---|---|
| play_typewriter | text, cps?, mode? | Receipt |
| play_spinner | label, duration_ms?, kind?, mode? | Receipt |
| play_progress | label, total (int or null), steps?, mode? | Receipt |
| estimate | effect, args | CostEstimate (no render) |
| overview | — | ServerStatus (uptime, mode, terminal caps, last 20 receipts, cumulative cost) |
See AGENTS.md for the agent-facing contract and sequencing rules.
Cost estimation
Every effect can be estimated without rendering. The --cost CLI flag prints a CostEstimate to stderr and exits:
$ flicker typewriter "hi" --cost
{
"bytes_to_tty": 6,
"bytes_stripped": 3,
"tokens_stripped": 1,
"dollars_stripped": 0.000003,
"dollars_worst_case": 0.000006,
"estimated": true
}dollars_worst_case is what the call costs if every escape byte lands in your context. dollars_stripped is what it costs after stripForAgent() runs. The gap is the ANSI tax. Pricing is calibrated against Sonnet 4.6 input tokens at $3.00 / Mtok (refreshed quarterly; a CI check fails the build if the constant is stale).
Library
import { typewriter, spinner, progress, estimate } from "@capitalthought/flicker";
const receipt = await typewriter.render({ text: "hello", cps: 60 });
// receipt.cost contains the same CostEstimate the CLI prints.See examples/ for runnable scripts.
Design + research
docs/plans/2026-05-17-flicker-design.md— design doc, CSI allowlist, mode-detection cascade, cost model.docs/research/2026-05-16-terminal-animation-research.md— research brief.
License
MIT
