klio 1.2.2 → 1.2.3

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 CHANGED
@@ -174,6 +174,44 @@ Nächste 5 Konjunktionen zwischen saturn und neptun:
174
174
  ================================================================================
175
175
  ```
176
176
 
177
+ ### Aspektfiguren
178
+ ```bash
179
+ klio --af
180
+ ````
181
+ Kombiniere mit `--i`, `--d`, `--hs` oder `--p`
182
+ ```bash
183
+ klio --af --d "01.12.2025" --hs koch
184
+ Verwende benutzerdefiniertes Datum: 1.12.2025 19:00
185
+ Aktive Aspektfiguren:
186
+ ═══════════════════════════════════════════════════════════════════════════
187
+
188
+ Großes Trigon:
189
+ ────────────────────────────────────────────────────────────────────────────────
190
+ 1. Planeten: Jupiter, Merkur, Saturn
191
+ Planetenpositionen:
192
+ - Jupiter: Krebs 24.48° (Haus 1)
193
+ - Merkur: Skorpion 21.07° (Haus 5)
194
+ - Saturn: Fische 25.17° (Haus 10)
195
+ Trigone: jupiter-merkur, jupiter-saturn, merkur-saturn
196
+
197
+ Kite (Drachen):
198
+ ────────────────────────────────────────────────────────────────────────────────
199
+ 1. Planeten: Jupiter, Merkur, Saturn, Uranus
200
+ Planetenpositionen:
201
+ - Jupiter: Krebs 24.48° (Haus 1)
202
+ - Merkur: Skorpion 21.07° (Haus 5)
203
+ - Saturn: Fische 25.17° (Haus 10)
204
+ - Uranus: Stier 29.02° (Haus 11)
205
+ Trigon: jupiter-merkur, jupiter-saturn, merkur-saturn
206
+ Opposition: merkur-uranus
207
+
208
+ ═══════════════════════════════════════════════════════════════════════════
209
+ Gesamt: 2 Aspektfigur(en) gefunden
210
+
211
+ Diese Analyse basiert auf der aktuellen Planetenposition.
212
+
213
+ ```
214
+
177
215
  #### Elementverteilung
178
216
  ```bash
179
217
  klio --el
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klio",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "Eine CLI für astrologische Berechnungen",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -637,6 +637,448 @@ function determineAspectPhase(planet1, planet2, dateComponents, aspectType, targ
637
637
  }
638
638
  }
639
639
 
640
+ // Funktion zur Erkennung von Aspektfiguren
641
+ function detectAspectFigures(dateComponents, houseSystem = null, useBirthData = false, houses = null) {
642
+ const allAspects = getAllActiveAspects(dateComponents);
643
+ const aspectFigures = [];
644
+
645
+ // Hole die astrologischen Daten für alle Planeten, um Zeichen und Häuser zu erhalten
646
+ const planetDataCache = {};
647
+ const allPlanets = new Set(allAspects.flatMap(aspect => [aspect.planet1, aspect.planet2]));
648
+
649
+ allPlanets.forEach(planetName => {
650
+ planetDataCache[planetName] = getAstrologicalData(planetName, dateComponents);
651
+ });
652
+
653
+ // Häuser werden jetzt von der CLI übergeben, keine Berechnung mehr nötig
654
+ // Falls houses null ist, werden keine Hausinformationen angezeigt
655
+
656
+ // Erstelle eine einfache Darstellung der Aspekte für die Mustererkennung
657
+ const aspectMap = {};
658
+ allAspects.forEach(aspect => {
659
+ const key1 = `${aspect.planet1}-${aspect.planet2}`;
660
+ const key2 = `${aspect.planet2}-${aspect.planet1}`;
661
+
662
+ if (!aspectMap[key1] && !aspectMap[key2]) {
663
+ aspectMap[key1] = {
664
+ type: aspect.type,
665
+ angle: parseFloat(aspect.angle),
666
+ planets: [aspect.planet1, aspect.planet2]
667
+ };
668
+ }
669
+ });
670
+
671
+ // 1. T-Quadrat Erkennung
672
+ // Ein T-Quadrat besteht aus:
673
+ // - Zwei Oppositionen (180°)
674
+ // - Vier Quadrate (90°)
675
+ // - Drei Planeten, wobei zwei in Opposition stehen und der dritte mit beiden ein Quadrat bildet
676
+
677
+ const planetsInAspects = new Set();
678
+ allAspects.forEach(aspect => {
679
+ planetsInAspects.add(aspect.planet1);
680
+ planetsInAspects.add(aspect.planet2);
681
+ });
682
+
683
+ // Suche nach T-Quadrat Mustern
684
+ const tSquares = [];
685
+
686
+ // Durchsuche alle Planetenkombinationen
687
+ const planetsArray = Array.from(planetsInAspects);
688
+
689
+ for (let i = 0; i < planetsArray.length; i++) {
690
+ for (let j = i + 1; j < planetsArray.length; j++) {
691
+ for (let k = j + 1; k < planetsArray.length; k++) {
692
+ const planetA = planetsArray[i];
693
+ const planetB = planetsArray[j];
694
+ const planetC = planetsArray[k];
695
+
696
+ // Prüfe, ob wir eine Opposition zwischen zwei Planeten haben
697
+ const opposition1 = allAspects.find(a =>
698
+ ((a.planet1 === planetA && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetA)) &&
699
+ a.type === 'Opposition'
700
+ );
701
+
702
+ const opposition2 = allAspects.find(a =>
703
+ ((a.planet1 === planetA && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetA)) &&
704
+ a.type === 'Opposition'
705
+ );
706
+
707
+ const opposition3 = allAspects.find(a =>
708
+ ((a.planet1 === planetB && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetB)) &&
709
+ a.type === 'Opposition'
710
+ );
711
+
712
+ // Prüfe auf T-Quadrat Muster
713
+ if (opposition1) {
714
+ // Planet A und B in Opposition, prüfe ob Planet C mit beiden ein Quadrat bildet
715
+ const squareAC = allAspects.find(a =>
716
+ ((a.planet1 === planetA && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetA)) &&
717
+ a.type === 'Quadrat'
718
+ );
719
+
720
+ const squareBC = allAspects.find(a =>
721
+ ((a.planet1 === planetB && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetB)) &&
722
+ a.type === 'Quadrat'
723
+ );
724
+
725
+ if (squareAC && squareBC) {
726
+ tSquares.push({
727
+ type: 'T-Quadrat',
728
+ planets: [planetA, planetB, planetC],
729
+ opposition: `${planetA}-${planetB}`,
730
+ squares: [`${planetA}-${planetC}`, `${planetB}-${planetC}`],
731
+ focusPlanet: planetC
732
+ });
733
+ }
734
+ }
735
+
736
+ if (opposition2) {
737
+ // Planet A und C in Opposition, prüfe ob Planet B mit beiden ein Quadrat bildet
738
+ const squareAB = allAspects.find(a =>
739
+ ((a.planet1 === planetA && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetA)) &&
740
+ a.type === 'Quadrat'
741
+ );
742
+
743
+ const squareCB = allAspects.find(a =>
744
+ ((a.planet1 === planetC && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetC)) &&
745
+ a.type === 'Quadrat'
746
+ );
747
+
748
+ if (squareAB && squareCB) {
749
+ tSquares.push({
750
+ type: 'T-Quadrat',
751
+ planets: [planetA, planetB, planetC],
752
+ opposition: `${planetA}-${planetC}`,
753
+ squares: [`${planetA}-${planetB}`, `${planetC}-${planetB}`],
754
+ focusPlanet: planetB
755
+ });
756
+ }
757
+ }
758
+
759
+ if (opposition3) {
760
+ // Planet B und C in Opposition, prüfe ob Planet A mit beiden ein Quadrat bildet
761
+ const squareAB = allAspects.find(a =>
762
+ ((a.planet1 === planetA && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetA)) &&
763
+ a.type === 'Quadrat'
764
+ );
765
+
766
+ const squareAC = allAspects.find(a =>
767
+ ((a.planet1 === planetA && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetA)) &&
768
+ a.type === 'Quadrat'
769
+ );
770
+
771
+ if (squareAB && squareAC) {
772
+ tSquares.push({
773
+ type: 'T-Quadrat',
774
+ planets: [planetA, planetB, planetC],
775
+ opposition: `${planetB}-${planetC}`,
776
+ squares: [`${planetA}-${planetB}`, `${planetA}-${planetC}`],
777
+ focusPlanet: planetA
778
+ });
779
+ }
780
+ }
781
+ }
782
+ }
783
+ }
784
+
785
+ // 2. Großes Trigon Erkennung
786
+ // Ein Großes Trigon besteht aus drei Trigon-Aspekten (120°), die ein gleichseitiges Dreieck bilden
787
+ const grandTrines = [];
788
+
789
+ // Suche nach drei Planeten, die alle durch Trigone verbunden sind
790
+ for (let i = 0; i < planetsArray.length; i++) {
791
+ for (let j = i + 1; j < planetsArray.length; j++) {
792
+ for (let k = j + 1; k < planetsArray.length; k++) {
793
+ const planetA = planetsArray[i];
794
+ const planetB = planetsArray[j];
795
+ const planetC = planetsArray[k];
796
+
797
+ const trineAB = allAspects.find(a =>
798
+ ((a.planet1 === planetA && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetA)) &&
799
+ a.type === 'Trigon'
800
+ );
801
+
802
+ const trineAC = allAspects.find(a =>
803
+ ((a.planet1 === planetA && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetA)) &&
804
+ a.type === 'Trigon'
805
+ );
806
+
807
+ const trineBC = allAspects.find(a =>
808
+ ((a.planet1 === planetB && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetB)) &&
809
+ a.type === 'Trigon'
810
+ );
811
+
812
+ if (trineAB && trineAC && trineBC) {
813
+ grandTrines.push({
814
+ type: 'Großes Trigon',
815
+ planets: [planetA, planetB, planetC],
816
+ trines: [`${planetA}-${planetB}`, `${planetA}-${planetC}`, `${planetB}-${planetC}`]
817
+ });
818
+ }
819
+ }
820
+ }
821
+ }
822
+
823
+ // 3. Großes Kreuz Erkennung
824
+ // Ein Großes Kreuz besteht aus zwei Oppositionen und vier Quadraten
825
+ const grandCrosses = [];
826
+
827
+ // Suche nach vier Planeten, die zwei Oppositionen und vier Quadrate bilden
828
+ for (let i = 0; i < planetsArray.length; i++) {
829
+ for (let j = i + 1; j < planetsArray.length; j++) {
830
+ for (let k = j + 1; k < planetsArray.length; k++) {
831
+ for (let l = k + 1; l < planetsArray.length; l++) {
832
+ const planetA = planetsArray[i];
833
+ const planetB = planetsArray[j];
834
+ const planetC = planetsArray[k];
835
+ const planetD = planetsArray[l];
836
+
837
+ // Prüfe auf zwei Oppositionen
838
+ const opposition1 = allAspects.find(a =>
839
+ ((a.planet1 === planetA && a.planet2 === planetB) || (a.planet1 === planetB && a.planet2 === planetA)) &&
840
+ a.type === 'Opposition'
841
+ );
842
+
843
+ const opposition2 = allAspects.find(a =>
844
+ ((a.planet1 === planetC && a.planet2 === planetD) || (a.planet1 === planetD && a.planet2 === planetC)) &&
845
+ a.type === 'Opposition'
846
+ );
847
+
848
+ if (opposition1 && opposition2) {
849
+ // Prüfe auf vier Quadrate zwischen den Planeten
850
+ const squareAC = allAspects.find(a =>
851
+ ((a.planet1 === planetA && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetA)) &&
852
+ a.type === 'Quadrat'
853
+ );
854
+
855
+ const squareAD = allAspects.find(a =>
856
+ ((a.planet1 === planetA && a.planet2 === planetD) || (a.planet1 === planetD && a.planet2 === planetA)) &&
857
+ a.type === 'Quadrat'
858
+ );
859
+
860
+ const squareBC = allAspects.find(a =>
861
+ ((a.planet1 === planetB && a.planet2 === planetC) || (a.planet1 === planetC && a.planet2 === planetB)) &&
862
+ a.type === 'Quadrat'
863
+ );
864
+
865
+ const squareBD = allAspects.find(a =>
866
+ ((a.planet1 === planetB && a.planet2 === planetD) || (a.planet1 === planetD && a.planet2 === planetB)) &&
867
+ a.type === 'Quadrat'
868
+ );
869
+
870
+ if (squareAC && squareAD && squareBC && squareBD) {
871
+ grandCrosses.push({
872
+ type: 'Großes Kreuz',
873
+ planets: [planetA, planetB, planetC, planetD],
874
+ oppositions: [`${planetA}-${planetB}`, `${planetC}-${planetD}`],
875
+ squares: [`${planetA}-${planetC}`, `${planetA}-${planetD}`, `${planetB}-${planetC}`, `${planetB}-${planetD}`]
876
+ });
877
+ }
878
+ }
879
+ }
880
+ }
881
+ }
882
+ }
883
+
884
+ // 4. Yod (Finger Gottes) Erkennung
885
+ // Ein Yod besteht aus zwei Quinkunx-Aspekten (150°), die auf einen dritten Planeten zeigen
886
+ const yods = [];
887
+
888
+ // Suche nach Quinkunx-Aspekten (150°)
889
+ const quincunxes = allAspects.filter(a => a.type === 'Quinkunx');
890
+
891
+ // Gruppiere Quinkunx-Aspekte nach dem Fokusplaneten
892
+ const quincunxByFocus = {};
893
+ quincunxes.forEach(q => {
894
+ if (!quincunxByFocus[q.planet2]) {
895
+ quincunxByFocus[q.planet2] = [];
896
+ }
897
+ quincunxByFocus[q.planet2].push(q.planet1);
898
+ });
899
+
900
+ // Suche nach Planeten mit mindestens zwei Quinkunx-Aspekten
901
+ for (const [focusPlanet, sourcePlanets] of Object.entries(quincunxByFocus)) {
902
+ if (sourcePlanets.length >= 2) {
903
+ // Prüfe, ob die beiden Quelleplaneten durch ein Sextil (60°) verbunden sind
904
+ const source1 = sourcePlanets[0];
905
+ const source2 = sourcePlanets[1];
906
+
907
+ const sextile = allAspects.find(a =>
908
+ ((a.planet1 === source1 && a.planet2 === source2) || (a.planet1 === source2 && a.planet2 === source1)) &&
909
+ a.type === 'Sextil'
910
+ );
911
+
912
+ if (sextile) {
913
+ yods.push({
914
+ type: 'Yod (Finger Gottes)',
915
+ planets: [source1, source2, focusPlanet],
916
+ quincunxes: [`${source1}-${focusPlanet}`, `${source2}-${focusPlanet}`],
917
+ sextile: `${source1}-${source2}`,
918
+ focusPlanet: focusPlanet
919
+ });
920
+ }
921
+ }
922
+ }
923
+
924
+ // 5. Kite (Drachen) Erkennung
925
+ // Ein Kite besteht aus einem Großen Trigon mit einem zusätzlichen Planeten, der mit einem der Trigon-Planeten in Opposition steht
926
+ const kites = [];
927
+
928
+ // Durchsuche alle Großen Trigone
929
+ grandTrines.forEach(trine => {
930
+ const [planetA, planetB, planetC] = trine.planets;
931
+
932
+ // Suche nach einem vierten Planeten, der mit einem der Trigon-Planeten in Opposition steht
933
+ const allPlanets = new Set(planetsInAspects);
934
+
935
+ for (const planet of allPlanets) {
936
+ if (planet !== planetA && planet !== planetB && planet !== planetC) {
937
+ const oppositionA = allAspects.find(a =>
938
+ ((a.planet1 === planetA && a.planet2 === planet) || (a.planet1 === planet && a.planet2 === planetA)) &&
939
+ a.type === 'Opposition'
940
+ );
941
+
942
+ const oppositionB = allAspects.find(a =>
943
+ ((a.planet1 === planetB && a.planet2 === planet) || (a.planet1 === planet && a.planet2 === planetB)) &&
944
+ a.type === 'Opposition'
945
+ );
946
+
947
+ const oppositionC = allAspects.find(a =>
948
+ ((a.planet1 === planetC && a.planet2 === planet) || (a.planet1 === planet && a.planet2 === planetC)) &&
949
+ a.type === 'Opposition'
950
+ );
951
+
952
+ if (oppositionA || oppositionB || oppositionC) {
953
+ kites.push({
954
+ type: 'Kite (Drachen)',
955
+ planets: [planetA, planetB, planetC, planet],
956
+ trine: trine.trines,
957
+ opposition: oppositionA ? `${planetA}-${planet}` : oppositionB ? `${planetB}-${planet}` : `${planetC}-${planet}`
958
+ });
959
+ }
960
+ }
961
+ }
962
+ });
963
+
964
+ // Füge alle gefundenen Aspektfiguren zusammen
965
+ if (tSquares.length > 0) {
966
+ aspectFigures.push(...tSquares);
967
+ }
968
+ if (grandTrines.length > 0) {
969
+ aspectFigures.push(...grandTrines);
970
+ }
971
+ if (grandCrosses.length > 0) {
972
+ aspectFigures.push(...grandCrosses);
973
+ }
974
+ if (yods.length > 0) {
975
+ aspectFigures.push(...yods);
976
+ }
977
+ if (kites.length > 0) {
978
+ aspectFigures.push(...kites);
979
+ }
980
+
981
+ // Füge Zeichen und Häuserinformationen zu jeder Aspektfigur hinzu
982
+ aspectFigures.forEach(figure => {
983
+ figure.planetDetails = {};
984
+ figure.planets.forEach(planetName => {
985
+ const planetData = planetDataCache[planetName];
986
+ if (planetData) {
987
+ figure.planetDetails[planetName] = {
988
+ sign: planetData.sign,
989
+ degree: planetData.degreeInSign,
990
+ element: planetData.element,
991
+ dignity: planetData.dignity
992
+ };
993
+
994
+ // Füge Hausinformation hinzu, falls Häuser berechnet wurden
995
+ if (houses && houses.house) {
996
+ const planetLongitude = parseFloat(planetData.degreeInSign) + (signs.indexOf(planetData.sign) * 30);
997
+ const house = getPlanetHouse(planetLongitude, houses.house);
998
+ figure.planetDetails[planetName].house = house;
999
+ }
1000
+ }
1001
+ });
1002
+ });
1003
+
1004
+ return aspectFigures;
1005
+ }
1006
+
1007
+ // Funktion zur Anzeige von Aspektfiguren
1008
+ function showAspectFigures(dateComponents, useBirthData = false, houseSystem = null, houses = null) {
1009
+ const aspectFigures = detectAspectFigures(dateComponents, houseSystem, useBirthData, houses);
1010
+
1011
+ if (aspectFigures.length === 0) {
1012
+ console.log('Keine Aspektfiguren gefunden.');
1013
+ return;
1014
+ }
1015
+
1016
+ console.log('Aktive Aspektfiguren:');
1017
+ console.log('═══════════════════════════════════════════════════════════════════════════');
1018
+
1019
+ // Gruppiere nach Figur-Typ
1020
+ const figuresByType = {};
1021
+ aspectFigures.forEach(figure => {
1022
+ if (!figuresByType[figure.type]) {
1023
+ figuresByType[figure.type] = [];
1024
+ }
1025
+ figuresByType[figure.type].push(figure);
1026
+ });
1027
+
1028
+ // Zeige jede Figur an
1029
+ for (const [type, figures] of Object.entries(figuresByType)) {
1030
+ console.log(`\n${type}:`);
1031
+ console.log('─'.repeat(80));
1032
+
1033
+ figures.forEach((figure, index) => {
1034
+ console.log(`${index + 1}. Planeten: ${figure.planets.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(', ')}`);
1035
+
1036
+ // Zeige Zeichen und Häuserinformationen an
1037
+ if (figure.planetDetails) {
1038
+ console.log(' Planetenpositionen:');
1039
+ figure.planets.forEach(planetName => {
1040
+ const details = figure.planetDetails[planetName];
1041
+ if (details) {
1042
+ const planetDisplay = planetName.charAt(0).toUpperCase() + planetName.slice(1);
1043
+ let positionInfo = ` - ${planetDisplay}: ${details.sign} ${details.degree}°`;
1044
+ if (details.house) {
1045
+ positionInfo += ` (Haus ${details.house})`;
1046
+ }
1047
+ console.log(positionInfo);
1048
+ }
1049
+ });
1050
+ }
1051
+
1052
+ if (figure.type === 'T-Quadrat') {
1053
+ console.log(` Opposition: ${figure.opposition}`);
1054
+ console.log(` Quadrate: ${figure.squares.join(', ')}`);
1055
+ console.log(` Fokusplanet: ${figure.focusPlanet.charAt(0).toUpperCase() + figure.focusPlanet.slice(1)}`);
1056
+ } else if (figure.type === 'Großes Trigon') {
1057
+ console.log(` Trigone: ${figure.trines.join(', ')}`);
1058
+ } else if (figure.type === 'Großes Kreuz') {
1059
+ console.log(` Oppositionen: ${figure.oppositions.join(', ')}`);
1060
+ console.log(` Quadrate: ${figure.squares.join(', ')}`);
1061
+ } else if (figure.type === 'Yod (Finger Gottes)') {
1062
+ console.log(` Quinkunxe: ${figure.quincunxes.join(', ')}`);
1063
+ console.log(` Sextil: ${figure.sextile}`);
1064
+ console.log(` Fokusplanet: ${figure.focusPlanet.charAt(0).toUpperCase() + figure.focusPlanet.slice(1)}`);
1065
+ } else if (figure.type === 'Kite (Drachen)') {
1066
+ console.log(` Trigon: ${figure.trine.join(', ')}`);
1067
+ console.log(` Opposition: ${figure.opposition}`);
1068
+ }
1069
+ });
1070
+ }
1071
+
1072
+ console.log('\n═══════════════════════════════════════════════════════════════════════════');
1073
+ console.log(`Gesamt: ${aspectFigures.length} Aspektfigur(en) gefunden`);
1074
+
1075
+ if (useBirthData) {
1076
+ console.log('\nDiese Analyse basiert auf deinem Geburtshoroskop.');
1077
+ } else {
1078
+ console.log('\nDiese Analyse basiert auf der aktuellen Planetenposition.');
1079
+ }
1080
+ }
1081
+
640
1082
  // Funktion zur Analyse der Elementverteilung
641
1083
  function analyzeElementDistribution(dateComponents, useBirthData = false) {
642
1084
  const elementCounts = {
@@ -825,7 +1267,9 @@ module.exports = {
825
1267
  getPastAspects,
826
1268
  getAspectAngle,
827
1269
  getFutureAspects,
828
- findExactAspectTime
1270
+ findExactAspectTime,
1271
+ detectAspectFigures,
1272
+ showAspectFigures
829
1273
  };
830
1274
 
831
1275
  // Funktion zur Berechnung vergangener Aspekte zwischen zwei Planeten
package/src/cli/cli.js CHANGED
@@ -1,25 +1,7 @@
1
1
  const { Command } = require('commander');
2
- const {
3
- getAstrologicalData,
4
- calculateHouses,
5
- getPlanetHouse,
6
- getBirthDataFromConfig,
7
- getCriticalPlanets,
8
- showPlanetAspects,
9
- getCurrentTimeInTimezone,
10
- calculatePlanetAspects,
11
- showAllActiveAspects,
12
- getAllActiveAspects,
13
- calculateJulianDayUTC,
14
- getTimezoneOffset,
15
- analyzeElementDistribution,
16
- getPastAspects,
17
- getFutureAspects,
18
- getAspectAngle,
19
- findExactAspectTime
20
- } = require('../astrology/astrologyService');
21
2
  const { planets, signs } = require('../astrology/astrologyConstants');
22
3
  const { showRetrogradePlanets } = require('../astrology/retrogradeService');
4
+ const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, detectAspectFigures } = require('../astrology/astrologyService');
23
5
  const { performSetup, showConfigStatus, loadConfig, setAIModel, askAIModel } = require('../config/configService');
24
6
  const { parseAppleHealthXML } = require('../health/healthService');
25
7
  const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep } = require('../health/healthAnalysis');
@@ -117,6 +99,7 @@ program
117
99
  .option('--stress', 'Analysiert Stressmuster mit Mondaspekten basierend auf HRV')
118
100
  .option('--setup', 'Konfiguriert Standort und Geburtsdaten für personalisierte Berechnungen')
119
101
  .option('--ki <model>', 'Setzt ein bestimmtes KI-Modell (z.B. "google/gemma-3n-e4b")')
102
+ .option('--system <prompt>', 'Setzt einen benutzerdefinierten System-Prompt für alle KI-Anfragen')
120
103
  .option('--rx', 'Zeigt alle rückläufigen oder stationären Planeten an')
121
104
  .option('--kritisch', 'Zeigt alle Planeten auf kritischen Grad an')
122
105
  .option('--status', 'Zeigt die gespeicherten Konfigurationsdaten an')
@@ -125,6 +108,7 @@ program
125
108
  .option('--t <days>', 'Zeitraumbeschränkung (z.B. 7d, 30d, 90d, 14d)')
126
109
  .option('--p <prompt>', 'Stellt eine Frage an das KI-Modell (z.B. "Welche Berufe passen zu dieser Position?")')
127
110
  .option('--el', 'Zeigt die Elementverteilung der Planeten in einem horizontalen Chart an')
111
+ .option('--af', 'Zeigt aktive Aspektfiguren wie T-Quadrate, Große Trigone, etc. an')
128
112
  .option('--v <count>', 'Zeigt vergangene Aspekte zwischen zwei Planeten an (Format: --v <count> planet1 aspectType planet2)')
129
113
  .option('--z <count>', 'Zeigt zukünftige Aspekte zwischen zwei Planeten an (Format: --z <count> planet1 aspectType planet2)')
130
114
  .description('Zeigt astrologische Daten für einen Planeten an')
@@ -320,12 +304,131 @@ program
320
304
  return;
321
305
  }
322
306
 
307
+ // Aspektfiguren anzeigen, falls --af Option angegeben (ohne Planet erforderlich)
308
+ if (options.af) {
309
+ // Geburtsdaten verwenden, falls --ich Option angegeben
310
+ let customDate = null;
311
+ let useBirthData = false;
312
+
313
+ if (shouldUseBirthData(options)) {
314
+ const birthData = getBirthDataFromConfig();
315
+ if (birthData) {
316
+ customDate = birthData;
317
+ useBirthData = true;
318
+ console.log(`Verwende Geburtsdaten: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
319
+ if (birthData.location) {
320
+ console.log(`Geburtsort: ${birthData.location.name}, ${birthData.location.country}`);
321
+ }
322
+ } else {
323
+ console.log('Keine Geburtsdaten in der Konfiguration gefunden. Verwende aktuelle Daten.');
324
+ }
325
+ }
326
+ // Benutzerdefiniertes Datum verwenden, falls angegeben (überschreibt --ich Option)
327
+ else if (options.d) {
328
+ // Versuche, das Datum als DD.MM.YYYY oder DD.MM.YYYY HH:MM zu parsen
329
+ const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
330
+ const match = options.d.match(dateRegex);
331
+
332
+ if (match) {
333
+ const day = parseInt(match[1], 10);
334
+ const month = parseInt(match[2], 10);
335
+ const year = parseInt(match[3], 10);
336
+ const hour = match[4] ? parseInt(match[4], 10) : 12; // Standard: 12 Uhr
337
+ const minute = match[5] ? parseInt(match[5], 10) : 0; // Standard: 0 Minuten
338
+
339
+ // Überprüfe, ob das Datum gültig ist
340
+ const date = new Date(year, month - 1, day, hour, minute);
341
+ if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
342
+ customDate = {
343
+ day: day,
344
+ month: month,
345
+ year: year,
346
+ hour: hour,
347
+ minute: minute
348
+ };
349
+ console.log(`Verwende benutzerdefiniertes Datum: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
350
+ } else {
351
+ console.error('Ungültiges Datum:', options.d);
352
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM" (mit Anführungszeichen für Datum mit Uhrzeit)');
353
+ process.exit(1);
354
+ }
355
+ } else {
356
+ console.error('Ungültiges Datum:', options.d);
357
+ console.error('💡 Bitte verwende das Format: DD.MM.YYYY oder "DD.MM.YYYY HH:MM" (mit Anführungszeichen für Datum mit Uhrzeit)');
358
+ process.exit(1);
359
+ }
360
+ }
361
+
362
+ // Wenn kein benutzerdefiniertes Datum angegeben ist, verwende aktuelles Datum
363
+ if (!customDate) {
364
+ const currentTime = getCurrentTimeInTimezone();
365
+ customDate = {
366
+ day: currentTime.day,
367
+ month: currentTime.month,
368
+ year: currentTime.year,
369
+ hour: currentTime.hour,
370
+ minute: currentTime.minute
371
+ };
372
+ }
373
+
374
+ // Analysiere und zeige die Aspektfiguren
375
+ const houseSystem = options.hs ? options.hs.toLowerCase() : null;
376
+
377
+ // Berechne Häuser für die Anzeige, falls Haus-System angegeben
378
+ let housesForDisplay = null;
379
+ if (houseSystem) {
380
+ try {
381
+ const julianDay = getJulianDay(customDate, useBirthData);
382
+ const useBirthLocation = useBirthData;
383
+
384
+ if (houseSystem === 'koch') {
385
+ housesForDisplay = await calculateHouses(julianDay, 'K', useBirthLocation);
386
+ } else if (houseSystem === 'wholesign') {
387
+ housesForDisplay = await calculateHouses(julianDay, 'W', useBirthLocation);
388
+ } else if (houseSystem === 'gauquelin') {
389
+ housesForDisplay = await calculateHouses(julianDay, 'G', useBirthLocation);
390
+ }
391
+ } catch (error) {
392
+ console.error('Fehler bei der Hausberechnung für Aspektfiguren:', error);
393
+ }
394
+ }
395
+
396
+ showAspectFigures(customDate, useBirthData, houseSystem, housesForDisplay);
397
+
398
+ // Frage an das KI-Modell senden, falls --p Option angegeben
399
+ if (options.p) {
400
+ // Erstelle ein Datenobjekt für die KI mit den Aspektfiguren
401
+ const aspectFiguresData = detectAspectFigures(customDate, houseSystem, useBirthData, housesForDisplay);
402
+
403
+ const aiData = {
404
+ planet: 'alle',
405
+ sign: 'Multiple',
406
+ degreeInSign: 'Multiple',
407
+ dignity: `Aspektfiguren: ${aspectFiguresData.map(fig => `${fig.type} mit ${fig.planets.join(', ')}`).join(' | ')}`,
408
+ element: 'Multiple',
409
+ decan: 'Multiple',
410
+ aspectFigures: aspectFiguresData
411
+ };
412
+
413
+ await askAIModel(options.p, aiData);
414
+ }
415
+
416
+ return;
417
+ }
418
+
323
419
  // Konfigurationsstatus anzeigen, falls --status Option angegeben (ohne Planet erforderlich)
324
420
  if (options.status) {
325
421
  showConfigStatus();
326
422
  return;
327
423
  }
328
424
 
425
+ // System-Prompt setzen, falls --system Option angegeben (ohne Planet erforderlich)
426
+ if (options.system) {
427
+ const { setSystemPrompt } = require('../config/configService');
428
+ setSystemPrompt(options.system);
429
+ return;
430
+ }
431
+
329
432
  // KI-Modell setzen, falls --ki Option angegeben (ohne Planet erforderlich)
330
433
  if (options.ki) {
331
434
  setAIModel(options.ki);
@@ -88,6 +88,35 @@ function setAIModel(modelName) {
88
88
  return false;
89
89
  }
90
90
 
91
+ // Funktion zum Setzen eines benutzerdefinierten System-Prompts
92
+ function setSystemPrompt(prompt) {
93
+ const config = loadConfig() || {
94
+ currentLocation: null,
95
+ birthData: null,
96
+ setupDate: new Date().toISOString()
97
+ };
98
+
99
+ // Erstelle aiConfiguration, falls nicht vorhanden
100
+ if (!config.aiConfiguration) {
101
+ config.aiConfiguration = {
102
+ provider: 'lm-studio',
103
+ model: null,
104
+ endpoint: 'http://localhost:1234/api/v1/chat',
105
+ apiKey: null
106
+ };
107
+ }
108
+
109
+ // Setze den benutzerdefinierten System-Prompt
110
+ config.aiConfiguration.systemPrompt = prompt;
111
+
112
+ if (saveConfig(config)) {
113
+ console.log(`System-Prompt erfolgreich gesetzt: "${prompt}"`);
114
+ console.log('Dieser Prompt wird für alle zukünftigen KI-Anfragen verwendet.');
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+
91
120
  // Funktion zum Anzeigen der gespeicherten Konfiguration
92
121
  function showConfigStatus() {
93
122
  const config = loadConfig();
@@ -132,6 +161,11 @@ function showConfigStatus() {
132
161
  } else {
133
162
  console.log(`API-Schlüssel: Nicht konfiguriert`);
134
163
  }
164
+ if (config.aiConfiguration.systemPrompt) {
165
+ console.log(`System-Prompt: "${config.aiConfiguration.systemPrompt}"`);
166
+ } else {
167
+ console.log(`System-Prompt: Standard (astrologischer Berater)`);
168
+ }
135
169
  } else {
136
170
  console.log('Kein lokales KI-Modell konfiguriert');
137
171
  }
@@ -313,7 +347,34 @@ async function askAIModel(prompt, planetData, houseInfo = null) {
313
347
  }
314
348
 
315
349
  // Bereite die Frage mit den astrologischen Daten vor
316
- let systemPrompt = `Du bist ein astrologischer Berater. Beantworte kurz basierend auf den folgenden astrologischen Daten:
350
+ let systemPrompt;
351
+ let userPrompt;
352
+
353
+ // Verwende benutzerdefinierten System-Prompt, falls vorhanden
354
+ if (aiConfig.systemPrompt) {
355
+ systemPrompt = aiConfig.systemPrompt;
356
+ // Füge die astrologischen Daten als Kontext zur Nutzerfrage hinzu
357
+ userPrompt = `Astrologische Daten:
358
+ Planet: ${planetData.planet}
359
+ Zeichen: ${planetData.sign}
360
+ Grad im Zeichen: ${planetData.degreeInSign}°
361
+ Würde: ${planetData.dignity}
362
+ Element: ${planetData.element}
363
+ Dekan: ${planetData.decan}`;
364
+
365
+ // Füge Hausinformationen hinzu, falls verfügbar
366
+ if (houseInfo) {
367
+ userPrompt += `
368
+ Haus: ${houseInfo.house}
369
+ Haus-System: ${houseInfo.houseSystem}`;
370
+ }
371
+
372
+ userPrompt += `
373
+
374
+ Frage: ${prompt}`;
375
+ } else {
376
+ // Standard-System-Prompt
377
+ systemPrompt = `Du bist ein astrologischer Berater. Beantworte kurz die Frage in Fliesstext basierend auf den folgenden astrologischen Daten:
317
378
 
318
379
  Planet: ${planetData.planet}
319
380
  Zeichen: ${planetData.sign}
@@ -321,22 +382,24 @@ Grad im Zeichen: ${planetData.degreeInSign}°
321
382
  Würde: ${planetData.dignity}
322
383
  Element: ${planetData.element}
323
384
  Dekan: ${planetData.decan}`;
324
-
325
- // Füge Hausinformationen hinzu, falls verfügbar
326
- if (houseInfo) {
327
- systemPrompt += `
385
+
386
+ // Füge Hausinformationen hinzu, falls verfügbar
387
+ if (houseInfo) {
388
+ systemPrompt += `
328
389
  Haus: ${houseInfo.house}
329
390
  Haus-System: ${houseInfo.houseSystem}`;
330
- }
331
-
332
- systemPrompt += `
391
+ }
392
+
393
+ systemPrompt += `
333
394
 
334
395
  Die Frage des Nutzers lautet: ${prompt}`;
396
+ userPrompt = prompt; // Verwende die ursprüngliche Frage
397
+ }
335
398
 
336
399
  try {
337
400
  const response = await axios.post(`${baseUrl}`, {
338
401
  model: model,
339
- input: prompt,
402
+ input: userPrompt,
340
403
  system_prompt: systemPrompt,
341
404
  temperature: 0.7,
342
405
  max_output_tokens: 500
@@ -397,5 +460,6 @@ module.exports = {
397
460
  performSetup,
398
461
  showConfigStatus,
399
462
  setAIModel,
463
+ setSystemPrompt,
400
464
  askAIModel
401
465
  };