klio 1.4.2 → 1.4.4
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 +7 -5
- package/package.json +1 -1
- package/src/astrology/astrologyService.js +219 -1
- package/src/cli/cli.js +101 -1
package/README.md
CHANGED
|
@@ -90,8 +90,6 @@ It's possible to analyze Apple Health data. This happens on the device and no se
|
|
|
90
90
|
- **Step analysis**: `klio [planet] --apple <filepath> --steps` - Analyzes step patterns of any planet
|
|
91
91
|
- **Stress analysis**: `klio [planet] --apple <filepath> --stress` - Analyzes stress patterns of planet sign stress based on HRV
|
|
92
92
|
|
|
93
|
-
> Maybe I'll add some more in the future.
|
|
94
|
-
|
|
95
93
|
### CSV Analysis
|
|
96
94
|
It's possible to analyze a csv with a column of either ISO date time or unix time stamp.
|
|
97
95
|
|
|
@@ -100,9 +98,6 @@ It's possible to analyze a csv with a column of either ISO date time or unix tim
|
|
|
100
98
|
|
|
101
99
|
The command also returns a Chi-Square.
|
|
102
100
|
|
|
103
|
-
You can research a lot of fun things with this. You find a lot of free datasets on Kaggle.
|
|
104
|
-
|
|
105
|
-
|
|
106
101
|
### Adding different charts
|
|
107
102
|
|
|
108
103
|
- **Use personal data**: `--i` - Uses birth data from setup for calculations
|
|
@@ -127,6 +122,13 @@ Then, instead using `--i` for the commands from above you can use `--wp <id>` i.
|
|
|
127
122
|
- **Past aspects**: `klio --v <count> [planet1] [aspect-type] [planet2]` - Shows past aspects (Format: --v <count> planet1 aspectType planet2) Available aspect types: c, o, s, t, se (conjunction, opposition, square, trine, sextile)
|
|
128
123
|
- **Future aspects**: `klio --z <count> [planet1] [aspect-type] [planet2]` - Shows future aspects (Format: --z <count> planet1 aspectType planet2)
|
|
129
124
|
|
|
125
|
+
### Planet Ingresses
|
|
126
|
+
|
|
127
|
+
- **Planet ingress**: `klio [planet] --in [count]` - Shows when a planet enters a new zodiac sign. Optional count parameter for multiple ingresses (default: 1). Works with any planet (moon, mercury, venus, etc.)
|
|
128
|
+
- Example: `klio moon --in` - Shows next moon ingress
|
|
129
|
+
- Example: `klio saturn --in 2` - Shows next 2 Saturn ingresses (note: slow planets may have limited future data)
|
|
130
|
+
- Example: `klio mercury --in --d "15.03.2026"` - Shows next Mercury ingress from a specific date
|
|
131
|
+
|
|
130
132
|
### AI Integration
|
|
131
133
|
|
|
132
134
|
- **AI model selection**: `--ai <model>` - Sets a specific AI model (e.g., "google/gemma-3n-e4b") for LM Studio ([lmstudio.ai](https://lmstudio.ai))
|
package/package.json
CHANGED
|
@@ -2491,6 +2491,223 @@ function analyzeHouseDistributionSignificance(houseDistribution) {
|
|
|
2491
2491
|
};
|
|
2492
2492
|
}
|
|
2493
2493
|
|
|
2494
|
+
// Function to calculate the next planet ingress (when planet enters a new sign)
|
|
2495
|
+
function calculateNextPlanetIngress(planetName, startDate = null, count = 1) {
|
|
2496
|
+
// Use current date if no start date is provided
|
|
2497
|
+
const startDateObj = startDate ? new Date(startDate.year, startDate.month - 1, startDate.day, startDate.hour, startDate.minute) : new Date();
|
|
2498
|
+
|
|
2499
|
+
const results = [];
|
|
2500
|
+
let currentDate = new Date(startDateObj);
|
|
2501
|
+
|
|
2502
|
+
// Calculate timezone offset for the start date
|
|
2503
|
+
const config = loadConfig();
|
|
2504
|
+
let timezone = 'Europe/Zurich'; // Default
|
|
2505
|
+
|
|
2506
|
+
if (config && config.currentLocation && config.currentLocation.timezone) {
|
|
2507
|
+
timezone = config.currentLocation.timezone;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
const timezoneOffsetMinutes = getTimezoneOffset({
|
|
2511
|
+
year: currentDate.getFullYear(),
|
|
2512
|
+
month: currentDate.getMonth() + 1,
|
|
2513
|
+
day: currentDate.getDate(),
|
|
2514
|
+
hour: currentDate.getHours(),
|
|
2515
|
+
minute: currentDate.getMinutes()
|
|
2516
|
+
}, timezone);
|
|
2517
|
+
|
|
2518
|
+
// Get current planet position
|
|
2519
|
+
const currentPlanetData = getAstrologicalData(planetName, {
|
|
2520
|
+
year: currentDate.getFullYear(),
|
|
2521
|
+
month: currentDate.getMonth() + 1,
|
|
2522
|
+
day: currentDate.getDate(),
|
|
2523
|
+
hour: currentDate.getHours(),
|
|
2524
|
+
minute: currentDate.getMinutes()
|
|
2525
|
+
});
|
|
2526
|
+
|
|
2527
|
+
let currentSignIndex = signs.indexOf(currentPlanetData.sign);
|
|
2528
|
+
let targetSignIndex = (currentSignIndex + 1) % 12; // Next sign
|
|
2529
|
+
|
|
2530
|
+
for (let i = 0; i < count; i++) {
|
|
2531
|
+
// Find the next ingress by checking positions in 1-hour increments
|
|
2532
|
+
let foundIngress = false;
|
|
2533
|
+
let ingressDate = null;
|
|
2534
|
+
let attempts = 0;
|
|
2535
|
+
|
|
2536
|
+
// Dynamic search strategy based on planet speed
|
|
2537
|
+
const verySlowPlanets = ['saturn', 'uranus', 'neptune', 'pluto'];
|
|
2538
|
+
const mediumSlowPlanets = ['jupiter'];
|
|
2539
|
+
const isVerySlowPlanet = verySlowPlanets.includes(planetName);
|
|
2540
|
+
const isMediumSlowPlanet = mediumSlowPlanets.includes(planetName);
|
|
2541
|
+
|
|
2542
|
+
// Determine time step based on planet speed
|
|
2543
|
+
const timeStepHours = isVerySlowPlanet ? 24 :
|
|
2544
|
+
isMediumSlowPlanet ? 6 :
|
|
2545
|
+
1; // Daily for very slow, 6-hourly for medium, hourly for fast
|
|
2546
|
+
|
|
2547
|
+
// Maximum search window (in days) - calculate based on planet and requested count
|
|
2548
|
+
// Mars: ~2 months per sign, so for multiple ingresses we need ~count * 60 days
|
|
2549
|
+
// Other fast planets (Moon, Mercury, Venus, Sun): 90 days is sufficient
|
|
2550
|
+
const fastPlanets = ['moon', 'mercury', 'venus', 'sun'];
|
|
2551
|
+
const isFastPlanet = fastPlanets.includes(planetName);
|
|
2552
|
+
const isMars = planetName === 'mars';
|
|
2553
|
+
|
|
2554
|
+
const maxDays = isVerySlowPlanet ? 365 * 90 :
|
|
2555
|
+
isMediumSlowPlanet ? 365 * 90 :
|
|
2556
|
+
isMars ? Math.min(365 * 5, 90 * count) : // Mars: up to 5 years or count * 3 months
|
|
2557
|
+
isFastPlanet ? 90 : // 30 days for fast planets (enough for multiple moon ingresses)
|
|
2558
|
+
365 * 90; // Default for other planets
|
|
2559
|
+
const maxAttempts = maxDays * 24 / timeStepHours;
|
|
2560
|
+
|
|
2561
|
+
while (!foundIngress && attempts < maxAttempts) {
|
|
2562
|
+
attempts++;
|
|
2563
|
+
currentDate.setHours(currentDate.getHours() + timeStepHours);
|
|
2564
|
+
|
|
2565
|
+
const testPlanetData = getAstrologicalData(planetName, {
|
|
2566
|
+
year: currentDate.getFullYear(),
|
|
2567
|
+
month: currentDate.getMonth() + 1,
|
|
2568
|
+
day: currentDate.getDate(),
|
|
2569
|
+
hour: currentDate.getHours(),
|
|
2570
|
+
minute: currentDate.getMinutes()
|
|
2571
|
+
});
|
|
2572
|
+
|
|
2573
|
+
const testSignIndex = signs.indexOf(testPlanetData.sign);
|
|
2574
|
+
|
|
2575
|
+
if (testSignIndex === targetSignIndex) {
|
|
2576
|
+
// Found the ingress! Now refine to get more precise time
|
|
2577
|
+
ingressDate = findExactPlanetIngressTime(planetName, currentDate, targetSignIndex, timezoneOffsetMinutes);
|
|
2578
|
+
foundIngress = true;
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
if (foundIngress) {
|
|
2583
|
+
results.push({
|
|
2584
|
+
sign: signs[targetSignIndex],
|
|
2585
|
+
date: ingressDate,
|
|
2586
|
+
fromSign: signs[currentSignIndex]
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
// Prepare for next ingress
|
|
2590
|
+
currentSignIndex = targetSignIndex;
|
|
2591
|
+
targetSignIndex = (currentSignIndex + 1) % 12;
|
|
2592
|
+
// Convert ingressDate object to Date object
|
|
2593
|
+
currentDate = new Date(ingressDate.year, ingressDate.month - 1, ingressDate.day, ingressDate.hour, ingressDate.minute);
|
|
2594
|
+
// For fast planets, start searching a few hours after the ingress to avoid missing the next one
|
|
2595
|
+
if (isFastPlanet) {
|
|
2596
|
+
currentDate.setHours(currentDate.getHours() + 6); // Start 6 hours after ingress
|
|
2597
|
+
}
|
|
2598
|
+
} else {
|
|
2599
|
+
const daysLimit = isVerySlowPlanet ? 365 * 90 :
|
|
2600
|
+
isMediumSlowPlanet ? 365 * 90 :
|
|
2601
|
+
isMars ? Math.min(365 * 5, 90 * count) :
|
|
2602
|
+
isFastPlanet ? 90 :
|
|
2603
|
+
365 * 90;
|
|
2604
|
+
console.log(`Could not find ${planetName} ingress to ${signs[targetSignIndex]} within ${daysLimit} days`);
|
|
2605
|
+
if (isVerySlowPlanet) {
|
|
2606
|
+
console.log(`💡 Note: For very slow planets like ${planetName}, the ephemeris data may not extend far enough into the future.`);
|
|
2607
|
+
} else if (isMediumSlowPlanet) {
|
|
2608
|
+
console.log(`💡 Note: For medium-slow planets like ${planetName}, the ephemeris data may not extend far enough into the future.`);
|
|
2609
|
+
} else if (isMars) {
|
|
2610
|
+
console.log(`💡 Note: For Mars, the requested number of ingresses may be too high for the current search window.`);
|
|
2611
|
+
} else {
|
|
2612
|
+
console.log(`💡 Note: For ${planetName}, the requested number of ingresses may be too high.`);
|
|
2613
|
+
}
|
|
2614
|
+
console.log(`💡 Try requesting fewer ingresses or use a shorter time frame with the --d option.`);
|
|
2615
|
+
// Continue to next iteration instead of breaking the entire loop
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
return results;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
// Helper function to find exact planet ingress time using binary search
|
|
2624
|
+
function findExactPlanetIngressTime(planetName, approximateDate, targetSignIndex, timezoneOffsetMinutes) {
|
|
2625
|
+
const targetLongitude = targetSignIndex * 30; // Each sign is 30 degrees
|
|
2626
|
+
|
|
2627
|
+
// Convert approximate date to Julian Day
|
|
2628
|
+
const approxJulianDay = calculateJulianDayUTC({
|
|
2629
|
+
year: approximateDate.getFullYear(),
|
|
2630
|
+
month: approximateDate.getMonth() + 1,
|
|
2631
|
+
day: approximateDate.getDate(),
|
|
2632
|
+
hour: approximateDate.getHours(),
|
|
2633
|
+
minute: approximateDate.getMinutes()
|
|
2634
|
+
}, timezoneOffsetMinutes);
|
|
2635
|
+
|
|
2636
|
+
// Binary search parameters
|
|
2637
|
+
let low = approxJulianDay - 0.1; // 2.4 hours before
|
|
2638
|
+
let high = approxJulianDay + 0.1; // 2.4 hours after
|
|
2639
|
+
let precision = 0.0001; // About 1.44 minutes precision
|
|
2640
|
+
|
|
2641
|
+
// Perform binary search
|
|
2642
|
+
for (let i = 0; i < 50; i++) {
|
|
2643
|
+
const mid = (low + high) / 2;
|
|
2644
|
+
const result = swisseph.swe_calc_ut(mid, planets[planetName], swisseph.SEFLG_SWIEPH | swisseph.SEFLG_SPEED);
|
|
2645
|
+
|
|
2646
|
+
if (result.error) {
|
|
2647
|
+
// Fallback to Moshier
|
|
2648
|
+
const moshierResult = swisseph.swe_calc_ut(mid, planets[planetName], swisseph.SEFLG_MOSEPH | swisseph.SEFLG_SPEED);
|
|
2649
|
+
if (moshierResult.error) {
|
|
2650
|
+
console.error(`Error calculating ${planetName} position:`, moshierResult.error);
|
|
2651
|
+
break;
|
|
2652
|
+
}
|
|
2653
|
+
result.longitude = moshierResult.longitude;
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
if (result.longitude >= targetLongitude) {
|
|
2657
|
+
high = mid;
|
|
2658
|
+
} else {
|
|
2659
|
+
low = mid;
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
if (high - low < precision) {
|
|
2663
|
+
break;
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
// Convert Julian Day back to date components
|
|
2668
|
+
const finalJulianDay = (low + high) / 2;
|
|
2669
|
+
return convertJulianDayToDate(finalJulianDay, timezoneOffsetMinutes);
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// Helper function to convert Julian Day to date components
|
|
2673
|
+
function convertJulianDayToDate(julianDay, timezoneOffsetMinutes) {
|
|
2674
|
+
// This is a simplified conversion - for more accuracy, use a proper algorithm
|
|
2675
|
+
// We'll use the Swiss Ephemeris function if available
|
|
2676
|
+
try {
|
|
2677
|
+
const result = swisseph.swe_revjul(julianDay, swisseph.SE_GREG_CAL);
|
|
2678
|
+
|
|
2679
|
+
// Extract minutes from the decimal part of the hour
|
|
2680
|
+
const hour = Math.floor(result.hour);
|
|
2681
|
+
const minute = Math.round((result.hour - hour) * 60);
|
|
2682
|
+
|
|
2683
|
+
// Adjust for timezone
|
|
2684
|
+
const utcDate = new Date(Date.UTC(result.year, result.month - 1, result.day, hour, minute));
|
|
2685
|
+
utcDate.setMinutes(utcDate.getMinutes() + timezoneOffsetMinutes);
|
|
2686
|
+
|
|
2687
|
+
return {
|
|
2688
|
+
year: utcDate.getFullYear(),
|
|
2689
|
+
month: utcDate.getMonth() + 1,
|
|
2690
|
+
day: utcDate.getDate(),
|
|
2691
|
+
hour: utcDate.getHours(),
|
|
2692
|
+
minute: utcDate.getMinutes()
|
|
2693
|
+
};
|
|
2694
|
+
} catch (error) {
|
|
2695
|
+
console.error('Error converting Julian Day to date:', error);
|
|
2696
|
+
|
|
2697
|
+
// Fallback: return approximate date
|
|
2698
|
+
const baseDate = new Date();
|
|
2699
|
+
baseDate.setTime((julianDay - 2440587.5) * 86400000); // Convert Julian Day to milliseconds
|
|
2700
|
+
|
|
2701
|
+
return {
|
|
2702
|
+
year: baseDate.getFullYear(),
|
|
2703
|
+
month: baseDate.getMonth() + 1,
|
|
2704
|
+
day: baseDate.getDate(),
|
|
2705
|
+
hour: baseDate.getHours(),
|
|
2706
|
+
minute: baseDate.getMinutes()
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2494
2711
|
|
|
2495
2712
|
module.exports = {
|
|
2496
2713
|
calculateHouses,
|
|
@@ -2527,5 +2744,6 @@ module.exports = {
|
|
|
2527
2744
|
analyzeSignDistributionSignificance,
|
|
2528
2745
|
calculateAspectStatistics,
|
|
2529
2746
|
calculatePlanetComboAspects,
|
|
2530
|
-
showPlanetComboAspects
|
|
2747
|
+
showPlanetComboAspects,
|
|
2748
|
+
calculateNextPlanetIngress
|
|
2531
2749
|
};
|
package/src/cli/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { Command } = require('commander');
|
|
2
2
|
const { planets, signs } = require('../astrology/astrologyConstants');
|
|
3
3
|
const { showRetrogradePlanets } = require('../astrology/retrogradeService');
|
|
4
|
-
const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, getPersonDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, findExactAspectTime, findLastExactAspectTime, getAspectAngle, getFutureAspects, getPastAspects, analyzeCSVWithDatetime, analyzeHouseDistributionSignificance, analyzeAspectDistributionSignificance, analyzeSignDistributionSignificance, calculateAspectStatistics, calculatePlanetComboAspects, showPlanetComboAspects, getCriticalPlanets, getHouseSystemCode } = require('../astrology/astrologyService');
|
|
4
|
+
const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, getPersonDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, findExactAspectTime, findLastExactAspectTime, getAspectAngle, getFutureAspects, getPastAspects, analyzeCSVWithDatetime, analyzeHouseDistributionSignificance, analyzeAspectDistributionSignificance, analyzeSignDistributionSignificance, calculateAspectStatistics, calculatePlanetComboAspects, showPlanetComboAspects, getCriticalPlanets, getHouseSystemCode, calculateNextPlanetIngress } = require('../astrology/astrologyService');
|
|
5
5
|
const { performSetup, showConfigStatus, loadConfig, setAIModel, askAIModel, setPerson1, setPerson2, setPerson, getPersonData, listPeople, deletePerson } = require('../config/configService');
|
|
6
6
|
const { parseAppleHealthXML } = require('../health/healthService');
|
|
7
7
|
const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep } = require('../health/healthAnalysis');
|
|
@@ -254,6 +254,7 @@ program
|
|
|
254
254
|
.option('--v <count>', 'Shows past aspects between two planets (Format: --v <count> planet1 aspectType planet2)')
|
|
255
255
|
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
256
256
|
.option('--csv <filepath>', 'Analyzes a CSV file with ISO-Datetime values or Unix timestamps')
|
|
257
|
+
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
257
258
|
.option('--gui', 'Launches the web interface for command history (port 37421)')
|
|
258
259
|
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
259
260
|
.description('Shows astrological data for a planet')
|
|
@@ -1012,6 +1013,105 @@ program
|
|
|
1012
1013
|
return;
|
|
1013
1014
|
}
|
|
1014
1015
|
|
|
1016
|
+
// Show next planet ingress if --in option is specified
|
|
1017
|
+
if (options.in) {
|
|
1018
|
+
// Determine the planet to use (default to moon if no planet specified)
|
|
1019
|
+
const planetName = planetArg ? planetArg.toLowerCase() : 'moon';
|
|
1020
|
+
|
|
1021
|
+
// Validate planet name
|
|
1022
|
+
if (planets[planetName] === undefined) {
|
|
1023
|
+
console.error(`Error: Invalid planet '${planetName}'. Available planets: ${Object.keys(planets).join(', ')}`);
|
|
1024
|
+
process.exit(1);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// Parse the count parameter (default to 1 if not specified)
|
|
1028
|
+
const count = options.in && typeof options.in === 'string' && options.in.trim() ? parseInt(options.in) : 1;
|
|
1029
|
+
|
|
1030
|
+
// Use custom date if specified
|
|
1031
|
+
let customDate = null;
|
|
1032
|
+
if (options.d) {
|
|
1033
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
1034
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
1035
|
+
const match = options.d.match(dateRegex);
|
|
1036
|
+
|
|
1037
|
+
if (match) {
|
|
1038
|
+
const day = parseInt(match[1], 10);
|
|
1039
|
+
const month = parseInt(match[2], 10);
|
|
1040
|
+
const year = parseInt(match[3], 10);
|
|
1041
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
1042
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
1043
|
+
|
|
1044
|
+
// Check if the date is valid
|
|
1045
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
1046
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
1047
|
+
customDate = {
|
|
1048
|
+
day: day,
|
|
1049
|
+
month: month,
|
|
1050
|
+
year: year,
|
|
1051
|
+
hour: hour,
|
|
1052
|
+
minute: minute
|
|
1053
|
+
};
|
|
1054
|
+
console.log(`Using custom date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
1055
|
+
} else {
|
|
1056
|
+
console.error('Invalid date:', options.d);
|
|
1057
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
1058
|
+
process.exit(1);
|
|
1059
|
+
}
|
|
1060
|
+
} else {
|
|
1061
|
+
console.error('Invalid date:', options.d);
|
|
1062
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
1063
|
+
process.exit(1);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Calculate next planet ingress(es)
|
|
1068
|
+
const ingresses = calculateNextPlanetIngress(planetName, customDate, count);
|
|
1069
|
+
|
|
1070
|
+
if (ingresses.length === 0) {
|
|
1071
|
+
console.log(`No ${planetName} ingresses found.`);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const planetNameCapitalized = planetName.charAt(0).toUpperCase() + planetName.slice(1);
|
|
1076
|
+
console.log(`Next ${planetNameCapitalized} Ingress(es):`);
|
|
1077
|
+
console.log('================================================================================');
|
|
1078
|
+
console.log('| # | Date & Time | From Sign | To Sign |');
|
|
1079
|
+
console.log('================================================================================');
|
|
1080
|
+
|
|
1081
|
+
ingresses.forEach((ingress, index) => {
|
|
1082
|
+
const dateStr = `${ingress.date.day.toString().padStart(2, '0')}.${ingress.date.month.toString().padStart(2, '0')}.${ingress.date.year} ${ingress.date.hour.toString().padStart(2, '0')}:${ingress.date.minute.toString().padStart(2, '0')}`;
|
|
1083
|
+
const fromSign = ingress.fromSign.padEnd(12, ' ');
|
|
1084
|
+
const toSign = ingress.sign.padEnd(12, ' ');
|
|
1085
|
+
|
|
1086
|
+
console.log(`| ${(index + 1).toString().padEnd(2)} | ${dateStr.padEnd(21)} | ${fromSign} | ${toSign} |`);
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
console.log('================================================================================');
|
|
1090
|
+
|
|
1091
|
+
if (count > 1) {
|
|
1092
|
+
console.log(`\nShowing ${ingresses.length} ${planetName} ingresses starting from ${customDate ? 'the specified date' : 'now'}.`);
|
|
1093
|
+
} else {
|
|
1094
|
+
console.log(`\nShowing the next ${planetName} ingress from ${customDate ? 'the specified date' : 'now'}.`);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// Handle AI request
|
|
1098
|
+
if (options.p) {
|
|
1099
|
+
const aiData = {
|
|
1100
|
+
planet: planetName,
|
|
1101
|
+
sign: 'Multiple',
|
|
1102
|
+
degreeInSign: 'Multiple',
|
|
1103
|
+
dignity: `Next ${planetName} ingress: ${ingresses[0].sign} at ${ingresses[0].date.day}.${ingresses[0].date.month}.${ingresses[0].date.year} ${ingresses[0].date.hour}:${ingresses[0].date.minute.toString().padStart(2, '0')}`,
|
|
1104
|
+
element: 'Multiple',
|
|
1105
|
+
decan: 'Multiple',
|
|
1106
|
+
planetIngresses: ingresses
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
await handleAIRequest(options, aiData);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1015
1115
|
// For other options, a planet is required (except for --a or --s without planet)
|
|
1016
1116
|
if (!planetArg && !options.a && !options.s) {
|
|
1017
1117
|
console.error('Error: Planet is required for this operation.');
|