thoth-cli 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,34 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  chart,
4
+ composite,
4
5
  ephemeris,
6
+ ephemerisRange,
5
7
  formatChart,
8
+ formatComposite,
6
9
  formatEphemeris,
10
+ formatEphemerisRange,
11
+ formatHorary,
12
+ formatLunarReturn,
7
13
  formatMoon,
14
+ formatProgressions,
15
+ formatSolarArc,
16
+ formatSolarReturn,
17
+ formatSynastry,
8
18
  formatTransits,
19
+ horary,
9
20
  isError,
21
+ lunarReturn,
10
22
  moon,
23
+ progressions,
24
+ solarArc,
25
+ solarReturn,
26
+ synastry,
11
27
  transit,
12
28
  version
13
- } from "./chunk-NYXKYHNK.js";
29
+ } from "./chunk-NEQKNEBO.js";
14
30
 
15
31
  // src/bin.ts
16
32
  import { Command } from "commander";
17
33
  import chalk from "chalk";
18
34
  import ora from "ora";
35
+ import { writeFileSync } from "fs";
19
36
  var program = new Command();
20
37
  program.name("thoth").description(`\u{1315D} Astrological calculations from the command line
21
38
 
22
39
  Examples:
23
40
  thoth chart --date 1991-07-08 --time 14:06 --city "New York"
24
41
  thoth transit --natal-date 1991-07-08 --natal-time 14:06 --city "New York"
42
+ thoth solar-return --natal-date 1991-07-08 --natal-time 14:06 --city "New York" --year 2026
43
+ thoth synastry --date1 1991-07-08 --time1 14:06 --city1 "New York" --date2 2026-02-26 --time2 04:35 --city2 "New York"
44
+ thoth progressions --natal-date 1991-07-08 --natal-time 14:06 --city "New York" --target-date 2026-03-01
25
45
  thoth moon
26
- thoth ephemeris --body pluto`).version("0.1.0");
27
- program.command("chart").description(`Calculate a natal chart
28
-
29
- Examples:
30
- thoth chart --date 1991-07-08 --time 14:06 --city "New York" --name "AKLO"
31
- thoth chart --date 1986-07-29 --time 12:00 --lat 40.7128 --lng -74.0060`).requiredOption("--date <date>", "Birth date (YYYY-MM-DD)").requiredOption("--time <time>", "Birth time (HH:MM)").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--city <city>", 'City name (e.g., "New York")').option("--nation <nation>", "Country code (e.g., US, UK)", "US").option("--name <name>", "Name", "Subject").option("--json", "Output raw JSON").action(async (options) => {
46
+ thoth ephemeris --body pluto`).version("0.2.0");
47
+ program.command("chart").description("Calculate a natal chart").requiredOption("--date <date>", "Birth date (YYYY-MM-DD)").requiredOption("--time <time>", "Birth time (HH:MM)").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--city <city>", "City name").option("--nation <nation>", "Country code", "US").option("--name <name>", "Name", "Subject").option("--json", "Output raw JSON").option("--svg", "Output SVG chart").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
32
48
  if (!options.city && (!options.lat || !options.lng)) {
33
49
  console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
34
50
  process.exit(1);
@@ -46,24 +62,24 @@ program.command("chart").description(`Calculate a natal chart
46
62
  lng: options.lng,
47
63
  city: options.city,
48
64
  nation: options.nation,
49
- name: options.name
65
+ name: options.name,
66
+ svg: options.svg || options.svgFile
50
67
  });
51
68
  spinner.stop();
52
69
  if (isError(result)) {
53
70
  console.error(chalk.red(`Error: ${result.error}`));
54
71
  process.exit(1);
55
72
  }
56
- if (options.json) {
73
+ if (result.svg && options.svgFile) {
74
+ writeFileSync(options.svgFile, result.svg);
75
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
76
+ } else if (options.json || result.svg) {
57
77
  console.log(JSON.stringify(result, null, 2));
58
78
  } else {
59
79
  console.log(formatChart(result));
60
80
  }
61
81
  });
62
- program.command("transit").description(`Calculate transits to a natal chart
63
-
64
- Examples:
65
- thoth transit --natal-date 1991-07-08 --natal-time 14:06 --city "New York"
66
- thoth transit --natal-date 1991-07-08 --natal-time 14:06 --city "New York" --orb 1`).requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--city <city>", 'City name (e.g., "New York")').option("--nation <nation>", "Country code (e.g., US, UK)", "US").option("--transit-date <date>", "Transit date (YYYY-MM-DD, default: today)").option("--orb <degrees>", "Orb in degrees", parseFloat, 3).option("--json", "Output raw JSON").action(async (options) => {
82
+ program.command("transit").description("Calculate transits to a natal chart").requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--city <city>", "City name").option("--nation <nation>", "Country code", "US").option("--transit-date <date>", "Transit date (default: today)").option("--orb <degrees>", "Orb in degrees", parseFloat, 3).option("--json", "Output raw JSON").option("--svg", "Output SVG bi-wheel").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
67
83
  if (!options.city && (!options.lat || !options.lng)) {
68
84
  console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
69
85
  process.exit(1);
@@ -88,19 +104,330 @@ program.command("transit").description(`Calculate transits to a natal chart
88
104
  transitYear,
89
105
  transitMonth,
90
106
  transitDay,
91
- orb: options.orb
107
+ orb: options.orb,
108
+ svg: options.svg || options.svgFile
92
109
  });
93
110
  spinner.stop();
94
111
  if (isError(result)) {
95
112
  console.error(chalk.red(`Error: ${result.error}`));
96
113
  process.exit(1);
97
114
  }
98
- if (options.json) {
115
+ if (result.svg && options.svgFile) {
116
+ writeFileSync(options.svgFile, result.svg);
117
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
118
+ } else if (options.json || result.svg) {
99
119
  console.log(JSON.stringify(result, null, 2));
100
120
  } else {
101
121
  console.log(formatTransits(result));
102
122
  }
103
123
  });
124
+ program.command("solar-return").description("Calculate solar return chart for a year").requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").requiredOption("--year <year>", "Return year", parseInt).option("--city <city>", "Natal city").option("--nation <nation>", "Country code", "US").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--return-city <city>", "Location for return (default: natal)").option("--json", "Output raw JSON").option("--svg", "Output SVG chart").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
125
+ if (!options.city && (!options.lat || !options.lng)) {
126
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
127
+ process.exit(1);
128
+ }
129
+ const spinner = ora(`Calculating ${options.year} solar return...`).start();
130
+ const [natalYear, natalMonth, natalDay] = options.natalDate.split("-").map(Number);
131
+ const [natalHour, natalMinute] = options.natalTime.split(":").map(Number);
132
+ const result = await solarReturn({
133
+ natalYear,
134
+ natalMonth,
135
+ natalDay,
136
+ natalHour,
137
+ natalMinute,
138
+ natalLat: options.lat,
139
+ natalLng: options.lng,
140
+ natalCity: options.city,
141
+ nation: options.nation,
142
+ returnYear: options.year,
143
+ returnCity: options.returnCity,
144
+ svg: options.svg || options.svgFile
145
+ });
146
+ spinner.stop();
147
+ if (isError(result)) {
148
+ console.error(chalk.red(`Error: ${result.error}`));
149
+ process.exit(1);
150
+ }
151
+ if (result.svg && options.svgFile) {
152
+ writeFileSync(options.svgFile, result.svg);
153
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
154
+ } else if (options.json || result.svg) {
155
+ console.log(JSON.stringify(result, null, 2));
156
+ } else {
157
+ console.log(formatSolarReturn(result));
158
+ }
159
+ });
160
+ program.command("lunar-return").description("Calculate next lunar return from a date").requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").requiredOption("--from <date>", "Search from date (YYYY-MM-DD)").option("--city <city>", "Natal city").option("--nation <nation>", "Country code", "US").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--return-city <city>", "Location for return").option("--json", "Output raw JSON").option("--svg", "Output SVG chart").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
161
+ if (!options.city && (!options.lat || !options.lng)) {
162
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
163
+ process.exit(1);
164
+ }
165
+ const spinner = ora("Calculating lunar return...").start();
166
+ const [natalYear, natalMonth, natalDay] = options.natalDate.split("-").map(Number);
167
+ const [natalHour, natalMinute] = options.natalTime.split(":").map(Number);
168
+ const [fromYear, fromMonth, fromDay] = options.from.split("-").map(Number);
169
+ const result = await lunarReturn({
170
+ natalYear,
171
+ natalMonth,
172
+ natalDay,
173
+ natalHour,
174
+ natalMinute,
175
+ natalLat: options.lat,
176
+ natalLng: options.lng,
177
+ natalCity: options.city,
178
+ nation: options.nation,
179
+ fromYear,
180
+ fromMonth,
181
+ fromDay,
182
+ returnCity: options.returnCity,
183
+ svg: options.svg || options.svgFile
184
+ });
185
+ spinner.stop();
186
+ if (isError(result)) {
187
+ console.error(chalk.red(`Error: ${result.error}`));
188
+ process.exit(1);
189
+ }
190
+ if (result.svg && options.svgFile) {
191
+ writeFileSync(options.svgFile, result.svg);
192
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
193
+ } else if (options.json || result.svg) {
194
+ console.log(JSON.stringify(result, null, 2));
195
+ } else {
196
+ console.log(formatLunarReturn(result));
197
+ }
198
+ });
199
+ program.command("synastry").description("Calculate synastry between two charts").requiredOption("--date1 <date>", "Person 1 birth date (YYYY-MM-DD)").requiredOption("--time1 <time>", "Person 1 birth time (HH:MM)").requiredOption("--date2 <date>", "Person 2 birth date (YYYY-MM-DD)").requiredOption("--time2 <time>", "Person 2 birth time (HH:MM)").option("--city1 <city>", "Person 1 city").option("--nation1 <nation>", "Person 1 country", "US").option("--lat1 <lat>", "Person 1 latitude", parseFloat).option("--lng1 <lng>", "Person 1 longitude", parseFloat).option("--name1 <name>", "Person 1 name", "Person 1").option("--city2 <city>", "Person 2 city").option("--nation2 <nation>", "Person 2 country", "US").option("--lat2 <lat>", "Person 2 latitude", parseFloat).option("--lng2 <lng>", "Person 2 longitude", parseFloat).option("--name2 <name>", "Person 2 name", "Person 2").option("--orb <degrees>", "Orb in degrees", parseFloat, 6).option("--json", "Output raw JSON").option("--svg", "Output SVG bi-wheel").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
200
+ if (!options.city1 && (!options.lat1 || !options.lng1)) {
201
+ console.error(chalk.red("Error: Must provide either --city1 or both --lat1 and --lng1"));
202
+ process.exit(1);
203
+ }
204
+ if (!options.city2 && (!options.lat2 || !options.lng2)) {
205
+ console.error(chalk.red("Error: Must provide either --city2 or both --lat2 and --lng2"));
206
+ process.exit(1);
207
+ }
208
+ const spinner = ora("Calculating synastry...").start();
209
+ const [year1, month1, day1] = options.date1.split("-").map(Number);
210
+ const [hour1, minute1] = options.time1.split(":").map(Number);
211
+ const [year2, month2, day2] = options.date2.split("-").map(Number);
212
+ const [hour2, minute2] = options.time2.split(":").map(Number);
213
+ const result = await synastry({
214
+ year1,
215
+ month1,
216
+ day1,
217
+ hour1,
218
+ minute1,
219
+ lat1: options.lat1,
220
+ lng1: options.lng1,
221
+ city1: options.city1,
222
+ nation1: options.nation1,
223
+ name1: options.name1,
224
+ year2,
225
+ month2,
226
+ day2,
227
+ hour2,
228
+ minute2,
229
+ lat2: options.lat2,
230
+ lng2: options.lng2,
231
+ city2: options.city2,
232
+ nation2: options.nation2,
233
+ name2: options.name2,
234
+ orb: options.orb,
235
+ svg: options.svg || options.svgFile
236
+ });
237
+ spinner.stop();
238
+ if (isError(result)) {
239
+ console.error(chalk.red(`Error: ${result.error}`));
240
+ process.exit(1);
241
+ }
242
+ if (result.svg && options.svgFile) {
243
+ writeFileSync(options.svgFile, result.svg);
244
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
245
+ } else if (options.json || result.svg) {
246
+ console.log(JSON.stringify(result, null, 2));
247
+ } else {
248
+ console.log(formatSynastry(result));
249
+ }
250
+ });
251
+ program.command("composite").description("Calculate composite (midpoint) chart for a relationship").requiredOption("--date1 <date>", "Person 1 birth date (YYYY-MM-DD)").requiredOption("--time1 <time>", "Person 1 birth time (HH:MM)").requiredOption("--date2 <date>", "Person 2 birth date (YYYY-MM-DD)").requiredOption("--time2 <time>", "Person 2 birth time (HH:MM)").option("--city1 <city>", "Person 1 city").option("--nation1 <nation>", "Person 1 country", "US").option("--lat1 <lat>", "Person 1 latitude", parseFloat).option("--lng1 <lng>", "Person 1 longitude", parseFloat).option("--name1 <name>", "Person 1 name", "Person 1").option("--city2 <city>", "Person 2 city").option("--nation2 <nation>", "Person 2 country", "US").option("--lat2 <lat>", "Person 2 latitude", parseFloat).option("--lng2 <lng>", "Person 2 longitude", parseFloat).option("--name2 <name>", "Person 2 name", "Person 2").option("--json", "Output raw JSON").option("--svg", "Output SVG chart").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
252
+ if (!options.city1 && (!options.lat1 || !options.lng1)) {
253
+ console.error(chalk.red("Error: Must provide either --city1 or both --lat1 and --lng1"));
254
+ process.exit(1);
255
+ }
256
+ if (!options.city2 && (!options.lat2 || !options.lng2)) {
257
+ console.error(chalk.red("Error: Must provide either --city2 or both --lat2 and --lng2"));
258
+ process.exit(1);
259
+ }
260
+ const spinner = ora("Calculating composite chart...").start();
261
+ const [year1, month1, day1] = options.date1.split("-").map(Number);
262
+ const [hour1, minute1] = options.time1.split(":").map(Number);
263
+ const [year2, month2, day2] = options.date2.split("-").map(Number);
264
+ const [hour2, minute2] = options.time2.split(":").map(Number);
265
+ const result = await composite({
266
+ year1,
267
+ month1,
268
+ day1,
269
+ hour1,
270
+ minute1,
271
+ lat1: options.lat1,
272
+ lng1: options.lng1,
273
+ city1: options.city1,
274
+ nation1: options.nation1,
275
+ name1: options.name1,
276
+ year2,
277
+ month2,
278
+ day2,
279
+ hour2,
280
+ minute2,
281
+ lat2: options.lat2,
282
+ lng2: options.lng2,
283
+ city2: options.city2,
284
+ nation2: options.nation2,
285
+ name2: options.name2,
286
+ svg: options.svg || options.svgFile
287
+ });
288
+ spinner.stop();
289
+ if (isError(result)) {
290
+ console.error(chalk.red(`Error: ${result.error}`));
291
+ process.exit(1);
292
+ }
293
+ if (result.svg && options.svgFile) {
294
+ writeFileSync(options.svgFile, result.svg);
295
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
296
+ } else if (options.json || result.svg) {
297
+ console.log(JSON.stringify(result, null, 2));
298
+ } else {
299
+ console.log(formatComposite(result));
300
+ }
301
+ });
302
+ program.command("solar-arc").description("Calculate solar arc directions (all planets move by Sun's arc)").requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").requiredOption("--target-date <date>", "Target date for directions (YYYY-MM-DD)").option("--city <city>", "Natal city").option("--nation <nation>", "Country code", "US").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--json", "Output raw JSON").action(async (options) => {
303
+ if (!options.city && (!options.lat || !options.lng)) {
304
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
305
+ process.exit(1);
306
+ }
307
+ const spinner = ora("Calculating solar arc directions...").start();
308
+ const [natalYear, natalMonth, natalDay] = options.natalDate.split("-").map(Number);
309
+ const [natalHour, natalMinute] = options.natalTime.split(":").map(Number);
310
+ const [targetYear, targetMonth, targetDay] = options.targetDate.split("-").map(Number);
311
+ const result = await solarArc({
312
+ natalYear,
313
+ natalMonth,
314
+ natalDay,
315
+ natalHour,
316
+ natalMinute,
317
+ natalLat: options.lat,
318
+ natalLng: options.lng,
319
+ natalCity: options.city,
320
+ nation: options.nation,
321
+ targetYear,
322
+ targetMonth,
323
+ targetDay
324
+ });
325
+ spinner.stop();
326
+ if (isError(result)) {
327
+ console.error(chalk.red(`Error: ${result.error}`));
328
+ process.exit(1);
329
+ }
330
+ if (options.json) {
331
+ console.log(JSON.stringify(result, null, 2));
332
+ } else {
333
+ console.log(formatSolarArc(result));
334
+ }
335
+ });
336
+ program.command("progressions").description("Calculate secondary progressions (day-for-a-year)").requiredOption("--natal-date <date>", "Natal date (YYYY-MM-DD)").requiredOption("--natal-time <time>", "Natal time (HH:MM)").requiredOption("--target-date <date>", "Target date for progressions (YYYY-MM-DD)").option("--city <city>", "Natal city").option("--nation <nation>", "Country code", "US").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--json", "Output raw JSON").option("--svg", "Output SVG chart").option("--svg-file <path>", "Save SVG to file").action(async (options) => {
337
+ if (!options.city && (!options.lat || !options.lng)) {
338
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
339
+ process.exit(1);
340
+ }
341
+ const spinner = ora("Calculating progressions...").start();
342
+ const [natalYear, natalMonth, natalDay] = options.natalDate.split("-").map(Number);
343
+ const [natalHour, natalMinute] = options.natalTime.split(":").map(Number);
344
+ const [targetYear, targetMonth, targetDay] = options.targetDate.split("-").map(Number);
345
+ const result = await progressions({
346
+ natalYear,
347
+ natalMonth,
348
+ natalDay,
349
+ natalHour,
350
+ natalMinute,
351
+ natalLat: options.lat,
352
+ natalLng: options.lng,
353
+ natalCity: options.city,
354
+ nation: options.nation,
355
+ targetYear,
356
+ targetMonth,
357
+ targetDay,
358
+ svg: options.svg || options.svgFile
359
+ });
360
+ spinner.stop();
361
+ if (isError(result)) {
362
+ console.error(chalk.red(`Error: ${result.error}`));
363
+ process.exit(1);
364
+ }
365
+ if (result.svg && options.svgFile) {
366
+ writeFileSync(options.svgFile, result.svg);
367
+ console.log(chalk.green(`\u2713 SVG saved to ${options.svgFile}`));
368
+ } else if (options.json || result.svg) {
369
+ console.log(JSON.stringify(result, null, 2));
370
+ } else {
371
+ console.log(formatProgressions(result));
372
+ }
373
+ });
374
+ program.command("ephemeris-range").description("Get ephemeris positions over a date range").requiredOption("--body <body>", "Celestial body (sun, moon, mars, etc.)").requiredOption("--from <date>", "Start date (YYYY-MM-DD)").requiredOption("--to <date>", "End date (YYYY-MM-DD)").option("--step <step>", "Step: day, week, month", "month").option("--json", "Output raw JSON").action(async (options) => {
375
+ const spinner = ora(`Getting ${options.body} positions...`).start();
376
+ const [startYear, startMonth, startDay] = options.from.split("-").map(Number);
377
+ const [endYear, endMonth, endDay] = options.to.split("-").map(Number);
378
+ const result = await ephemerisRange({
379
+ body: options.body,
380
+ startYear,
381
+ startMonth,
382
+ startDay,
383
+ endYear,
384
+ endMonth,
385
+ endDay,
386
+ step: options.step
387
+ });
388
+ spinner.stop();
389
+ if (isError(result)) {
390
+ console.error(chalk.red(`Error: ${result.error}`));
391
+ process.exit(1);
392
+ }
393
+ if (options.json) {
394
+ console.log(JSON.stringify(result, null, 2));
395
+ } else {
396
+ console.log(formatEphemerisRange(result));
397
+ }
398
+ });
399
+ program.command("horary").description("Cast a horary chart for divination (like tarot for astrology)").requiredOption("--question <question>", "The question being asked").option("--date <date>", "Date (YYYY-MM-DD, default: now)").option("--time <time>", "Time (HH:MM, default: now)").option("--city <city>", "City where question is asked").option("--nation <nation>", "Country code", "US").option("--lat <lat>", "Latitude", parseFloat).option("--lng <lng>", "Longitude", parseFloat).option("--json", "Output raw JSON").action(async (options) => {
400
+ const spinner = ora("Casting horary chart...").start();
401
+ let year, month, day, hour, minute;
402
+ if (options.date) {
403
+ [year, month, day] = options.date.split("-").map(Number);
404
+ }
405
+ if (options.time) {
406
+ [hour, minute] = options.time.split(":").map(Number);
407
+ }
408
+ const result = await horary({
409
+ question: options.question,
410
+ year,
411
+ month,
412
+ day,
413
+ hour,
414
+ minute,
415
+ lat: options.lat,
416
+ lng: options.lng,
417
+ city: options.city,
418
+ nation: options.nation
419
+ });
420
+ spinner.stop();
421
+ if (isError(result)) {
422
+ console.error(chalk.red(`Error: ${result.error}`));
423
+ process.exit(1);
424
+ }
425
+ if (options.json) {
426
+ console.log(JSON.stringify(result, null, 2));
427
+ } else {
428
+ console.log(formatHorary(result));
429
+ }
430
+ });
104
431
  program.command("moon").description("Get current moon phase and position").option("--date <date>", "Date (YYYY-MM-DD, default: today)").option("--lat <lat>", "Latitude", parseFloat, 40.7128).option("--lng <lng>", "Longitude", parseFloat, -74.006).option("--json", "Output raw JSON").action(async (options) => {
105
432
  const spinner = ora("Getting moon phase...").start();
106
433
  let year, month, day;
@@ -174,125 +501,40 @@ program.command("key").description("Symbol reference guide").action(() => {
174
501
  console.log(chalk.bold.white("\n\u{1315D} THOTH KEY \u2014 Complete Reference\n"));
175
502
  console.log(chalk.bold.cyan("\u2550\u2550 PLANETS \u2550\u2550"));
176
503
  console.log(` ${sun("\u2609 Sun")} Identity, vitality, ego, life force`);
177
- console.log(` ${chalk.dim("Rules:")} ${sun("Leo")} ${chalk.dim("Sephira:")} Tiphareth`);
178
- console.log(` ${moon2("\u263D Moon")} Emotions, instincts, the unconscious, mother`);
179
- console.log(` ${chalk.dim("Rules:")} ${moon2("Cancer")} ${chalk.dim("Sephira:")} Yesod`);
180
- console.log(` ${mercury("\u263F Mercury")} Mind, communication, learning, commerce`);
181
- console.log(` ${chalk.dim("Rules:")} ${mercury("Gemini, Virgo")} ${chalk.dim("Sephira:")} Hod`);
182
- console.log(` ${venus("\u2640 Venus")} Love, beauty, values, attraction, harmony`);
183
- console.log(` ${chalk.dim("Rules:")} ${venus("Taurus, Libra")} ${chalk.dim("Sephira:")} Netzach`);
184
- console.log(` ${mars("\u2642 Mars")} Action, desire, aggression, courage, drive`);
185
- console.log(` ${chalk.dim("Rules:")} ${mars("Aries")} ${chalk.dim("Sephira:")} Geburah`);
186
- console.log(` ${jupiter("\u2643 Jupiter")} Expansion, luck, wisdom, abundance, faith`);
187
- console.log(` ${chalk.dim("Rules:")} ${jupiter("Sagittarius")} ${chalk.dim("Sephira:")} Chesed`);
188
- console.log(` ${saturn("\u2644 Saturn")} Structure, limits, time, karma, discipline`);
189
- console.log(` ${chalk.dim("Rules:")} ${saturn("Capricorn")} ${chalk.dim("Sephira:")} Binah`);
190
- console.log(` ${uranus("\u2645 Uranus")} Revolution, awakening, innovation, freedom`);
191
- console.log(` ${chalk.dim("Rules:")} ${uranus("Aquarius")} ${chalk.dim("Sephira:")} Chokmah`);
192
- console.log(` ${neptune("\u2646 Neptune")} Dreams, illusion, spirituality, dissolution`);
193
- console.log(` ${chalk.dim("Rules:")} ${neptune("Pisces")}`);
194
- console.log(` ${pluto("\u2647 Pluto")} Transformation, death/rebirth, power, shadow`);
195
- console.log(` ${chalk.dim("Rules:")} ${pluto("Scorpio")}`);
504
+ console.log(` ${moon2("\u263D Moon")} Emotions, instincts, the unconscious`);
505
+ console.log(` ${mercury("\u263F Mercury")} Mind, communication, learning`);
506
+ console.log(` ${venus("\u2640 Venus")} Love, beauty, values, attraction`);
507
+ console.log(` ${mars("\u2642 Mars")} Action, desire, aggression, drive`);
508
+ console.log(` ${jupiter("\u2643 Jupiter")} Expansion, luck, wisdom, abundance`);
509
+ console.log(` ${saturn("\u2644 Saturn")} Structure, limits, time, karma`);
510
+ console.log(` ${uranus("\u2645 Uranus")} Revolution, awakening, innovation`);
511
+ console.log(` ${neptune("\u2646 Neptune")} Dreams, illusion, spirituality`);
512
+ console.log(` ${pluto("\u2647 Pluto")} Transformation, death/rebirth, power`);
196
513
  console.log("");
197
514
  console.log(chalk.bold.cyan("\u2550\u2550 POINTS \u2550\u2550"));
198
- console.log(` ${chiron("\u26B7 Chiron")} The wounded healer. Core wound & gift of healing.`);
199
- console.log(` ${lilith("\u26B8 Lilith")} Black Moon. Primal feminine, shadow, rejection.`);
200
- console.log(` ${northNode("\u260A North Node")} Soul's direction. Growth edge. Future karma.`);
201
- console.log(` ${southNode("\u260B South Node")} Past life gifts. Comfort zone. Past karma.`);
202
- console.log("");
203
- console.log(chalk.bold.cyan("\u2550\u2550 ZODIAC SIGNS \u2550\u2550"));
204
- console.log(` ${mars("\u2648 Aries")} ${chalk.dim("Cardinal Fire")} Initiative, courage, impatience`);
205
- console.log(` ${chalk.dim("Ruler:")} ${mars("Mars")} ${chalk.dim('"I AM"')}`);
206
- console.log(` ${venus("\u2649 Taurus")} ${chalk.dim("Fixed Earth")} Stability, sensuality, stubborn`);
207
- console.log(` ${chalk.dim("Ruler:")} ${venus("Venus")} ${chalk.dim('"I HAVE"')}`);
208
- console.log(` ${mercury("\u264A Gemini")} ${chalk.dim("Mutable Air")} Curiosity, duality, scattered`);
209
- console.log(` ${chalk.dim("Ruler:")} ${mercury("Mercury")} ${chalk.dim('"I THINK"')}`);
210
- console.log(` ${moon2("\u264B Cancer")} ${chalk.dim("Cardinal Water")} Nurturing, protective, moody`);
211
- console.log(` ${chalk.dim("Ruler:")} ${moon2("Moon")} ${chalk.dim('"I FEEL"')}`);
212
- console.log(` ${sun("\u264C Leo")} ${chalk.dim("Fixed Fire")} Creative, proud, dramatic`);
213
- console.log(` ${chalk.dim("Ruler:")} ${sun("Sun")} ${chalk.dim('"I WILL"')}`);
214
- console.log(` ${mercury("\u264D Virgo")} ${chalk.dim("Mutable Earth")} Analytical, service, critical`);
215
- console.log(` ${chalk.dim("Ruler:")} ${mercury("Mercury")} ${chalk.dim('"I ANALYZE"')}`);
216
- console.log(` ${venus("\u264E Libra")} ${chalk.dim("Cardinal Air")} Balance, partnership, indecisive`);
217
- console.log(` ${chalk.dim("Ruler:")} ${venus("Venus")} ${chalk.dim('"I BALANCE"')}`);
218
- console.log(` ${pluto("\u264F Scorpio")} ${chalk.dim("Fixed Water")} Intensity, depth, secretive`);
219
- console.log(` ${chalk.dim("Ruler:")} ${pluto("Pluto")} ${chalk.dim('"I DESIRE"')}`);
220
- console.log(` ${jupiter("\u2650 Sagittarius")} ${chalk.dim("Mutable Fire")} Adventure, truth, reckless`);
221
- console.log(` ${chalk.dim("Ruler:")} ${jupiter("Jupiter")} ${chalk.dim('"I SEE"')}`);
222
- console.log(` ${saturn("\u2651 Capricorn")} ${chalk.dim("Cardinal Earth")} Ambition, mastery, cold`);
223
- console.log(` ${chalk.dim("Ruler:")} ${saturn("Saturn")} ${chalk.dim('"I USE"')}`);
224
- console.log(` ${uranus("\u2652 Aquarius")} ${chalk.dim("Fixed Air")} Humanitarian, eccentric, detached`);
225
- console.log(` ${chalk.dim("Ruler:")} ${uranus("Uranus")} ${chalk.dim('"I KNOW"')}`);
226
- console.log(` ${neptune("\u2653 Pisces")} ${chalk.dim("Mutable Water")} Compassion, dreams, escapist`);
227
- console.log(` ${chalk.dim("Ruler:")} ${neptune("Neptune")} ${chalk.dim('"I BELIEVE"')}`);
228
- console.log("");
229
- console.log(chalk.bold.cyan("\u2550\u2550 HOUSES \u2550\u2550"));
230
- console.log(` ${chalk.bold("1st House")} ${mars("Ascendant")} Self, identity, appearance, first impressions`);
231
- console.log(` ${chalk.bold("2nd House")} Money, possessions, values, self-worth`);
232
- console.log(` ${chalk.bold("3rd House")} Communication, siblings, short travel, learning`);
233
- console.log(` ${chalk.bold("4th House")} ${saturn("IC")} Home, family, roots, private self, mother`);
234
- console.log(` ${chalk.bold("5th House")} Creativity, romance, children, pleasure, play`);
235
- console.log(` ${chalk.bold("6th House")} Health, work, service, daily routines, pets`);
236
- console.log(` ${chalk.bold("7th House")} ${chalk.white("Descendant")} Partnerships, marriage, open enemies, others`);
237
- console.log(` ${chalk.bold("8th House")} Death, sex, shared resources, transformation`);
238
- console.log(` ${chalk.bold("9th House")} Philosophy, long travel, higher education, beliefs`);
239
- console.log(` ${chalk.bold("10th House")} ${sun("MC")} Career, reputation, public self, father`);
240
- console.log(` ${chalk.bold("11th House")} Friends, groups, hopes, wishes, humanity`);
241
- console.log(` ${chalk.bold("12th House")} Unconscious, isolation, secrets, karma, dreams`);
515
+ console.log(` ${chiron("\u26B7 Chiron")} Wound & gift of healing`);
516
+ console.log(` ${lilith("\u26B8 Lilith")} Primal feminine, shadow`);
517
+ console.log(` ${northNode("\u260A North Node")} Soul's direction`);
518
+ console.log(` ${southNode("\u260B South Node")} Past karma`);
242
519
  console.log("");
243
520
  console.log(chalk.bold.cyan("\u2550\u2550 ASPECTS \u2550\u2550"));
244
- console.log(` ${sun("\u260C Conjunction")} 0\xB0 Fusion, intensification, new cycle`);
245
- console.log(` ${chalk.dim("Sephira: Tiphareth (Sun) \u2014 creative union")}`);
246
- console.log(` ${moon2("\u260D Opposition")} 180\xB0 Awareness, tension, projection`);
247
- console.log(` ${chalk.dim("Sephira: Yesod (Moon) \u2014 polarity, reflection")}`);
248
- console.log(` ${jupiter("\u25B3 Trine")} 120\xB0 Harmony, ease, natural talent`);
249
- console.log(` ${chalk.dim("Sephira: Chesed (Jupiter) \u2014 grace, flow")}`);
250
- console.log(` ${mars("\u25A1 Square")} 90\xB0 Friction, challenge, forced growth`);
251
- console.log(` ${chalk.dim("Sephira: Geburah (Mars) \u2014 strength through struggle")}`);
252
- console.log(` ${venus("\u26B9 Sextile")} 60\xB0 Opportunity, cooperation, ease`);
253
- console.log(` ${chalk.dim("Sephira: Netzach (Venus) \u2014 gentle harmony")}`);
254
- console.log(` ${mercury("\u235F Quintile")} 72\xB0 Creativity, talent, genius`);
255
- console.log(` ${chalk.dim("Sephira: Hod (Mercury) \u2014 mental brilliance")}`);
256
- console.log(` ${uranus("\u26BB Quincunx")} 150\xB0 Adjustment, awkwardness, recalibration`);
521
+ console.log(` ${sun("\u260C")} Conjunction 0\xB0 Fusion, intensification`);
522
+ console.log(` ${moon2("\u260D")} Opposition 180\xB0 Tension, awareness`);
523
+ console.log(` ${jupiter("\u25B3")} Trine 120\xB0 Harmony, ease`);
524
+ console.log(` ${mars("\u25A1")} Square 90\xB0 Challenge, growth`);
525
+ console.log(` ${venus("\u26B9")} Sextile 60\xB0 Opportunity`);
257
526
  console.log("");
258
527
  console.log(chalk.bold.cyan("\u2550\u2550 ELEMENTS \u2550\u2550"));
259
- console.log(` ${mars("\u{1F702} Fire")} ${mars("Aries, Leo, Sagittarius")}`);
260
- console.log(` Spirit, will, action, enthusiasm`);
261
- console.log(` ${chalk.dim("Hot & Dry \u2014 Choleric \u2014 Intuitive function")}`);
262
- console.log(` ${venus("\u{1F703} Earth")} ${venus("Taurus, Virgo, Capricorn")}`);
263
- console.log(` Matter, stability, practicality, form`);
264
- console.log(` ${chalk.dim("Cold & Dry \u2014 Melancholic \u2014 Sensation function")}`);
265
- console.log(` ${mercury("\u{1F701} Air")} ${mercury("Gemini, Libra, Aquarius")}`);
266
- console.log(` Mind, communication, connection, ideas`);
267
- console.log(` ${chalk.dim("Hot & Wet \u2014 Sanguine \u2014 Thinking function")}`);
268
- console.log(` ${moon2("\u{1F704} Water")} ${moon2("Cancer")}, ${pluto("Scorpio")}, ${neptune("Pisces")}`);
269
- console.log(` Emotion, intuition, the unconscious, soul`);
270
- console.log(` ${chalk.dim("Cold & Wet \u2014 Phlegmatic \u2014 Feeling function")}`);
271
- console.log("");
272
- console.log(chalk.bold.cyan("\u2550\u2550 MODALITIES \u2550\u2550"));
273
- console.log(` ${chalk.bold("Cardinal")} ${mars("Aries")}, ${moon2("Cancer")}, ${venus("Libra")}, ${saturn("Capricorn")}`);
274
- console.log(` Initiating, leadership, action`);
275
- console.log(` ${chalk.dim('The spark. Begins seasons. "I start."')}`);
276
- console.log(` ${chalk.bold("Fixed")} ${venus("Taurus")}, ${sun("Leo")}, ${pluto("Scorpio")}, ${uranus("Aquarius")}`);
277
- console.log(` Stabilizing, persistence, stubborn`);
278
- console.log(` ${chalk.dim('The sustainer. Mid-season. "I maintain."')}`);
279
- console.log(` ${chalk.bold("Mutable")} ${mercury("Gemini")}, ${mercury("Virgo")}, ${jupiter("Sagittarius")}, ${neptune("Pisces")}`);
280
- console.log(` Adapting, flexible, changeable`);
281
- console.log(` ${chalk.dim('The dissolver. End of season. "I change."')}`);
528
+ console.log(` ${mars("\u{1F702} Fire")} Spirit, will, action`);
529
+ console.log(` ${venus("\u{1F703} Earth")} Matter, stability`);
530
+ console.log(` ${mercury("\u{1F701} Air")} Mind, communication`);
531
+ console.log(` ${moon2("\u{1F704} Water")} Emotion, intuition`);
282
532
  console.log("");
283
533
  console.log(chalk.bold.cyan("\u2550\u2550 OTHER \u2550\u2550"));
284
- console.log(` ${mars("\u211E")} Retrograde Planet appears to move backward. Internalized energy.`);
285
- console.log(` H House Area of life. e.g., 4H = home, roots, private self`);
286
- console.log(` \u2192 Flow Direction. e.g., 2H\u21924H (transit house \u2192 natal house)`);
287
- console.log("");
288
- console.log(chalk.bold.cyan("\u2550\u2550 DIGNITIES \u2550\u2550"));
289
- console.log(` ${chalk.bold("Domicile")} Planet in sign it rules. Full power. ${chalk.dim("e.g., Mars in Aries")}`);
290
- console.log(` ${chalk.bold("Exaltation")} Planet in sign of peak expression. ${chalk.dim("e.g., Sun in Aries")}`);
291
- console.log(` ${chalk.bold("Detriment")} Planet opposite its home. Challenged. ${chalk.dim("e.g., Mars in Libra")}`);
292
- console.log(` ${chalk.bold("Fall")} Planet opposite exaltation. Weakened. ${chalk.dim("e.g., Sun in Libra")}`);
534
+ console.log(` \u211E Retrograde \u2014 Planet appears to move backward`);
293
535
  console.log("");
294
536
  });
295
537
  console.log(chalk.dim(""));
296
- console.log(chalk.yellow(" \u{1315D}") + chalk.dim(" thoth-cli"));
538
+ console.log(chalk.yellow(" \u{1315D}") + chalk.dim(" thoth-cli v0.2.0"));
297
539
  console.log(chalk.dim(""));
298
540
  program.parse();