thoth-cli 0.1.2 → 0.2.1
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 +367 -125
- package/dist/chunk-NEQKNEBO.js +1211 -0
- package/dist/index.d.ts +447 -6
- package/dist/index.js +33 -1
- package/package.json +1 -1
- package/dist/chunk-NYXKYHNK.js +0 -576
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-
|
|
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.
|
|
27
|
-
program.command("chart").description(
|
|
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.
|
|
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(
|
|
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.
|
|
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(`
|
|
178
|
-
console.log(` ${
|
|
179
|
-
console.log(`
|
|
180
|
-
console.log(` ${
|
|
181
|
-
console.log(`
|
|
182
|
-
console.log(` ${
|
|
183
|
-
console.log(`
|
|
184
|
-
console.log(` ${
|
|
185
|
-
console.log(`
|
|
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")}
|
|
199
|
-
console.log(` ${lilith("\u26B8 Lilith")}
|
|
200
|
-
console.log(` ${northNode("\u260A North Node")} Soul's direction
|
|
201
|
-
console.log(` ${southNode("\u260B South Node")} Past
|
|
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
|
|
245
|
-
console.log(`
|
|
246
|
-
console.log(` ${
|
|
247
|
-
console.log(`
|
|
248
|
-
console.log(` ${
|
|
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")}
|
|
260
|
-
console.log(`
|
|
261
|
-
console.log(`
|
|
262
|
-
console.log(` ${
|
|
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(`
|
|
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();
|