arcana-cli: The Tarot Engine That Makes Divination Deterministic

A TypeScript CLI that turns card meaning into a layered interpretation pipeline, then exposes that logic to humans in the terminal and LLMs through MCP.

7 min read • View on GitHub • More from kellymears

A terminal window sits on a desk arranged like an altar, with three tarot cards laid out in front of it and engraved lines branching upward into the screen. The scene explains that the project fuses ritual aesthetics with a deterministic software pipeline.
`arcana-cli` treats interpretation like a system, not a stunt. The ritual is real, but the machinery underneath is repeatable.
Key Takeaways

The strange promise: Tarot, but deterministic

Most tarot tools sell novelty. `arcana-cli` sells structure. It takes a domain that usually depends on mood, intuition, and ad hoc phrasing, then encodes it as a repeatable pipeline. That is the twist: the mystery stays, but the method becomes inspectable.

That changes the feel of the output. A reading no longer reads like a random flourish from a prompt. It reads like authored meaning assembled from rules, lookup tables, and a stable synthesis pass.

Why this project exists

The repo is built for two audiences at once. In the terminal, it behaves like a reading experience. Through MCP, it becomes a machine-readable interpretation service that an LLM can call instead of inventing meanings from scratch.

That split is the project’s most interesting design choice. It treats tarot text as a source of truth, then packages that truth into interfaces that can be consumed by a person or a model without changing the underlying logic.

The engine has three layers for a reason

The codebase is organized like a small editorial machine. `src/engine/` is the brain, and it is split into three successive passes: passages, relations, and narrative. Each pass answers a different question about the same cards.

The engine is not lookup plus output. It is a layered synthesis pipeline, where each stage narrows and reshapes meaning before prose is assembled.

Layer 1 resolves what a card means in context. It looks at position and orientation, then applies reversal framing instead of a crude upside-down swap. Layer 2 adds relational logic, including elemental dignities and suit dominance. Layer 3 stitches those signals into the final prose.

A close-up of a tarot card being interpreted through stacked transparent panels. The first panel shows upright meaning, the second adds reversal framing, and the third overlays relational forces from nearby cards before all layers resolve into one sentence. The image explains that meaning is built, not merely retrieved.
A reading is assembled in layers. Each layer changes the signal without flattening it.

Reversal is not just the opposite

This is where the project gets subtle. Reversal is treated as a family of framings, not a single inversion switch. A card can be blocked, shadowed, weakened, or contextually reframed, which is a more honest model of how interpretive systems actually work.

That matters because it avoids the trap of simplistic opposites. The code is not asking, “What is the anti-meaning?” It is asking, “How does this meaning bend when position, orientation, and neighboring cards apply pressure?”

// Conceptual shape of the logic
const interpretation = applyPassage(card, position, orientation)
const relations = scoreRelations(card, neighbors)
const reading = synthesizeNarrative(interpretation, relations)
return reading

The terminal UI makes the reading feel like a performance

`src/components/` is where the system becomes theatrical. Ink and React let the CLI animate, stage reveals, and pace the output with a kind of deliberate tension. That turns the reading into an event instead of a dump of text.

The important part is not the polish. It is the sequencing. A tarot reading depends on disclosure, and the UI respects that by revealing state in steps instead of spraying everything on screen at once.

The MCP bridge changes the audience

`src/mcp.ts` widens the project’s reach. Once the engine is exposed through MCP, the tarot logic is no longer only for the person in front of the terminal. It becomes a tool an LLM can call when it needs grounded, authored interpretation.

That is a sharper idea than “AI integration.” It is a way of keeping meaning inside a domain engine instead of letting the model improvise a lore dump. The model gets access to structure, and the structure stays authoritative.

What it replaces

`arcana-cli` is easiest to understand by comparison. It is not a static tarot app, not a generic CLI wrapper, and not a freeform prompt that hallucinates meaning. It sits between those categories and refuses the worst habits of each.

ApproachWhat it doesWhat it missesWhy `arcana-cli` is different
Static tarot appMaps cards to canned textNo layered context or narrative synthesisUses authored rules to change meaning with position, reversal, and relations
Generic CLI formatterPrints output cleanlyHas no interpretive engineUses the terminal as a staged reading interface, not just a text sink
LLM promptImprovise an answer from contextCan drift, flatten, or invent loreKeeps interpretation grounded in deterministic code and structured data

The deeper distinction is trust. A pure prompt asks a model to imitate expertise. `arcana-cli` encodes expertise as data and rules, then lets the interface present that expertise in different forms.

The bigger idea

The best software for symbolic work is not always the most expressive. Sometimes it is the most disciplined. `arcana-cli` shows that mystery can survive structure, and in this case, structure is what makes the mystery reusable.

That is why the project stands out. It is a tarot CLI, but more importantly, it is a machine for authored meaning. The terminal is just one face of it. MCP is the other.