thoth-cli 0.1.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/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # 𓅝 thoth-cli
2
+
3
+ Hermetic astrology from the command line.
4
+
5
+ See the [main README](../../README.md) for full documentation.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install -g thoth-cli
11
+
12
+ thoth chart --date 1879-03-14 --time 11:30 --city "Ulm" --nation "DE" --name "Einstein"
13
+ thoth transit --natal-date 1879-03-14 --natal-time 11:30 --city "Ulm" --nation "DE"
14
+ thoth moon
15
+ thoth ephemeris --body pluto
16
+ thoth key
17
+ ```
18
+
19
+ ## Privacy
20
+
21
+ No data collection. All calculations run locally.
22
+
23
+ ## License
24
+
25
+ MIT
File without changes
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/bin.js ADDED
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ chart,
4
+ ephemeris,
5
+ formatChart,
6
+ formatEphemeris,
7
+ formatMoon,
8
+ formatTransits,
9
+ isError,
10
+ moon,
11
+ transit,
12
+ version
13
+ } from "./chunk-NYXKYHNK.js";
14
+
15
+ // src/bin.ts
16
+ import { Command } from "commander";
17
+ import chalk from "chalk";
18
+ import ora from "ora";
19
+ var program = new Command();
20
+ program.name("thoth").description(`\u{1315D} Astrological calculations from the command line
21
+
22
+ Examples:
23
+ thoth chart --date 1991-07-08 --time 14:06 --city "New York"
24
+ thoth transit --natal-date 1991-07-08 --natal-time 14:06 --city "New York"
25
+ 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) => {
32
+ if (!options.city && (!options.lat || !options.lng)) {
33
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
34
+ process.exit(1);
35
+ }
36
+ const spinner = ora("Calculating chart...").start();
37
+ const [year, month, day] = options.date.split("-").map(Number);
38
+ const [hour, minute] = options.time.split(":").map(Number);
39
+ const result = await chart({
40
+ year,
41
+ month,
42
+ day,
43
+ hour,
44
+ minute,
45
+ lat: options.lat,
46
+ lng: options.lng,
47
+ city: options.city,
48
+ nation: options.nation,
49
+ name: options.name
50
+ });
51
+ spinner.stop();
52
+ if (isError(result)) {
53
+ console.error(chalk.red(`Error: ${result.error}`));
54
+ process.exit(1);
55
+ }
56
+ if (options.json) {
57
+ console.log(JSON.stringify(result, null, 2));
58
+ } else {
59
+ console.log(formatChart(result));
60
+ }
61
+ });
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) => {
67
+ if (!options.city && (!options.lat || !options.lng)) {
68
+ console.error(chalk.red("Error: Must provide either --city or both --lat and --lng"));
69
+ process.exit(1);
70
+ }
71
+ const spinner = ora("Calculating transits...").start();
72
+ const [natalYear, natalMonth, natalDay] = options.natalDate.split("-").map(Number);
73
+ const [natalHour, natalMinute] = options.natalTime.split(":").map(Number);
74
+ let transitYear, transitMonth, transitDay;
75
+ if (options.transitDate) {
76
+ [transitYear, transitMonth, transitDay] = options.transitDate.split("-").map(Number);
77
+ }
78
+ const result = await transit({
79
+ natalYear,
80
+ natalMonth,
81
+ natalDay,
82
+ natalHour,
83
+ natalMinute,
84
+ natalLat: options.lat,
85
+ natalLng: options.lng,
86
+ natalCity: options.city,
87
+ nation: options.nation,
88
+ transitYear,
89
+ transitMonth,
90
+ transitDay,
91
+ orb: options.orb
92
+ });
93
+ spinner.stop();
94
+ if (isError(result)) {
95
+ console.error(chalk.red(`Error: ${result.error}`));
96
+ process.exit(1);
97
+ }
98
+ if (options.json) {
99
+ console.log(JSON.stringify(result, null, 2));
100
+ } else {
101
+ console.log(formatTransits(result));
102
+ }
103
+ });
104
+ 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
+ const spinner = ora("Getting moon phase...").start();
106
+ let year, month, day;
107
+ if (options.date) {
108
+ [year, month, day] = options.date.split("-").map(Number);
109
+ }
110
+ const result = await moon({
111
+ year,
112
+ month,
113
+ day,
114
+ lat: options.lat,
115
+ lng: options.lng
116
+ });
117
+ spinner.stop();
118
+ if (isError(result)) {
119
+ console.error(chalk.red(`Error: ${result.error}`));
120
+ process.exit(1);
121
+ }
122
+ if (options.json) {
123
+ console.log(JSON.stringify(result, null, 2));
124
+ } else {
125
+ console.log(formatMoon(result));
126
+ }
127
+ });
128
+ program.command("ephemeris").description("Get ephemeris position for a celestial body").requiredOption("--body <body>", "Celestial body (sun, moon, mars, etc.)").option("--date <date>", "Date (YYYY-MM-DD, default: today)").option("--json", "Output raw JSON").action(async (options) => {
129
+ const spinner = ora(`Getting ${options.body} position...`).start();
130
+ let year, month, day;
131
+ if (options.date) {
132
+ [year, month, day] = options.date.split("-").map(Number);
133
+ }
134
+ const result = await ephemeris({
135
+ body: options.body,
136
+ year,
137
+ month,
138
+ day
139
+ });
140
+ spinner.stop();
141
+ if (isError(result)) {
142
+ console.error(chalk.red(`Error: ${result.error}`));
143
+ process.exit(1);
144
+ }
145
+ if (options.json) {
146
+ console.log(JSON.stringify(result, null, 2));
147
+ } else {
148
+ console.log(formatEphemeris(result));
149
+ }
150
+ });
151
+ program.command("core-version").description("Show thoth-core version").action(async () => {
152
+ const result = await version();
153
+ if (isError(result)) {
154
+ console.error(chalk.red(`Error: ${result.error}`));
155
+ process.exit(1);
156
+ }
157
+ console.log(chalk.cyan(`thoth-core v${result.version}`));
158
+ });
159
+ program.command("key").description("Symbol reference guide").action(() => {
160
+ const sun = chalk.hex("#FFD700");
161
+ const moon2 = chalk.hex("#C0C0C0");
162
+ const mercury = chalk.hex("#FF8C00");
163
+ const venus = chalk.hex("#00FF7F");
164
+ const mars = chalk.hex("#FF0000");
165
+ const jupiter = chalk.hex("#4169E1");
166
+ const saturn = chalk.hex("#4B0082");
167
+ const uranus = chalk.hex("#00FFFF");
168
+ const neptune = chalk.hex("#20B2AA");
169
+ const pluto = chalk.hex("#8B0000");
170
+ const chiron = chalk.hex("#9932CC");
171
+ const lilith = chalk.hex("#800020");
172
+ const northNode = chalk.hex("#FFD700");
173
+ const southNode = chalk.hex("#C0C0C0");
174
+ console.log(chalk.bold.white("\n\u{1315D} THOTH KEY \u2014 Complete Reference\n"));
175
+ console.log(chalk.bold.cyan("\u2550\u2550 PLANETS \u2550\u2550"));
176
+ 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")}`);
196
+ console.log("");
197
+ 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`);
242
+ console.log("");
243
+ 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`);
257
+ console.log("");
258
+ 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."')}`);
282
+ console.log("");
283
+ 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")}`);
293
+ console.log("");
294
+ });
295
+ console.log(chalk.dim(""));
296
+ console.log(chalk.yellow(" \u{1315D}") + chalk.dim(" thoth-cli"));
297
+ console.log(chalk.dim(""));
298
+ program.parse();
@@ -0,0 +1,576 @@
1
+ // src/lib/core.ts
2
+ import { execa } from "execa";
3
+
4
+ // src/lib/binary.ts
5
+ import { platform, arch } from "os";
6
+ import { join, dirname } from "path";
7
+ import { existsSync } from "fs";
8
+ import { fileURLToPath } from "url";
9
+ var __dirname = dirname(fileURLToPath(import.meta.url));
10
+ function getPlatformKey() {
11
+ const p = platform();
12
+ const a = arch();
13
+ const platformMap = {
14
+ "darwin": "darwin",
15
+ "linux": "linux",
16
+ "win32": "win32"
17
+ };
18
+ const archMap = {
19
+ "x64": "x64",
20
+ "arm64": "arm64"
21
+ };
22
+ const mappedPlatform = platformMap[p];
23
+ const mappedArch = archMap[a];
24
+ if (!mappedPlatform || !mappedArch) {
25
+ throw new Error(`Unsupported platform: ${p}-${a}`);
26
+ }
27
+ return `${mappedPlatform}-${mappedArch}`;
28
+ }
29
+ function getBinaryName() {
30
+ const p = platform();
31
+ return p === "win32" ? "thoth-core.exe" : "thoth-core";
32
+ }
33
+ function getBinaryPath() {
34
+ const platformKey = getPlatformKey();
35
+ const binaryName = getBinaryName();
36
+ const possiblePaths = [
37
+ // Installed via npm (production)
38
+ join(__dirname, "..", "..", "bin", platformKey, binaryName),
39
+ // Development (monorepo)
40
+ join(__dirname, "..", "..", "..", "..", "bin", platformKey, binaryName),
41
+ // Local development
42
+ join(__dirname, "..", "..", "bin", binaryName)
43
+ ];
44
+ for (const p of possiblePaths) {
45
+ if (existsSync(p)) {
46
+ return p;
47
+ }
48
+ }
49
+ return "python";
50
+ }
51
+ function getCommand() {
52
+ const binaryPath = getBinaryPath();
53
+ if (binaryPath === "python") {
54
+ return {
55
+ command: "python3",
56
+ args: ["-m", "thoth_core.cli"]
57
+ };
58
+ }
59
+ return {
60
+ command: binaryPath,
61
+ args: []
62
+ };
63
+ }
64
+
65
+ // src/lib/core.ts
66
+ async function execute(subcommand, args) {
67
+ const { command, args: baseArgs } = getCommand();
68
+ try {
69
+ const result = await execa(command, [...baseArgs, subcommand, ...args], {
70
+ encoding: "utf8",
71
+ reject: false
72
+ });
73
+ if (result.exitCode !== 0) {
74
+ const error = result.stderr || result.stdout;
75
+ try {
76
+ return JSON.parse(error);
77
+ } catch {
78
+ return { error: error || "Unknown error" };
79
+ }
80
+ }
81
+ return JSON.parse(result.stdout);
82
+ } catch (err) {
83
+ return { error: err instanceof Error ? err.message : "Unknown error" };
84
+ }
85
+ }
86
+ async function chart(options) {
87
+ const args = [
88
+ "--year",
89
+ String(options.year),
90
+ "--month",
91
+ String(options.month),
92
+ "--day",
93
+ String(options.day),
94
+ "--hour",
95
+ String(options.hour ?? 12),
96
+ "--minute",
97
+ String(options.minute ?? 0),
98
+ "--name",
99
+ options.name ?? "Subject"
100
+ ];
101
+ if (options.city) {
102
+ args.push("--city", options.city);
103
+ args.push("--nation", options.nation ?? "US");
104
+ } else if (options.lat !== void 0 && options.lng !== void 0) {
105
+ args.push("--lat", String(options.lat));
106
+ args.push("--lng", String(options.lng));
107
+ }
108
+ return execute("chart", args);
109
+ }
110
+ async function transit(options) {
111
+ const args = [
112
+ "--natal-year",
113
+ String(options.natalYear),
114
+ "--natal-month",
115
+ String(options.natalMonth),
116
+ "--natal-day",
117
+ String(options.natalDay),
118
+ "--natal-hour",
119
+ String(options.natalHour ?? 12),
120
+ "--natal-minute",
121
+ String(options.natalMinute ?? 0),
122
+ "--orb",
123
+ String(options.orb ?? 3)
124
+ ];
125
+ if (options.natalCity) {
126
+ args.push("--natal-city", options.natalCity);
127
+ args.push("--nation", options.nation ?? "US");
128
+ } else if (options.natalLat !== void 0 && options.natalLng !== void 0) {
129
+ args.push("--natal-lat", String(options.natalLat));
130
+ args.push("--natal-lng", String(options.natalLng));
131
+ }
132
+ if (options.transitYear) args.push("--transit-year", String(options.transitYear));
133
+ if (options.transitMonth) args.push("--transit-month", String(options.transitMonth));
134
+ if (options.transitDay) args.push("--transit-day", String(options.transitDay));
135
+ return execute("transit", args);
136
+ }
137
+ async function moon(options = {}) {
138
+ const args = [];
139
+ if (options.year) args.push("--year", String(options.year));
140
+ if (options.month) args.push("--month", String(options.month));
141
+ if (options.day) args.push("--day", String(options.day));
142
+ if (options.lat) args.push("--lat", String(options.lat));
143
+ if (options.lng) args.push("--lng", String(options.lng));
144
+ return execute("moon", args);
145
+ }
146
+ async function ephemeris(options) {
147
+ const args = ["--body", options.body];
148
+ if (options.year) args.push("--year", String(options.year));
149
+ if (options.month) args.push("--month", String(options.month));
150
+ if (options.day) args.push("--day", String(options.day));
151
+ return execute("ephemeris", args);
152
+ }
153
+ async function version() {
154
+ return execute("version", []);
155
+ }
156
+
157
+ // src/lib/format.ts
158
+ import chalk from "chalk";
159
+ var COLORS = {
160
+ // Planets (Sephirotic correspondences)
161
+ sun: chalk.hex("#FFD700"),
162
+ // Gold - Tiphareth
163
+ moon: chalk.hex("#C0C0C0"),
164
+ // Silver - Yesod
165
+ mercury: chalk.hex("#FF8C00"),
166
+ // Orange - Hod
167
+ venus: chalk.hex("#00FF7F"),
168
+ // Green - Netzach
169
+ mars: chalk.hex("#FF0000"),
170
+ // Red - Geburah
171
+ jupiter: chalk.hex("#4169E1"),
172
+ // Royal Blue - Chesed
173
+ saturn: chalk.hex("#4B0082"),
174
+ // Indigo - Binah
175
+ uranus: chalk.hex("#00FFFF"),
176
+ // Electric Cyan - Chokmah
177
+ neptune: chalk.hex("#20B2AA"),
178
+ // Sea Green
179
+ pluto: chalk.hex("#8B0000"),
180
+ // Dark Red
181
+ chiron: chalk.hex("#9932CC"),
182
+ // Purple - wounded healer
183
+ lilith: chalk.hex("#800020"),
184
+ // Burgundy - primal
185
+ northNode: chalk.hex("#FFD700"),
186
+ // Gold
187
+ southNode: chalk.hex("#C0C0C0")
188
+ // Silver
189
+ };
190
+ function getPlanetColor(name) {
191
+ const colorMap = {
192
+ "sun": COLORS.sun,
193
+ "moon": COLORS.moon,
194
+ "mercury": COLORS.mercury,
195
+ "venus": COLORS.venus,
196
+ "mars": COLORS.mars,
197
+ "jupiter": COLORS.jupiter,
198
+ "saturn": COLORS.saturn,
199
+ "uranus": COLORS.uranus,
200
+ "neptune": COLORS.neptune,
201
+ "pluto": COLORS.pluto,
202
+ "chiron": COLORS.chiron,
203
+ "mean_lilith": COLORS.lilith,
204
+ "lilith": COLORS.lilith,
205
+ "true_north_lunar_node": COLORS.northNode,
206
+ "true_south_lunar_node": COLORS.southNode,
207
+ "nn": COLORS.northNode,
208
+ "sn": COLORS.southNode
209
+ };
210
+ return colorMap[name.toLowerCase()] || chalk.white;
211
+ }
212
+ function getZodiacColor(sign) {
213
+ const colorMap = {
214
+ "Ari": COLORS.mars,
215
+ "Tau": COLORS.venus,
216
+ "Gem": COLORS.mercury,
217
+ "Can": COLORS.moon,
218
+ "Leo": COLORS.sun,
219
+ "Vir": COLORS.mercury,
220
+ "Lib": COLORS.venus,
221
+ "Sco": COLORS.pluto,
222
+ "Sag": COLORS.jupiter,
223
+ "Cap": COLORS.saturn,
224
+ "Aqu": COLORS.uranus,
225
+ "Pis": COLORS.neptune
226
+ };
227
+ return colorMap[sign] || chalk.white;
228
+ }
229
+ function getAspectColor(aspect) {
230
+ const colorMap = {
231
+ "conjunction": COLORS.sun,
232
+ // Tiphareth
233
+ "opposition": COLORS.moon,
234
+ // Yesod
235
+ "trine": COLORS.jupiter,
236
+ // Chesed
237
+ "square": COLORS.mars,
238
+ // Geburah
239
+ "sextile": COLORS.venus,
240
+ // Netzach
241
+ "quintile": COLORS.mercury,
242
+ // Hod
243
+ "quincunx": COLORS.uranus
244
+ };
245
+ return colorMap[aspect] || chalk.white;
246
+ }
247
+ var ZODIAC_SYMBOLS = {
248
+ "Ari": "\u2648",
249
+ "Tau": "\u2649",
250
+ "Gem": "\u264A",
251
+ "Can": "\u264B",
252
+ "Leo": "\u264C",
253
+ "Vir": "\u264D",
254
+ "Lib": "\u264E",
255
+ "Sco": "\u264F",
256
+ "Sag": "\u2650",
257
+ "Cap": "\u2651",
258
+ "Aqu": "\u2652",
259
+ "Pis": "\u2653"
260
+ };
261
+ var PLANET_SYMBOLS = {
262
+ "sun": "\u2609",
263
+ "moon": "\u263D",
264
+ "mercury": "\u263F",
265
+ "venus": "\u2640",
266
+ "mars": "\u2642",
267
+ "jupiter": "\u2643",
268
+ "saturn": "\u2644",
269
+ "uranus": "\u2645",
270
+ "neptune": "\u2646",
271
+ "pluto": "\u2647",
272
+ "chiron": "\u26B7",
273
+ "north_node": "\u260A",
274
+ "south_node": "\u260B",
275
+ "true_north_lunar_node": "\u260A",
276
+ "true_south_lunar_node": "\u260B",
277
+ "mean_lilith": "\u26B8",
278
+ "medium_coeli": "MC",
279
+ "imum_coeli": "IC",
280
+ "ascendant": "ASC",
281
+ "descendant": "DSC",
282
+ // Short names from transit aspects
283
+ "nn": "\u260A",
284
+ "sn": "\u260B",
285
+ "lilith": "\u26B8",
286
+ "mc": "MC",
287
+ "ic": "IC",
288
+ "asc": "ASC",
289
+ "dsc": "DSC"
290
+ };
291
+ var ASPECT_SYMBOLS = {
292
+ "conjunction": "\u260C",
293
+ "opposition": "\u260D",
294
+ "trine": "\u25B3",
295
+ "square": "\u25A1",
296
+ "sextile": "\u26B9",
297
+ "quintile": "\u235F",
298
+ "quincunx": "\u26BB",
299
+ "semi-sextile": "\u26BA",
300
+ "semi-square": "\u2220",
301
+ "sesquiquadrate": "\u26BC"
302
+ };
303
+ function getZodiacSymbol(sign) {
304
+ const key = sign.slice(0, 3);
305
+ return ZODIAC_SYMBOLS[key] || sign;
306
+ }
307
+ function getPlanetSymbol(planet) {
308
+ return PLANET_SYMBOLS[planet.toLowerCase()] || planet;
309
+ }
310
+ function getAspectSymbol(aspect) {
311
+ return ASPECT_SYMBOLS[aspect.toLowerCase()] || aspect;
312
+ }
313
+ function formatDegrees(degrees) {
314
+ const d = Math.floor(degrees);
315
+ const m = Math.floor((degrees - d) * 60);
316
+ return `${d}\xB0${m.toString().padStart(2, "0")}'`;
317
+ }
318
+ function formatChart(result) {
319
+ const lines = [];
320
+ lines.push(chalk.bold.white(`
321
+ \u{1315D} Natal Chart: ${result.name}`));
322
+ const dateStr = `${result.datetime.year}-${String(result.datetime.month).padStart(2, "0")}-${String(result.datetime.day).padStart(2, "0")} ${String(result.datetime.hour).padStart(2, "0")}:${String(result.datetime.minute).padStart(2, "0")}`;
323
+ lines.push(chalk.dim(` ${dateStr}`));
324
+ if (result.location.city) {
325
+ lines.push(chalk.dim(` ${result.location.city}`));
326
+ } else {
327
+ lines.push(chalk.dim(` Lat: ${result.location.lat}, Lng: ${result.location.lng}`));
328
+ }
329
+ if (result.lunar_phase) {
330
+ lines.push(chalk.dim(` Born during: ${result.lunar_phase.emoji} ${result.lunar_phase.name}`));
331
+ }
332
+ lines.push("");
333
+ lines.push(chalk.bold.cyan("\u2500\u2500 ANGLES \u2500\u2500"));
334
+ if (result.ascendant.sign) {
335
+ const zColor = getZodiacColor(result.ascendant.sign);
336
+ lines.push(` ${COLORS.mars("ASC")} ${zColor(getZodiacSymbol(result.ascendant.sign) + " " + result.ascendant.sign)} ${chalk.white(formatDegrees(result.ascendant.position || 0))}`);
337
+ }
338
+ if (result.midheaven.sign) {
339
+ const zColor = getZodiacColor(result.midheaven.sign);
340
+ lines.push(` ${COLORS.sun("MC")} ${zColor(getZodiacSymbol(result.midheaven.sign) + " " + result.midheaven.sign)} ${chalk.white(formatDegrees(result.midheaven.position || 0))}`);
341
+ }
342
+ lines.push("");
343
+ lines.push(chalk.bold.cyan("\u2500\u2500 PLANETS \u2500\u2500"));
344
+ const planetOrder = ["sun", "moon", "mercury", "venus", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto"];
345
+ for (const name of planetOrder) {
346
+ const planet = result.planets[name];
347
+ if (planet) {
348
+ const pColor = getPlanetColor(name);
349
+ const zColor = getZodiacColor(planet.sign);
350
+ const symbol = getPlanetSymbol(name);
351
+ const zodiac = getZodiacSymbol(planet.sign);
352
+ const deg = formatDegrees(planet.position);
353
+ const rx = planet.retrograde ? COLORS.mars(" \u211E") : "";
354
+ const house = planet.house ? chalk.dim(` (${planet.house})`) : "";
355
+ lines.push(` ${pColor(symbol)} ${name.padEnd(10)} ${zColor(zodiac + " " + planet.sign)} ${chalk.white(deg)}${rx}${house}`);
356
+ }
357
+ }
358
+ lines.push("");
359
+ lines.push(chalk.bold.cyan("\u2500\u2500 POINTS \u2500\u2500"));
360
+ const pointOrder = ["chiron", "mean_lilith", "true_north_lunar_node", "true_south_lunar_node"];
361
+ const pointNames = {
362
+ "chiron": "Chiron",
363
+ "mean_lilith": "Lilith",
364
+ "true_north_lunar_node": "North Node",
365
+ "true_south_lunar_node": "South Node"
366
+ };
367
+ for (const name of pointOrder) {
368
+ const planet = result.planets[name];
369
+ if (planet) {
370
+ const pColor = getPlanetColor(name);
371
+ const zColor = getZodiacColor(planet.sign);
372
+ const symbol = getPlanetSymbol(name);
373
+ const displayName = pointNames[name] || name;
374
+ const zodiac = getZodiacSymbol(planet.sign);
375
+ const deg = formatDegrees(planet.position);
376
+ const house = planet.house ? chalk.dim(` (${planet.house})`) : "";
377
+ lines.push(` ${pColor(symbol)} ${displayName.padEnd(10)} ${zColor(zodiac + " " + planet.sign)} ${chalk.white(deg)}${house}`);
378
+ }
379
+ }
380
+ lines.push("");
381
+ if (result.houses && Object.keys(result.houses).length > 0) {
382
+ lines.push(chalk.bold.cyan("\u2500\u2500 HOUSES \u2500\u2500"));
383
+ for (let i = 1; i <= 12; i++) {
384
+ const house = result.houses[String(i)];
385
+ if (house && house.sign) {
386
+ const zodiac = getZodiacSymbol(house.sign);
387
+ const deg = formatDegrees(house.position || 0);
388
+ const label = i === 1 ? "(ASC)" : i === 4 ? "(IC)" : i === 7 ? "(DSC)" : i === 10 ? "(MC)" : "";
389
+ lines.push(` ${String(i).padStart(2)} ${chalk.cyan(zodiac)} ${house.sign} ${chalk.dim(deg)} ${chalk.yellow(label)}`);
390
+ }
391
+ }
392
+ lines.push("");
393
+ }
394
+ if (result.elements && result.modes) {
395
+ lines.push(chalk.bold.cyan("\u2500\u2500 BALANCE \u2500\u2500"));
396
+ const elem = result.elements;
397
+ const mode = result.modes;
398
+ lines.push(` ${COLORS.mars("\u{1F702} Fire:")} ${elem.Fire} ${COLORS.venus("\u{1F703} Earth:")} ${elem.Earth} ${COLORS.mercury("\u{1F701} Air:")} ${elem.Air} ${COLORS.moon("\u{1F704} Water:")} ${elem.Water}`);
399
+ lines.push(` ${COLORS.mars("Cardinal:")} ${mode.Cardinal} ${COLORS.sun("Fixed:")} ${mode.Fixed} ${COLORS.mercury("Mutable:")} ${mode.Mutable}`);
400
+ lines.push("");
401
+ }
402
+ if (result.aspects && result.aspects.length > 0) {
403
+ lines.push(chalk.bold.cyan("\u2500\u2500 ASPECTS \u2500\u2500"));
404
+ const aspects = result.aspects.slice(0, 15);
405
+ for (const asp of aspects) {
406
+ const p1Name = asp.planet1.toLowerCase().replace(/ /g, "_");
407
+ const p2Name = asp.planet2.toLowerCase().replace(/ /g, "_");
408
+ const p1Color = getPlanetColor(p1Name);
409
+ const p2Color = getPlanetColor(p2Name);
410
+ const p1 = getPlanetSymbol(p1Name);
411
+ const p2 = getPlanetSymbol(p2Name);
412
+ const aspectSym = getAspectSymbol(asp.aspect);
413
+ const aspectName = asp.aspect.charAt(0).toUpperCase() + asp.aspect.slice(1);
414
+ const aColor = getAspectColor(asp.aspect);
415
+ lines.push(` ${p1Color(p1)} ${asp.planet1.padEnd(10)} ${aColor(aspectSym + " " + aspectName.padEnd(11))} ${p2Color(p2)} ${asp.planet2.padEnd(10)} ${chalk.dim(`${asp.orb}\xB0`)}`);
416
+ }
417
+ lines.push("");
418
+ }
419
+ return lines.join("\n");
420
+ }
421
+ function formatTransits(result) {
422
+ const lines = [];
423
+ lines.push(chalk.bold.white(`
424
+ \u{1315D} Transits`));
425
+ if (result.natal.name) {
426
+ lines.push(chalk.dim(` For: ${result.natal.name}`));
427
+ }
428
+ if (result.natal.city) {
429
+ lines.push(chalk.dim(` Natal: ${result.natal.datetime} \xB7 ${result.natal.city}`));
430
+ } else {
431
+ lines.push(chalk.dim(` Natal: ${result.natal.datetime}`));
432
+ }
433
+ lines.push(chalk.dim(` Transit: ${result.transit.datetime}`));
434
+ if (result.transit.lunar_phase) {
435
+ lines.push(chalk.dim(` Moon: ${result.transit.lunar_phase.emoji} ${result.transit.lunar_phase.name}`));
436
+ }
437
+ lines.push("");
438
+ if (result.transit.planets) {
439
+ lines.push(chalk.bold.cyan("\u2500\u2500 CURRENT SKY \u2500\u2500"));
440
+ const planets = result.transit.planets;
441
+ for (const name of ["sun", "moon", "mercury", "venus", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto"]) {
442
+ const planet = planets[name];
443
+ if (planet) {
444
+ const pColor = getPlanetColor(name);
445
+ const zColor = getZodiacColor(planet.sign);
446
+ const symbol = getPlanetSymbol(name);
447
+ const zodiac = getZodiacSymbol(planet.sign);
448
+ const deg = formatDegrees(planet.position);
449
+ const rx = planet.retrograde ? COLORS.mars("\u211E") : " ";
450
+ const cH = planet.house ? planet.house.replace(/ house/i, "").replace(/first/i, "1").replace(/second/i, "2").replace(/third/i, "3").replace(/fourth/i, "4").replace(/fifth/i, "5").replace(/sixth/i, "6").replace(/seventh/i, "7").replace(/eighth/i, "8").replace(/ninth/i, "9").replace(/tenth/i, "10").replace(/eleventh/i, "11").replace(/twelfth/i, "12") : "?";
451
+ lines.push(` ${pColor(symbol)} ${zColor(zodiac + " " + planet.sign)} ${chalk.white(deg)} ${rx} ${chalk.dim(`${cH}H`)}`);
452
+ }
453
+ }
454
+ lines.push("");
455
+ }
456
+ const transitHouses = result.transit.houses;
457
+ const natalHouses = result.natal_houses;
458
+ if (transitHouses && natalHouses && Object.keys(transitHouses).length > 0) {
459
+ lines.push(chalk.bold.cyan("\u2500\u2500 HOUSES \u2500\u2500"));
460
+ lines.push(chalk.dim(" TRANSIT NATAL"));
461
+ for (let i = 1; i <= 12; i++) {
462
+ const tH = transitHouses[String(i)];
463
+ const nH = natalHouses[String(i)];
464
+ if (tH && nH) {
465
+ const label = i === 1 ? "ASC" : i === 4 ? "IC" : i === 7 ? "DSC" : i === 10 ? "MC" : `${i}`.padStart(2) + "H";
466
+ const tZColor = getZodiacColor(tH.sign);
467
+ const nZColor = getZodiacColor(nH.sign);
468
+ const tZodiac = getZodiacSymbol(tH.sign);
469
+ const nZodiac = getZodiacSymbol(nH.sign);
470
+ const tDeg = formatDegrees(tH.position);
471
+ const nDeg = formatDegrees(nH.position);
472
+ lines.push(` ${chalk.white(label.padStart(3))} ${tZColor(tZodiac + " " + tH.sign)} ${chalk.dim(tDeg)} ${nZColor(nZodiac + " " + nH.sign)} ${chalk.dim(nDeg)}`);
473
+ }
474
+ }
475
+ lines.push("");
476
+ }
477
+ lines.push(chalk.bold.cyan("\u2500\u2500 TRANSITS TO NATAL \u2500\u2500"));
478
+ if (result.aspects.length === 0) {
479
+ lines.push(chalk.dim(" No aspects within orb"));
480
+ } else {
481
+ const shortName = (name) => {
482
+ const map = {
483
+ "sun": "SUN",
484
+ "moon": "MOO",
485
+ "mercury": "MER",
486
+ "venus": "VEN",
487
+ "mars": "MAR",
488
+ "jupiter": "JUP",
489
+ "saturn": "SAT",
490
+ "uranus": "URA",
491
+ "neptune": "NEP",
492
+ "pluto": "PLU",
493
+ "chiron": "CHI",
494
+ "nn": "NN",
495
+ "sn": "SN",
496
+ "lilith": "LIL",
497
+ "mc": "MC",
498
+ "ic": "IC",
499
+ "asc": "ASC",
500
+ "dsc": "DSC"
501
+ };
502
+ return map[name.toLowerCase()] || name.slice(0, 3).toUpperCase();
503
+ };
504
+ for (const aspect of result.aspects) {
505
+ const tPlanet = aspect.transit_planet.toLowerCase().replace(/ /g, "_");
506
+ const nPlanet = aspect.natal_planet.toLowerCase().replace(/ /g, "_");
507
+ const tColor = getPlanetColor(tPlanet);
508
+ const nColor = getPlanetColor(nPlanet);
509
+ const tSym = getPlanetSymbol(tPlanet);
510
+ const nSym = getPlanetSymbol(nPlanet);
511
+ const tName = shortName(aspect.transit_planet);
512
+ const nName = shortName(aspect.natal_planet);
513
+ const aSym = getAspectSymbol(aspect.aspect);
514
+ const aColor = getAspectColor(aspect.aspect);
515
+ const aspectShort = aspect.aspect === "conjunction" ? "CNJ" : aspect.aspect === "opposition" ? "OPP" : aspect.aspect === "trine" ? "TRI" : aspect.aspect === "square" ? "SQR" : aspect.aspect === "sextile" ? "SXT" : aspect.aspect === "quintile" ? "QNT" : aspect.aspect === "quincunx" ? "QCX" : String(aspect.aspect).slice(0, 3).toUpperCase();
516
+ const tH = aspect.transit_house;
517
+ const nH = aspect.natal_house;
518
+ const houses = `${tH || "?"}H\u2192${nH || "?"}H`;
519
+ const orb = aspect.orb.toFixed(2).padStart(5);
520
+ lines.push(` ${tColor(tSym)} ${tName.padEnd(3)} ${aColor(aSym + " " + aspectShort)} ${nColor(nSym)} ${nName.padEnd(3)} ${chalk.dim(orb + "\xB0")} ${chalk.dim(houses)}`);
521
+ }
522
+ }
523
+ lines.push("");
524
+ return lines.join("\n");
525
+ }
526
+ function formatMoon(result) {
527
+ const lines = [];
528
+ const moonEmoji = result.phase.illumination > 50 ? "\u{1F315}" : "\u{1F311}";
529
+ const zColor = getZodiacColor(result.moon.sign);
530
+ lines.push(chalk.bold.white(`
531
+ ${moonEmoji} Moon Phase`));
532
+ lines.push(chalk.dim(` ${result.datetime}`));
533
+ lines.push("");
534
+ lines.push(` ${COLORS.moon("\u263D")} ${chalk.white("Moon in")} ${zColor(result.moon.sign)} ${chalk.white(formatDegrees(result.moon.position))}`);
535
+ lines.push(` ${chalk.white("Phase:")} ${COLORS.moon(result.phase.name)}`);
536
+ lines.push(` ${chalk.white("Illumination:")} ${COLORS.moon(result.phase.illumination.toFixed(1) + "%")}`);
537
+ lines.push("");
538
+ return lines.join("\n");
539
+ }
540
+ function formatEphemeris(result) {
541
+ const lines = [];
542
+ const pColor = getPlanetColor(result.body);
543
+ const zColor = getZodiacColor(result.sign);
544
+ const symbol = getPlanetSymbol(result.body);
545
+ const rx = result.retrograde ? COLORS.mars(" \u211E") : "";
546
+ lines.push(chalk.bold.white(`
547
+ \u{1315D} Ephemeris: ${result.body}`));
548
+ lines.push(chalk.dim(` ${result.datetime}`));
549
+ lines.push("");
550
+ lines.push(` ${pColor(symbol)} ${chalk.white(result.body)} in ${zColor(result.sign)} ${chalk.white(formatDegrees(result.position))}${rx}`);
551
+ lines.push(` ${chalk.dim(`Absolute: ${result.abs_position.toFixed(4)}\xB0`)}`);
552
+ lines.push("");
553
+ return lines.join("\n");
554
+ }
555
+
556
+ // src/types.ts
557
+ function isError(result) {
558
+ return "error" in result;
559
+ }
560
+
561
+ export {
562
+ chart,
563
+ transit,
564
+ moon,
565
+ ephemeris,
566
+ version,
567
+ getZodiacSymbol,
568
+ getPlanetSymbol,
569
+ getAspectSymbol,
570
+ formatDegrees,
571
+ formatChart,
572
+ formatTransits,
573
+ formatMoon,
574
+ formatEphemeris,
575
+ isError
576
+ };
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Thoth CLI Types
3
+ * 𓅝
4
+ */
5
+ interface Planet {
6
+ sign: string;
7
+ position: number;
8
+ abs_position: number;
9
+ house?: number | null;
10
+ retrograde: boolean;
11
+ }
12
+ interface House {
13
+ sign: string | null;
14
+ position: number | null;
15
+ }
16
+ interface ChartResult {
17
+ name: string;
18
+ datetime: {
19
+ year: number;
20
+ month: number;
21
+ day: number;
22
+ hour: number;
23
+ minute: number;
24
+ };
25
+ location: {
26
+ lat: number;
27
+ lng: number;
28
+ };
29
+ planets: Record<string, Planet>;
30
+ houses: Record<string, House>;
31
+ ascendant: {
32
+ sign: string | null;
33
+ position: number | null;
34
+ };
35
+ midheaven: {
36
+ sign: string | null;
37
+ position: number | null;
38
+ };
39
+ }
40
+ interface Aspect {
41
+ transit_planet: string;
42
+ natal_planet: string;
43
+ aspect: 'conjunction' | 'opposition' | 'trine' | 'square' | 'sextile';
44
+ orb: number;
45
+ transit_position: number;
46
+ natal_position: number;
47
+ applying: boolean;
48
+ }
49
+ interface TransitResult {
50
+ natal: {
51
+ datetime: string;
52
+ };
53
+ transit: {
54
+ datetime: string;
55
+ };
56
+ aspects: Aspect[];
57
+ }
58
+ interface MoonResult {
59
+ datetime: string;
60
+ moon: {
61
+ sign: string;
62
+ position: number;
63
+ abs_position: number;
64
+ };
65
+ phase: {
66
+ name: string;
67
+ angle: number;
68
+ illumination: number;
69
+ };
70
+ }
71
+ interface EphemerisResult {
72
+ body: string;
73
+ datetime: string;
74
+ sign: string;
75
+ position: number;
76
+ abs_position: number;
77
+ retrograde: boolean;
78
+ }
79
+ interface ChartOptions {
80
+ year: number;
81
+ month: number;
82
+ day: number;
83
+ hour?: number;
84
+ minute?: number;
85
+ lat?: number;
86
+ lng?: number;
87
+ city?: string;
88
+ nation?: string;
89
+ name?: string;
90
+ }
91
+ interface TransitOptions {
92
+ natalYear: number;
93
+ natalMonth: number;
94
+ natalDay: number;
95
+ natalHour?: number;
96
+ natalMinute?: number;
97
+ natalLat?: number;
98
+ natalLng?: number;
99
+ natalCity?: string;
100
+ nation?: string;
101
+ transitYear?: number;
102
+ transitMonth?: number;
103
+ transitDay?: number;
104
+ orb?: number;
105
+ }
106
+ interface MoonOptions {
107
+ year?: number;
108
+ month?: number;
109
+ day?: number;
110
+ lat?: number;
111
+ lng?: number;
112
+ }
113
+ interface EphemerisOptions {
114
+ body: string;
115
+ year?: number;
116
+ month?: number;
117
+ day?: number;
118
+ }
119
+ interface ThothError {
120
+ error: string;
121
+ }
122
+ type ThothResult<T> = T | ThothError;
123
+ declare function isError<T extends object>(result: ThothResult<T>): result is ThothError;
124
+
125
+ /**
126
+ * Core wrapper for thoth-core binary
127
+ * 𓅝
128
+ */
129
+
130
+ /**
131
+ * Calculate a natal chart
132
+ */
133
+ declare function chart(options: ChartOptions): Promise<ThothResult<ChartResult>>;
134
+ /**
135
+ * Calculate transits to a natal chart
136
+ */
137
+ declare function transit(options: TransitOptions): Promise<ThothResult<TransitResult>>;
138
+ /**
139
+ * Get moon phase and position
140
+ */
141
+ declare function moon(options?: MoonOptions): Promise<ThothResult<MoonResult>>;
142
+ /**
143
+ * Get ephemeris position for a celestial body
144
+ */
145
+ declare function ephemeris(options: EphemerisOptions): Promise<ThothResult<EphemerisResult>>;
146
+ /**
147
+ * Get version
148
+ */
149
+ declare function version(): Promise<ThothResult<{
150
+ version: string;
151
+ }>>;
152
+
153
+ /**
154
+ * Output formatting utilities
155
+ * 𓅝
156
+ */
157
+
158
+ /**
159
+ * Get zodiac symbol
160
+ */
161
+ declare function getZodiacSymbol(sign: string): string;
162
+ /**
163
+ * Get planet symbol
164
+ */
165
+ declare function getPlanetSymbol(planet: string): string;
166
+ /**
167
+ * Get aspect symbol
168
+ */
169
+ declare function getAspectSymbol(aspect: string): string;
170
+ /**
171
+ * Format degrees to degrees, minutes, seconds
172
+ */
173
+ declare function formatDegrees(degrees: number): string;
174
+ /**
175
+ * Format a chart result
176
+ */
177
+ declare function formatChart(result: ChartResult): string;
178
+ /**
179
+ * Format transits result
180
+ */
181
+ declare function formatTransits(result: TransitResult): string;
182
+ /**
183
+ * Format moon result
184
+ */
185
+ declare function formatMoon(result: MoonResult): string;
186
+ /**
187
+ * Format ephemeris result
188
+ */
189
+ declare function formatEphemeris(result: EphemerisResult): string;
190
+
191
+ export { type Aspect, type ChartOptions, type ChartResult, type EphemerisOptions, type EphemerisResult, type House, type MoonOptions, type MoonResult, type Planet, type ThothError, type ThothResult, type TransitOptions, type TransitResult, chart, ephemeris, formatChart, formatDegrees, formatEphemeris, formatMoon, formatTransits, getAspectSymbol, getPlanetSymbol, getZodiacSymbol, isError, moon, transit, version };
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ import {
2
+ chart,
3
+ ephemeris,
4
+ formatChart,
5
+ formatDegrees,
6
+ formatEphemeris,
7
+ formatMoon,
8
+ formatTransits,
9
+ getAspectSymbol,
10
+ getPlanetSymbol,
11
+ getZodiacSymbol,
12
+ isError,
13
+ moon,
14
+ transit,
15
+ version
16
+ } from "./chunk-NYXKYHNK.js";
17
+ export {
18
+ chart,
19
+ ephemeris,
20
+ formatChart,
21
+ formatDegrees,
22
+ formatEphemeris,
23
+ formatMoon,
24
+ formatTransits,
25
+ getAspectSymbol,
26
+ getPlanetSymbol,
27
+ getZodiacSymbol,
28
+ isError,
29
+ moon,
30
+ transit,
31
+ version
32
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "thoth-cli",
3
+ "version": "0.1.0",
4
+ "description": "𓅝 Astrological calculations from the command line. Swiss Ephemeris precision. Built for humans and agents.",
5
+ "author": "AKLO <aklo@aklolabs.com>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "bin": {
11
+ "thoth": "./dist/bin.js"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "bin"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsup src/index.ts src/bin.ts --format esm --dts --clean",
19
+ "dev": "tsup src/index.ts src/bin.ts --format esm --dts --watch",
20
+ "postinstall": "node scripts/postinstall.js",
21
+ "test": "vitest"
22
+ },
23
+ "dependencies": {
24
+ "chalk": "^5.3.0",
25
+ "commander": "^12.0.0",
26
+ "ora": "^8.0.0",
27
+ "execa": "^8.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.0.0",
33
+ "vitest": "^1.0.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "keywords": [
39
+ "astrology",
40
+ "cli",
41
+ "ephemeris",
42
+ "horoscope",
43
+ "natal-chart",
44
+ "thoth"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/aklo/thoth-cli.git"
49
+ }
50
+ }