Skip to content

Commit 9f64358

Browse files
committed
feat: add tarot module with cryptographic randomness (v0.2.22)
- thoth tarot: Draw cards with true entropy (secrets.SystemRandom) - thoth tarot-card: Look up any card by name/number - thoth tarot-deck: List all 78 cards (filterable by arcana/suit) - thoth tarot-spreads: List available spreads Spreads: single, 3-card, celtic, horseshoe, relationship, decision The LLM never picks cards — only interprets what entropy selects.
1 parent 823edd2 commit 9f64358

8 files changed

Lines changed: 931 additions & 5 deletions

File tree

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "thoth-cli",
3-
"version": "0.2.21",
3+
"version": "0.2.22",
44
"description": "𓅝 Astrological calculations from the command line. Swiss Ephemeris precision. Built for humans and agents.",
55
"author": "AKLO <aklo@aklolabs.com>",
66
"license": "MIT",

packages/cli/src/bin.ts

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import { writeFileSync } from 'fs';
1111
import {
1212
chart, transit, moon, ephemeris, version,
1313
solarReturn, lunarReturn, synastry, progressions, ephemerisRange,
14-
composite, solarArc, horary, score, moonExtended, transitScan, ephemerisMulti
14+
composite, solarArc, horary, score, moonExtended, transitScan, ephemerisMulti,
15+
tarotDraw, tarotCard, tarotDeck, tarotSpreads
1516
} from './lib/core.js';
1617
import {
1718
formatChart, formatTransits, formatMoon, formatEphemeris,
1819
formatSolarReturn, formatLunarReturn, formatSynastry,
1920
formatProgressions, formatEphemerisRange,
2021
formatComposite, formatSolarArc, formatHorary,
21-
formatScore, formatMoonExtended, formatTransitScan, formatEphemerisMulti
22+
formatScore, formatMoonExtended, formatTransitScan, formatEphemerisMulti,
23+
formatTarotDraw, formatTarotCard, formatTarotDeck, formatTarotSpreads
2224
} from './lib/format.js';
2325
import { isError } from './types.js';
2426

@@ -1040,9 +1042,111 @@ program
10401042
}
10411043
});
10421044

1045+
// ═══════════════════════════════════════════════════════════════
1046+
// TAROT COMMANDS
1047+
// ═══════════════════════════════════════════════════════════════
1048+
1049+
// Tarot draw command
1050+
program
1051+
.command('tarot')
1052+
.alias('draw')
1053+
.description('Draw tarot cards (cryptographic randomness)')
1054+
.option('-s, --spread <spread>', 'Spread: single, 3-card, celtic, horseshoe, relationship, decision', 'single')
1055+
.option('-q, --question <question>', 'Question for the reading')
1056+
.option('-n, --count <count>', 'Number of cards (for custom spread)', parseInt)
1057+
.option('--no-reversals', 'Disable reversed cards')
1058+
.option('--json', 'Output raw JSON')
1059+
.action(async (options) => {
1060+
const spinner = ora('Shuffling the deck...').start();
1061+
1062+
const result = await tarotDraw({
1063+
spread: options.spread,
1064+
question: options.question,
1065+
count: options.count,
1066+
reversals: options.reversals,
1067+
});
1068+
1069+
spinner.stop();
1070+
1071+
if (isError(result)) {
1072+
console.error(chalk.red('Error: ' + result.error));
1073+
process.exit(1);
1074+
}
1075+
1076+
if (options.json) {
1077+
console.log(JSON.stringify(result, null, 2));
1078+
} else {
1079+
console.log(formatTarotDraw(result));
1080+
}
1081+
});
1082+
1083+
// Tarot card lookup
1084+
program
1085+
.command('tarot-card <card>')
1086+
.alias('card')
1087+
.description('Look up a tarot card by name or number')
1088+
.option('--json', 'Output raw JSON')
1089+
.action(async (card, options) => {
1090+
const result = await tarotCard(card);
1091+
1092+
if (isError(result)) {
1093+
console.error(chalk.red('Error: ' + result.error));
1094+
process.exit(1);
1095+
}
1096+
1097+
if (options.json) {
1098+
console.log(JSON.stringify(result, null, 2));
1099+
} else {
1100+
console.log(formatTarotCard(result));
1101+
}
1102+
});
1103+
1104+
// Tarot deck listing
1105+
program
1106+
.command('tarot-deck')
1107+
.alias('deck')
1108+
.description('List tarot cards')
1109+
.option('-f, --filter <filter>', 'Filter: major, minor, wands, cups, swords, pentacles')
1110+
.option('--json', 'Output raw JSON')
1111+
.action(async (options) => {
1112+
const result = await tarotDeck(options.filter);
1113+
1114+
if (isError(result)) {
1115+
console.error(chalk.red('Error: ' + result.error));
1116+
process.exit(1);
1117+
}
1118+
1119+
if (options.json) {
1120+
console.log(JSON.stringify(result, null, 2));
1121+
} else {
1122+
console.log(formatTarotDeck(result));
1123+
}
1124+
});
1125+
1126+
// Tarot spreads listing
1127+
program
1128+
.command('tarot-spreads')
1129+
.alias('spreads')
1130+
.description('List available tarot spreads')
1131+
.option('--json', 'Output raw JSON')
1132+
.action(async (options) => {
1133+
const result = await tarotSpreads();
1134+
1135+
if (isError(result)) {
1136+
console.error(chalk.red('Error: ' + result.error));
1137+
process.exit(1);
1138+
}
1139+
1140+
if (options.json) {
1141+
console.log(JSON.stringify(result, null, 2));
1142+
} else {
1143+
console.log(formatTarotSpreads(result));
1144+
}
1145+
});
1146+
10431147
// Banner
10441148
console.log(chalk.dim(''));
1045-
console.log(chalk.yellow(' 𓅝') + chalk.dim(' thoth-cli v0.2.21'));
1149+
console.log(chalk.yellow(' 𓅝') + chalk.dim(' thoth-cli v0.2.22'));
10461150
console.log(chalk.dim(''));
10471151

10481152
program.parse();

packages/cli/src/lib/core.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import type {
2222
MoonExtendedResult,
2323
TransitScanResult,
2424
EphemerisMultiResult,
25+
TarotDrawResult,
26+
TarotCard,
27+
TarotDeckResult,
28+
TarotSpreadsResult,
2529
ChartOptions,
2630
TransitOptions,
2731
MoonOptions,
@@ -38,6 +42,7 @@ import type {
3842
MoonExtendedOptions,
3943
TransitScanOptions,
4044
EphemerisMultiOptions,
45+
TarotDrawOptions,
4146
ThothResult,
4247
} from '../types.js';
4348

@@ -499,3 +504,44 @@ export async function ephemerisMulti(options: EphemerisMultiOptions): Promise<Th
499504

500505
return execute<EphemerisMultiResult>('ephemeris-multi', args);
501506
}
507+
508+
// ═══════════════════════════════════════════════════════════════
509+
// TAROT
510+
// ═══════════════════════════════════════════════════════════════
511+
512+
/**
513+
* Draw tarot cards with cryptographic randomness
514+
*/
515+
export async function tarotDraw(options: TarotDrawOptions): Promise<ThothResult<TarotDrawResult>> {
516+
const args: string[] = [];
517+
518+
if (options.count) args.push('--count', String(options.count));
519+
if (options.spread) args.push('--spread', options.spread);
520+
if (options.question) args.push('--question', options.question);
521+
if (options.reversals === false) args.push('--no-reversals');
522+
523+
return execute<TarotDrawResult>('tarot-draw', args);
524+
}
525+
526+
/**
527+
* Look up a specific tarot card
528+
*/
529+
export async function tarotCard(identifier: string): Promise<ThothResult<TarotCard>> {
530+
return execute<TarotCard>('tarot-card', [identifier]);
531+
}
532+
533+
/**
534+
* Get tarot deck (optionally filtered)
535+
*/
536+
export async function tarotDeck(filter?: string): Promise<ThothResult<TarotDeckResult>> {
537+
const args: string[] = [];
538+
if (filter) args.push('--filter', filter);
539+
return execute<TarotDeckResult>('tarot-deck', args);
540+
}
541+
542+
/**
543+
* List available tarot spreads
544+
*/
545+
export async function tarotSpreads(): Promise<ThothResult<TarotSpreadsResult>> {
546+
return execute<TarotSpreadsResult>('tarot-spreads', []);
547+
}

0 commit comments

Comments
 (0)