thoth-cli 0.2.23 → 0.2.27
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/README.md +17 -17
- package/bin/darwin-arm64/thoth-core +0 -0
- package/dist/bin.js +287 -2
- package/dist/{chunk-X7IGMJUH.js → chunk-7N4ZNTPI.js} +750 -0
- package/dist/index.d.ts +122 -1
- package/dist/index.js +29 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,19 +34,19 @@ The binary downloads automatically on first install (~18MB).
|
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
# Natal chart
|
|
37
|
-
thoth chart --date
|
|
37
|
+
thoth chart --date 1879-03-14 --time 11:30 --city "New York"
|
|
38
38
|
|
|
39
39
|
# Current transits
|
|
40
|
-
thoth transit --natal-date
|
|
40
|
+
thoth transit --natal-date 1879-03-14 --natal-time 11:30 --city "New York"
|
|
41
41
|
|
|
42
42
|
# Solar return for 2026
|
|
43
|
-
thoth solar-return --natal-date
|
|
43
|
+
thoth solar-return --natal-date 1879-03-14 --natal-time 11:30 --city "New York" --year 2026
|
|
44
44
|
|
|
45
45
|
# Horary divination
|
|
46
46
|
thoth horary --question "Should I take the job?" --city "New York"
|
|
47
47
|
|
|
48
48
|
# Relationship synastry
|
|
49
|
-
thoth synastry --date1
|
|
49
|
+
thoth synastry --date1 1879-03-14 --time1 11:30 --city1 "NYC" \
|
|
50
50
|
--date2 1990-03-15 --time2 09:30 --city2 "LA"
|
|
51
51
|
|
|
52
52
|
# Moon phase
|
|
@@ -68,9 +68,9 @@ thoth key
|
|
|
68
68
|
Calculate a complete birth chart.
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
thoth chart --date
|
|
72
|
-
thoth chart --date
|
|
73
|
-
thoth chart --date
|
|
71
|
+
thoth chart --date 1879-03-14 --time 11:30 --city "New York"
|
|
72
|
+
thoth chart --date 1879-03-14 --time 11:30 --lat 40.7128 --lng -74.0060
|
|
73
|
+
thoth chart --date 1879-03-14 --time 11:30 --city "New York" --svg-file chart.svg
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
| Flag | Description | Required |
|
|
@@ -94,8 +94,8 @@ thoth chart --date 1991-07-08 --time 14:06 --city "New York" --svg-file chart.sv
|
|
|
94
94
|
Calculate transits to a natal chart.
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
thoth transit --natal-date
|
|
98
|
-
thoth transit --natal-date
|
|
97
|
+
thoth transit --natal-date 1879-03-14 --natal-time 11:30 --city "New York"
|
|
98
|
+
thoth transit --natal-date 1879-03-14 --natal-time 11:30 --city "NYC" --orb 1
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
| Flag | Description | Required |
|
|
@@ -115,7 +115,7 @@ thoth transit --natal-date 1991-07-08 --natal-time 14:06 --city "NYC" --orb 1
|
|
|
115
115
|
Calculate solar return chart for a specific year.
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
thoth solar-return --natal-date
|
|
118
|
+
thoth solar-return --natal-date 1879-03-14 --natal-time 11:30 --city "NYC" --year 2026
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
| Flag | Description | Required |
|
|
@@ -133,7 +133,7 @@ thoth solar-return --natal-date 1991-07-08 --natal-time 14:06 --city "NYC" --yea
|
|
|
133
133
|
Calculate next lunar return from a given date.
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
thoth lunar-return --natal-date
|
|
136
|
+
thoth lunar-return --natal-date 1879-03-14 --natal-time 11:30 --city "NYC" --from 2026-03-01
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
| Flag | Description | Required |
|
|
@@ -151,7 +151,7 @@ thoth lunar-return --natal-date 1991-07-08 --natal-time 14:06 --city "NYC" --fro
|
|
|
151
151
|
Calculate synastry aspects between two charts.
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
thoth synastry --date1
|
|
154
|
+
thoth synastry --date1 1879-03-14 --time1 11:30 --city1 "NYC" \
|
|
155
155
|
--date2 1990-03-15 --time2 09:30 --city2 "LA"
|
|
156
156
|
```
|
|
157
157
|
|
|
@@ -171,7 +171,7 @@ thoth synastry --date1 1991-07-08 --time1 14:06 --city1 "NYC" \
|
|
|
171
171
|
Calculate composite (midpoint) chart for a relationship.
|
|
172
172
|
|
|
173
173
|
```bash
|
|
174
|
-
thoth composite --date1
|
|
174
|
+
thoth composite --date1 1879-03-14 --time1 11:30 --city1 "NYC" \
|
|
175
175
|
--date2 1990-03-15 --time2 09:30 --city2 "LA"
|
|
176
176
|
```
|
|
177
177
|
|
|
@@ -184,7 +184,7 @@ Same options as synastry. Creates a merged chart representing the relationship i
|
|
|
184
184
|
Calculate secondary progressions (day-for-a-year method).
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
thoth progressions --natal-date
|
|
187
|
+
thoth progressions --natal-date 1879-03-14 --natal-time 11:30 --city "NYC" \
|
|
188
188
|
--target-date 2026-03-01
|
|
189
189
|
```
|
|
190
190
|
|
|
@@ -203,7 +203,7 @@ thoth progressions --natal-date 1991-07-08 --natal-time 14:06 --city "NYC" \
|
|
|
203
203
|
Calculate solar arc directions (all planets advance by Sun's arc).
|
|
204
204
|
|
|
205
205
|
```bash
|
|
206
|
-
thoth solar-arc --natal-date
|
|
206
|
+
thoth solar-arc --natal-date 1879-03-14 --natal-time 11:30 --city "NYC" \
|
|
207
207
|
--target-date 2026-03-01
|
|
208
208
|
```
|
|
209
209
|
|
|
@@ -320,7 +320,7 @@ thoth-cli uses **Sephirotic colors** based on Kabbalistic correspondences:
|
|
|
320
320
|
All commands support `--json` for programmatic use:
|
|
321
321
|
|
|
322
322
|
```bash
|
|
323
|
-
thoth chart --date
|
|
323
|
+
thoth chart --date 1879-03-14 --time 11:30 --city "NYC" --json
|
|
324
324
|
thoth horary --question "Test" --city "NYC" --json
|
|
325
325
|
```
|
|
326
326
|
|
|
@@ -367,7 +367,7 @@ npm install
|
|
|
367
367
|
npm run build
|
|
368
368
|
|
|
369
369
|
# Test
|
|
370
|
-
node dist/bin.js chart --date
|
|
370
|
+
node dist/bin.js chart --date 1879-03-14 --time 11:30 --city "New York"
|
|
371
371
|
```
|
|
372
372
|
|
|
373
373
|
---
|
|
Binary file
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
calculateGematria,
|
|
4
|
+
calculateNumerology,
|
|
5
|
+
calculatePersonalCycle,
|
|
3
6
|
chart,
|
|
7
|
+
compareGematria,
|
|
4
8
|
composite,
|
|
5
9
|
ephemeris,
|
|
6
10
|
ephemerisMulti,
|
|
@@ -10,10 +14,15 @@ import {
|
|
|
10
14
|
formatEphemeris,
|
|
11
15
|
formatEphemerisMulti,
|
|
12
16
|
formatEphemerisRange,
|
|
17
|
+
formatGematria,
|
|
18
|
+
formatGematriaCompare,
|
|
19
|
+
formatGematriaLookup,
|
|
13
20
|
formatHorary,
|
|
14
21
|
formatLunarReturn,
|
|
15
22
|
formatMoon,
|
|
16
23
|
formatMoonExtended,
|
|
24
|
+
formatNumerology,
|
|
25
|
+
formatNumerologyYear,
|
|
17
26
|
formatProgressions,
|
|
18
27
|
formatScore,
|
|
19
28
|
formatSolarArc,
|
|
@@ -27,6 +36,7 @@ import {
|
|
|
27
36
|
formatTransits,
|
|
28
37
|
horary,
|
|
29
38
|
isError,
|
|
39
|
+
lookupGematria,
|
|
30
40
|
lunarReturn,
|
|
31
41
|
moon,
|
|
32
42
|
moonExtended,
|
|
@@ -42,7 +52,7 @@ import {
|
|
|
42
52
|
transit,
|
|
43
53
|
transitScan,
|
|
44
54
|
version
|
|
45
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-7N4ZNTPI.js";
|
|
46
56
|
|
|
47
57
|
// src/bin.ts
|
|
48
58
|
import { Command } from "commander";
|
|
@@ -101,6 +111,20 @@ EPHEMERIS & MOON
|
|
|
101
111
|
thoth ephemeris --body saturn --date 2027-01-15 # saturn on specific date
|
|
102
112
|
thoth ephemeris-range --body pluto --from 2024-01-01 --to 2030-01-01 --step month
|
|
103
113
|
|
|
114
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
115
|
+
GEMATRIA
|
|
116
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
117
|
+
thoth gematria "AKLO" # all systems
|
|
118
|
+
thoth gematria "\u05D0\u05D4\u05D1\u05D4" # Hebrew text
|
|
119
|
+
thoth gematria "Love" --compare "Will" # compare two words
|
|
120
|
+
thoth gematria-lookup 93 # find words matching number
|
|
121
|
+
|
|
122
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
123
|
+
NUMEROLOGY
|
|
124
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
125
|
+
thoth numerology --name "John Doe" --date 1991-07-08 # full profile
|
|
126
|
+
thoth numerology-year --date 1991-07-08 # personal year cycles
|
|
127
|
+
|
|
104
128
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
105
129
|
REFERENCE
|
|
106
130
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
@@ -859,7 +883,268 @@ program.command("tarot-spreads").alias("spreads").description("List available ta
|
|
|
859
883
|
console.log(formatTarotSpreads(result));
|
|
860
884
|
}
|
|
861
885
|
});
|
|
886
|
+
program.command("gematria <text>").description("Calculate gematria values for text").option("-s, --system <system>", "Only show specific system (hebrew-standard, hebrew-ordinal, hebrew-reduced, greek, english-ordinal, english-reduced, english-sumerian, english-reverse)").option("-c, --compare <text2>", "Compare with another word/phrase").option("--json", "Output raw JSON").action(async (text, options) => {
|
|
887
|
+
if (options.compare) {
|
|
888
|
+
const result = compareGematria(text, options.compare, options.system);
|
|
889
|
+
if (options.json) {
|
|
890
|
+
console.log(JSON.stringify(result, null, 2));
|
|
891
|
+
} else {
|
|
892
|
+
console.log(formatGematriaCompare(result));
|
|
893
|
+
}
|
|
894
|
+
} else {
|
|
895
|
+
const result = calculateGematria(text, options.system);
|
|
896
|
+
if (options.json) {
|
|
897
|
+
console.log(JSON.stringify(result, null, 2));
|
|
898
|
+
} else {
|
|
899
|
+
console.log(formatGematria(result));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
program.command("gematria-lookup <number>").description("Find words/concepts matching a gematria value").option("-s, --system <system>", "Filter by system (hebrew, greek, english)").option("-l, --limit <n>", "Maximum results", parseInt).option("--json", "Output raw JSON").action(async (number, options) => {
|
|
904
|
+
const num = parseInt(number, 10);
|
|
905
|
+
if (isNaN(num)) {
|
|
906
|
+
console.error(chalk.red("Error: Please provide a valid number"));
|
|
907
|
+
process.exit(1);
|
|
908
|
+
}
|
|
909
|
+
const result = lookupGematria(num, options.system, options.limit);
|
|
910
|
+
if (options.json) {
|
|
911
|
+
console.log(JSON.stringify(result, null, 2));
|
|
912
|
+
} else {
|
|
913
|
+
console.log(formatGematriaLookup(result));
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
program.command("numerology [input]").description("Calculate core numerology numbers").option("-d, --date <date>", "Birth date (YYYY-MM-DD)").option("-n, --name <name>", "Full birth name").option("--json", "Output raw JSON").action(async (input, options) => {
|
|
917
|
+
let name = options.name;
|
|
918
|
+
let date = options.date;
|
|
919
|
+
if (input) {
|
|
920
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(input)) {
|
|
921
|
+
date = date || input;
|
|
922
|
+
} else {
|
|
923
|
+
name = name || input;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (!name && !date) {
|
|
927
|
+
console.error(chalk.red("Error: Provide --name and/or --date, or a positional argument"));
|
|
928
|
+
process.exit(1);
|
|
929
|
+
}
|
|
930
|
+
const result = calculateNumerology({ name, date });
|
|
931
|
+
if (options.json) {
|
|
932
|
+
console.log(JSON.stringify(result, null, 2));
|
|
933
|
+
} else {
|
|
934
|
+
console.log(formatNumerology(result));
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
program.command("numerology-year").description("Calculate Personal Year, Month, and Day numbers").requiredOption("-d, --date <date>", "Birth date (YYYY-MM-DD)").option("-t, --target-date <date>", "Target date (YYYY-MM-DD, default: today)").option("--json", "Output raw JSON").action(async (options) => {
|
|
938
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(options.date)) {
|
|
939
|
+
console.error(chalk.red("Error: Date must be in YYYY-MM-DD format"));
|
|
940
|
+
process.exit(1);
|
|
941
|
+
}
|
|
942
|
+
const result = calculatePersonalCycle(options.date, options.targetDate);
|
|
943
|
+
if (options.json) {
|
|
944
|
+
console.log(JSON.stringify(result, null, 2));
|
|
945
|
+
} else {
|
|
946
|
+
console.log(formatNumerologyYear(result));
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
var ASPECTS = [
|
|
950
|
+
{ name: "conjunction", symbol: "\u260C", angle: 0, orb: 8 },
|
|
951
|
+
{ name: "sextile", symbol: "\u26B9", angle: 60, orb: 4 },
|
|
952
|
+
{ name: "square", symbol: "\u25A1", angle: 90, orb: 6 },
|
|
953
|
+
{ name: "trine", symbol: "\u25B3", angle: 120, orb: 6 },
|
|
954
|
+
{ name: "opposition", symbol: "\u260D", angle: 180, orb: 8 }
|
|
955
|
+
];
|
|
956
|
+
function findAspect(pos1, pos2) {
|
|
957
|
+
const diff = Math.abs(pos1 - pos2);
|
|
958
|
+
const angle = diff > 180 ? 360 - diff : diff;
|
|
959
|
+
for (const aspect of ASPECTS) {
|
|
960
|
+
const orbDiff = Math.abs(angle - aspect.angle);
|
|
961
|
+
if (orbDiff <= aspect.orb) {
|
|
962
|
+
return { name: aspect.name, symbol: aspect.symbol, orb: Math.round(orbDiff * 10) / 10 };
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
function getMoonPhase(sunPos, moonPos) {
|
|
968
|
+
let angle = moonPos - sunPos;
|
|
969
|
+
if (angle < 0) angle += 360;
|
|
970
|
+
if (angle < 22.5 || angle >= 337.5) return { name: "New Moon", icon: "\u{1F311}" };
|
|
971
|
+
if (angle < 67.5) return { name: "Waxing Crescent", icon: "\u{1F312}" };
|
|
972
|
+
if (angle < 112.5) return { name: "First Quarter", icon: "\u{1F313}" };
|
|
973
|
+
if (angle < 157.5) return { name: "Waxing Gibbous", icon: "\u{1F314}" };
|
|
974
|
+
if (angle < 202.5) return { name: "Full Moon", icon: "\u{1F315}" };
|
|
975
|
+
if (angle < 247.5) return { name: "Waning Gibbous", icon: "\u{1F316}" };
|
|
976
|
+
if (angle < 292.5) return { name: "Last Quarter", icon: "\u{1F317}" };
|
|
977
|
+
return { name: "Waning Crescent", icon: "\u{1F318}" };
|
|
978
|
+
}
|
|
979
|
+
program.command("electional").description("Comprehensive electional astrology scan \u2014 moon phases, aspects, retrogrades, VOC").requiredOption("--start <date>", "Start date (YYYY-MM-DD)").requiredOption("--end <date>", "End date (YYYY-MM-DD)").option("--step <step>", "Granularity: day, week (default: day)", "day").option("--city <city>", "City for local context (optional)").option("--nation <nation>", "Country code (default: US)", "US").option("--lat <lat>", "Latitude (optional)", parseFloat).option("--lng <lng>", "Longitude (optional)", parseFloat).option("--json", "Output raw JSON").action(async (options) => {
|
|
980
|
+
const spinner = ora("Scanning date range (one efficient query)...").start();
|
|
981
|
+
const startDate = new Date(options.start);
|
|
982
|
+
const endDate = new Date(options.end);
|
|
983
|
+
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
|
984
|
+
spinner.stop();
|
|
985
|
+
console.error(chalk.red("Error: Invalid date format. Use YYYY-MM-DD"));
|
|
986
|
+
process.exit(1);
|
|
987
|
+
}
|
|
988
|
+
if (endDate <= startDate) {
|
|
989
|
+
spinner.stop();
|
|
990
|
+
console.error(chalk.red("Error: End date must be after start date"));
|
|
991
|
+
process.exit(1);
|
|
992
|
+
}
|
|
993
|
+
const [startYear, startMonth, startDay] = options.start.split("-").map(Number);
|
|
994
|
+
const [endYear, endMonth, endDay] = options.end.split("-").map(Number);
|
|
995
|
+
const location = options.city ? `${options.city}, ${options.nation}` : options.lat && options.lng ? `${options.lat}\xB0, ${options.lng}\xB0` : null;
|
|
996
|
+
const multiResult = await ephemerisMulti({
|
|
997
|
+
bodies: "sun,moon,mercury,venus,mars,jupiter,saturn,uranus,neptune,pluto",
|
|
998
|
+
startYear,
|
|
999
|
+
startMonth,
|
|
1000
|
+
startDay,
|
|
1001
|
+
endYear,
|
|
1002
|
+
endMonth,
|
|
1003
|
+
endDay,
|
|
1004
|
+
step: options.step,
|
|
1005
|
+
lat: options.lat,
|
|
1006
|
+
lng: options.lng
|
|
1007
|
+
});
|
|
1008
|
+
spinner.stop();
|
|
1009
|
+
if (isError(multiResult)) {
|
|
1010
|
+
console.error(chalk.red(`Error: ${multiResult.error}`));
|
|
1011
|
+
process.exit(1);
|
|
1012
|
+
}
|
|
1013
|
+
const positions = multiResult.positions || [];
|
|
1014
|
+
const days = Math.ceil((endDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
1015
|
+
const dailyData = [];
|
|
1016
|
+
const keyDates = [];
|
|
1017
|
+
const retrogradeWindows = {};
|
|
1018
|
+
let prevMoonSign = "";
|
|
1019
|
+
for (const pos of positions) {
|
|
1020
|
+
const dateStr = pos.datetime.split("T")[0];
|
|
1021
|
+
const sun = pos.sun;
|
|
1022
|
+
const moonData = pos.moon;
|
|
1023
|
+
const phase = getMoonPhase(sun.abs_position, moonData.abs_position);
|
|
1024
|
+
const sunMoonAngle = Math.abs(moonData.abs_position - sun.abs_position);
|
|
1025
|
+
const normalizedAngle = sunMoonAngle > 180 ? 360 - sunMoonAngle : sunMoonAngle;
|
|
1026
|
+
if (normalizedAngle < 5) {
|
|
1027
|
+
keyDates.push({ date: dateStr, event: `New Moon in ${moonData.sign}`, quality: "beginnings" });
|
|
1028
|
+
} else if (Math.abs(normalizedAngle - 180) < 5) {
|
|
1029
|
+
keyDates.push({ date: dateStr, event: `Full Moon in ${moonData.sign}`, quality: "culmination" });
|
|
1030
|
+
}
|
|
1031
|
+
if (prevMoonSign && prevMoonSign !== moonData.sign) {
|
|
1032
|
+
keyDates.push({ date: dateStr, event: `Moon enters ${moonData.sign}`, quality: "ingress" });
|
|
1033
|
+
}
|
|
1034
|
+
prevMoonSign = moonData.sign;
|
|
1035
|
+
const planets = {};
|
|
1036
|
+
const planetNames = ["mercury", "venus", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto"];
|
|
1037
|
+
for (const pName of planetNames) {
|
|
1038
|
+
const p = pos[pName];
|
|
1039
|
+
if (p) {
|
|
1040
|
+
planets[pName] = { sign: p.sign, degree: p.position, retrograde: p.retrograde, absPos: p.abs_position };
|
|
1041
|
+
if (p.retrograde) {
|
|
1042
|
+
if (!retrogradeWindows[pName]) {
|
|
1043
|
+
retrogradeWindows[pName] = { start: dateStr, sign: p.sign };
|
|
1044
|
+
}
|
|
1045
|
+
retrogradeWindows[pName].end = dateStr;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
const aspects = [];
|
|
1050
|
+
const allBodies = [{ name: "moon", pos: moonData.abs_position }, ...Object.entries(planets).map(([n, d]) => ({ name: n, pos: d.absPos }))];
|
|
1051
|
+
for (let i = 0; i < allBodies.length; i++) {
|
|
1052
|
+
for (let j = i + 1; j < allBodies.length; j++) {
|
|
1053
|
+
const asp = findAspect(allBodies[i].pos, allBodies[j].pos);
|
|
1054
|
+
if (asp) {
|
|
1055
|
+
aspects.push({
|
|
1056
|
+
planet1: allBodies[i].name,
|
|
1057
|
+
planet2: allBodies[j].name,
|
|
1058
|
+
aspect: asp.name,
|
|
1059
|
+
symbol: asp.symbol,
|
|
1060
|
+
orb: asp.orb
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
const moonAspects = aspects.filter((a) => a.planet1 === "moon" || a.planet2 === "moon");
|
|
1066
|
+
const tightMoonAspects = moonAspects.filter((a) => a.orb < 3);
|
|
1067
|
+
const vocMoon = tightMoonAspects.length === 0;
|
|
1068
|
+
let score2 = 0;
|
|
1069
|
+
for (const asp of aspects) {
|
|
1070
|
+
if (asp.aspect === "trine" || asp.aspect === "sextile") score2 += 1;
|
|
1071
|
+
if (asp.aspect === "square") score2 -= 1;
|
|
1072
|
+
if (asp.aspect === "opposition") score2 -= 0.5;
|
|
1073
|
+
if ((asp.planet1 === "jupiter" || asp.planet2 === "jupiter") && (asp.aspect === "trine" || asp.aspect === "sextile")) score2 += 1;
|
|
1074
|
+
if ((asp.planet1 === "venus" || asp.planet2 === "venus") && (asp.aspect === "trine" || asp.aspect === "sextile")) score2 += 0.5;
|
|
1075
|
+
}
|
|
1076
|
+
if (planets.mercury?.retrograde) score2 -= 2;
|
|
1077
|
+
const quality = score2 >= 4 ? "excellent" : score2 >= 2 ? "good" : score2 >= 0 ? "neutral" : score2 >= -2 ? "challenging" : "difficult";
|
|
1078
|
+
dailyData.push({
|
|
1079
|
+
date: dateStr,
|
|
1080
|
+
moon: { sign: moonData.sign, degree: moonData.position, phase: phase.name, phaseIcon: phase.icon },
|
|
1081
|
+
planets: Object.fromEntries(Object.entries(planets).map(([k, v]) => [k, { sign: v.sign, degree: v.degree, retrograde: v.retrograde }])),
|
|
1082
|
+
aspects,
|
|
1083
|
+
vocMoon,
|
|
1084
|
+
quality
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
const output = {
|
|
1088
|
+
range: { start: options.start, end: options.end, days, step: options.step, location },
|
|
1089
|
+
dailyData,
|
|
1090
|
+
keyDates,
|
|
1091
|
+
retrogradeWindows,
|
|
1092
|
+
summary: {
|
|
1093
|
+
excellentDays: dailyData.filter((d) => d.quality === "excellent").map((d) => d.date),
|
|
1094
|
+
goodDays: dailyData.filter((d) => d.quality === "good").map((d) => d.date),
|
|
1095
|
+
avoidDays: dailyData.filter((d) => d.quality === "difficult").map((d) => d.date),
|
|
1096
|
+
vocMoonDays: dailyData.filter((d) => d.vocMoon).map((d) => d.date)
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
if (options.json) {
|
|
1100
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1101
|
+
} else {
|
|
1102
|
+
console.log(chalk.yellow("\n\u{1315D}") + chalk.dim(" Electional Astrology Scan"));
|
|
1103
|
+
console.log(chalk.dim(` ${output.range.start} to ${output.range.end} (${output.range.days} days)
|
|
1104
|
+
`));
|
|
1105
|
+
console.log(chalk.dim("\u2500\u2500 SUMMARY \u2500\u2500"));
|
|
1106
|
+
if (output.summary.excellentDays.length > 0) {
|
|
1107
|
+
console.log(chalk.green(` \u2605 Excellent: ${output.summary.excellentDays.join(", ")}`));
|
|
1108
|
+
}
|
|
1109
|
+
if (output.summary.goodDays.length > 0) {
|
|
1110
|
+
console.log(chalk.cyan(` \u2713 Good: ${output.summary.goodDays.join(", ")}`));
|
|
1111
|
+
}
|
|
1112
|
+
if (output.summary.avoidDays.length > 0) {
|
|
1113
|
+
console.log(chalk.red(` \u2717 Avoid: ${output.summary.avoidDays.join(", ")}`));
|
|
1114
|
+
}
|
|
1115
|
+
if (output.summary.vocMoonDays.length > 0) {
|
|
1116
|
+
console.log(chalk.yellow(` \u25EF VOC Moon: ${output.summary.vocMoonDays.join(", ")}`));
|
|
1117
|
+
}
|
|
1118
|
+
const rxPlanets = Object.entries(output.retrogradeWindows);
|
|
1119
|
+
if (rxPlanets.length > 0) {
|
|
1120
|
+
console.log(chalk.dim("\n\u2500\u2500 RETROGRADES \u2500\u2500"));
|
|
1121
|
+
for (const [planet, window] of rxPlanets) {
|
|
1122
|
+
console.log(chalk.red(` \u211E ${planet.charAt(0).toUpperCase() + planet.slice(1)} in ${window.sign} (${window.start} \u2013 ${window.end})`));
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (output.keyDates.length > 0) {
|
|
1126
|
+
console.log(chalk.dim("\n\u2500\u2500 KEY DATES \u2500\u2500"));
|
|
1127
|
+
for (const kd of output.keyDates) {
|
|
1128
|
+
const icon = kd.quality === "beginnings" ? "\u{1F311}" : kd.quality === "culmination" ? "\u{1F315}" : kd.quality === "ingress" ? "\u2192" : "\u2728";
|
|
1129
|
+
console.log(` ${kd.date} ${icon} ${kd.event}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
console.log(chalk.dim("\n\u2500\u2500 DAILY POSITIONS \u2500\u2500"));
|
|
1133
|
+
for (const day of dailyData) {
|
|
1134
|
+
const qColor = day.quality === "excellent" ? chalk.green : day.quality === "good" ? chalk.cyan : day.quality === "challenging" ? chalk.yellow : day.quality === "difficult" ? chalk.red : chalk.white;
|
|
1135
|
+
const qIcon = day.quality === "excellent" ? "\u2605" : day.quality === "good" ? "\u2713" : day.quality === "challenging" ? "!" : day.quality === "difficult" ? "\u2717" : "\xB7";
|
|
1136
|
+
const vocFlag = day.vocMoon ? chalk.yellow(" [VOC]") : "";
|
|
1137
|
+
console.log(qColor(` ${day.date} ${qIcon} ${day.moon.phaseIcon} Moon ${day.moon.sign} ${day.moon.degree.toFixed(0)}\xB0${vocFlag}`));
|
|
1138
|
+
const tightAspects = day.aspects.filter((a) => a.orb < 2);
|
|
1139
|
+
if (tightAspects.length > 0) {
|
|
1140
|
+
const aspStr = tightAspects.map((a) => `${a.planet1}${a.symbol}${a.planet2}`).join(" ");
|
|
1141
|
+
console.log(chalk.dim(` ${aspStr}`));
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
console.log("");
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
862
1147
|
console.log(chalk.dim(""));
|
|
863
|
-
console.log(chalk.yellow(" \u{1315D}") + chalk.dim(" thoth-cli v0.2.
|
|
1148
|
+
console.log(chalk.yellow(" \u{1315D}") + chalk.dim(" thoth-cli v0.2.27"));
|
|
864
1149
|
console.log(chalk.dim(""));
|
|
865
1150
|
program.parse();
|