dsh-metaphysics 0.38.2 → 0.46.5

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/engine/ziwei.js CHANGED
@@ -74,12 +74,182 @@ export const MUTAGEN = {
74
74
  癸: { 破军: '禄', 巨门: '权', 太阴: '科', 贪狼: '忌' },
75
75
  };
76
76
 
77
+ // ── 流派开关 ──────────────────────────────────────────────────
78
+ // 默认值对齐本插件既有盘面:亮度用内置通行表,庚干用阳武阴同,
79
+ // 天马按年支、魁钺用全书理解一、天使天伤不换、长生按现有五行局表、
80
+ // 晚子时仍算当日;天空/截空旬空默认不排(旧盘没有这两组星,保持 49 颗)。
81
+ // 其余选项只在显式指定时改变盘面,不影响默认输出。
82
+ export const ZIWEI_SCHOOL_DEFAULTS = Object.freeze({
83
+ brightnessSchool: 'default',
84
+ gengYearSihua: 'GYWYT',
85
+ tianmaSchool: 'year-branch',
86
+ tiankongSchool: 'off',
87
+ jiekongSchool: 'off',
88
+ kuiyueSchool: 'qs1',
89
+ tianshiTianshang: 'normal',
90
+ changshengSchool: 'yin-yang-shun-ni',
91
+ lateZishi: 'current-day',
92
+ });
93
+
94
+ export const ZIWEI_SCHOOL_OPTIONS = Object.freeze({
95
+ brightnessSchool: ['default', 'QS', 'XD1', 'ZZ', 'XD2'],
96
+ gengYearSihua: ['GYWYT', 'GYWTY', 'GYWFT', 'GYWFX', 'GYWTX'],
97
+ tianmaSchool: ['year-branch', 'month-branch'],
98
+ tiankongSchool: ['off', 'normal', 'shun-jia-shi'],
99
+ jiekongSchool: ['off', 'double', 'single', 'zhanyan'],
100
+ kuiyueSchool: ['qs1', 'qs2', 'zhong-yiming', 'network'],
101
+ tianshiTianshang: ['normal', 'zhongzhou'],
102
+ changshengSchool: ['yin-yang-shun-ni', 'shui-tu', 'huo-tu'],
103
+ lateZishi: ['current-day', 'next-day', 'all-current', 'day-cur-time-next'],
104
+ });
105
+
106
+ export function ziweiSchoolsOf(input = {}) {
107
+ const nested = input.schools && typeof input.schools === 'object' ? input.schools : {};
108
+ const out = {};
109
+ for (const [key, allowed] of Object.entries(ZIWEI_SCHOOL_OPTIONS)) {
110
+ const raw = input[key] !== undefined ? input[key] : nested[key];
111
+ out[key] = allowed.includes(raw) ? raw : ZIWEI_SCHOOL_DEFAULTS[key];
112
+ }
113
+ return out;
114
+ }
115
+
116
+ // 亮度表按流派取值:数值 1–7 按各派档位换成中文。
117
+ // QS / XD1 为七档(1庙…7陷),ZZ / XD2 为五档(1庙 2旺 3平 4闲 5陷)。
118
+ const BRIGHTNESS_LEVELS_7 = ['', '庙', '旺', '得', '利', '平', '不', '陷'];
119
+ const BRIGHTNESS_LEVELS_5 = ['', '庙', '旺', '平', '闲', '陷'];
120
+ export const BRIGHTNESS_SCHOOLS = {
121
+ QS: {
122
+ levels: BRIGHTNESS_LEVELS_7,
123
+ rows: {
124
+ 紫微: [5, 1, 2, 2, 3, 2, 1, 1, 2, 2, 3, 2],
125
+ 天机: [1, 7, 3, 2, 4, 5, 1, 7, 3, 2, 4, 5],
126
+ 太阳: [7, 6, 2, 1, 2, 2, 2, 3, 3, 5, 6, 7],
127
+ 武曲: [2, 1, 3, 4, 1, 5, 2, 1, 3, 4, 1, 5],
128
+ 天同: [2, 6, 4, 5, 5, 1, 7, 6, 2, 5, 5, 1],
129
+ 廉贞: [5, 4, 1, 5, 4, 7, 5, 4, 1, 5, 4, 7],
130
+ 天府: [1, 1, 1, 3, 1, 3, 2, 1, 3, 2, 1, 3],
131
+ 太阴: [1, 1, 2, 7, 7, 7, 6, 6, 4, 2, 2, 1],
132
+ 贪狼: [2, 1, 5, 4, 1, 7, 2, 1, 5, 4, 1, 7],
133
+ 巨门: [2, 6, 1, 1, 7, 2, 2, 6, 1, 1, 7, 2],
134
+ 天相: [1, 1, 1, 7, 3, 3, 1, 3, 1, 7, 3, 3],
135
+ 天梁: [1, 2, 1, 1, 1, 3, 1, 2, 7, 3, 1, 7],
136
+ 七杀: [2, 1, 1, 2, 1, 5, 2, 1, 1, 2, 1, 5],
137
+ 破军: [1, 2, 3, 7, 2, 5, 1, 2, 3, 7, 2, 5],
138
+ },
139
+ },
140
+ XD1: {
141
+ levels: BRIGHTNESS_LEVELS_7,
142
+ rows: {
143
+ 紫微: [5, 1, 1, 2, 3, 2, 1, 1, 2, 2, 3, 2],
144
+ 天机: [1, 7, 3, 2, 4, 5, 1, 7, 3, 2, 4, 5],
145
+ 太阳: [7, 6, 2, 1, 2, 2, 1, 3, 4, 5, 7, 7],
146
+ 武曲: [2, 1, 3, 4, 1, 5, 2, 1, 3, 4, 1, 5],
147
+ 天同: [2, 6, 4, 1, 5, 1, 7, 6, 2, 5, 5, 1],
148
+ 廉贞: [5, 4, 1, 5, 4, 7, 5, 4, 1, 5, 4, 7],
149
+ 天府: [1, 1, 1, 3, 2, 3, 2, 1, 3, 2, 2, 3],
150
+ 太阴: [1, 1, 6, 7, 7, 7, 7, 5, 4, 2, 2, 1],
151
+ 贪狼: [2, 1, 5, 3, 1, 7, 2, 1, 5, 4, 1, 7],
152
+ 巨门: [2, 6, 1, 1, 5, 5, 2, 5, 1, 1, 5, 2],
153
+ 天相: [1, 1, 1, 7, 5, 3, 3, 3, 1, 7, 3, 3],
154
+ 天梁: [1, 2, 1, 2, 1, 7, 1, 2, 7, 3, 1, 7],
155
+ 七杀: [2, 1, 1, 2, 3, 5, 2, 1, 1, 2, 1, 5],
156
+ 破军: [1, 2, 3, 7, 2, 5, 1, 2, 3, 7, 2, 5],
157
+ },
158
+ },
159
+ ZZ: {
160
+ levels: BRIGHTNESS_LEVELS_5,
161
+ rows: {
162
+ 紫微: [3, 1, 1, 2, 5, 2, 1, 1, 2, 3, 4, 2],
163
+ 天机: [1, 5, 2, 2, 1, 3, 1, 5, 3, 2, 1, 3],
164
+ 太阳: [5, 5, 2, 1, 2, 2, 1, 3, 4, 4, 5, 5],
165
+ 武曲: [2, 1, 4, 5, 1, 3, 2, 1, 3, 2, 1, 3],
166
+ 天同: [2, 5, 4, 1, 3, 1, 5, 5, 2, 3, 3, 1],
167
+ 廉贞: [3, 2, 1, 4, 2, 5, 3, 1, 1, 3, 2, 5],
168
+ 天府: [1, 1, 1, 3, 1, 3, 2, 1, 3, 5, 1, 2],
169
+ 太阴: [1, 1, 4, 5, 4, 5, 5, 3, 3, 2, 2, 1],
170
+ 贪狼: [2, 1, 3, 4, 1, 5, 2, 1, 3, 3, 1, 5],
171
+ 巨门: [2, 2, 1, 1, 3, 3, 2, 5, 1, 1, 2, 2],
172
+ 天相: [1, 1, 1, 5, 2, 3, 2, 4, 1, 5, 4, 3],
173
+ 天梁: [1, 2, 1, 1, 2, 5, 1, 2, 5, 4, 2, 5],
174
+ 七杀: [2, 1, 1, 5, 2, 3, 2, 2, 1, 4, 1, 3],
175
+ 破军: [1, 2, 5, 2, 2, 4, 1, 1, 5, 5, 2, 3],
176
+ },
177
+ },
178
+ XD2: {
179
+ levels: BRIGHTNESS_LEVELS_5,
180
+ rows: {
181
+ 紫微: [4, 1, 1, 2, 4, 2, 1, 1, 2, 3, 5, 2],
182
+ 天机: [1, 5, 2, 2, 2, 3, 1, 5, 2, 2, 2, 3],
183
+ 太阳: [5, 5, 2, 1, 2, 2, 1, 2, 3, 3, 5, 5],
184
+ 武曲: [2, 1, 3, 5, 1, 3, 2, 1, 3, 2, 1, 3],
185
+ 天同: [2, 5, 3, 1, 2, 1, 5, 5, 2, 3, 3, 1],
186
+ 廉贞: [3, 2, 1, 4, 2, 5, 3, 1, 1, 3, 2, 5],
187
+ 天府: [2, 1, 1, 3, 1, 2, 2, 1, 2, 3, 1, 2],
188
+ 太阴: [1, 1, 5, 5, 5, 5, 5, 5, 2, 2, 1, 1],
189
+ 贪狼: [2, 1, 3, 2, 1, 5, 2, 1, 3, 2, 1, 5],
190
+ 巨门: [2, 5, 1, 2, 5, 1, 2, 5, 1, 2, 5, 1],
191
+ 天相: [1, 1, 1, 5, 2, 3, 2, 3, 1, 5, 5, 3],
192
+ 天梁: [1, 2, 1, 1, 2, 5, 1, 2, 5, 3, 2, 5],
193
+ 七杀: [2, 2, 1, 5, 1, 3, 2, 2, 1, 3, 1, 3],
194
+ 破军: [1, 1, 5, 2, 2, 3, 1, 1, 5, 5, 2, 3],
195
+ },
196
+ },
197
+ };
198
+
199
+ export function schoolBrightness(name, branchIndex, school = 'default') {
200
+ const table = BRIGHTNESS_SCHOOLS[school];
201
+ if (!table || !table.rows[name]) return BRIGHTNESS[name] ? BRIGHTNESS[name][fix(branchIndex)] : undefined;
202
+ const level = table.rows[name][fix(branchIndex)];
203
+ return level ? table.levels[level] : undefined;
204
+ }
205
+
206
+ // 庚干四化五变体
207
+ const GENG_MUTAGEN = {
208
+ GYWYT: { 太阳: '禄', 武曲: '权', 太阴: '科', 天同: '忌' },
209
+ GYWTY: { 太阳: '禄', 武曲: '权', 天同: '科', 太阴: '忌' },
210
+ GYWFT: { 太阳: '禄', 武曲: '权', 天府: '科', 天同: '忌' },
211
+ GYWFX: { 太阳: '禄', 武曲: '权', 天府: '科', 天相: '忌' },
212
+ GYWTX: { 太阳: '禄', 武曲: '权', 天同: '科', 天相: '忌' },
213
+ };
214
+
215
+ function mutagenOf(stem, schools) {
216
+ if (stem === '庚') return GENG_MUTAGEN[schools.gengYearSihua] || MUTAGEN.庚;
217
+ return MUTAGEN[stem] ?? {};
218
+ }
219
+
220
+ // 安魁钺四派:差异集中在庚辛壬癸年
221
+ export const KUI_YUE_SCHOOLS = {
222
+ qs2: {
223
+ 甲: [1, 7], 戊: [1, 7], 庚: [6, 2], 乙: [0, 8], 己: [0, 8],
224
+ 丙: [11, 9], 丁: [11, 9], 辛: [6, 2], 壬: [5, 3], 癸: [5, 3],
225
+ },
226
+ 'zhong-yiming': {
227
+ 甲: [1, 7], 戊: [1, 7], 庚: [2, 6], 乙: [0, 8], 己: [0, 8],
228
+ 丙: [11, 9], 丁: [11, 9], 辛: [2, 6], 壬: [5, 3], 癸: [5, 3],
229
+ },
230
+ network: {
231
+ 甲: [1, 7], 戊: [1, 7], 庚: [1, 7], 乙: [0, 8], 己: [0, 8],
232
+ 丙: [11, 9], 丁: [11, 9], 辛: [6, 2], 壬: [3, 5], 癸: [3, 5],
233
+ },
234
+ };
235
+
236
+ // 截空(按生年干):single 单星、double 正副双星、zhanyan 占验派
237
+ const JIEKONG = {
238
+ single: { 甲: 8, 乙: 6, 丙: 4, 丁: 2, 戊: 0, 己: 9, 庚: 7, 辛: 5, 壬: 3, 癸: 1 },
239
+ double: {
240
+ 甲: [8, 9], 乙: [7, 6], 丙: [4, 5], 丁: [3, 2], 戊: [0, 1],
241
+ 己: [9, 8], 庚: [6, 7], 辛: [5, 4], 壬: [2, 3], 癸: [1, 0],
242
+ },
243
+ zhanyan: { 甲: 8, 乙: 6, 丙: 4, 丁: 2, 戊: 10, 己: 9, 庚: 7, 辛: 5, 壬: 3, 癸: 11 },
244
+ };
245
+
77
246
  // 年干系
78
247
  export const LUCUN = { 甲: 2, 乙: 3, 丙: 5, 丁: 6, 戊: 5, 己: 6, 庚: 8, 辛: 9, 壬: 11, 癸: 0 };
79
248
  export const KUI_YUE = {
80
249
  甲: [1, 7], 戊: [1, 7], 庚: [1, 7], 乙: [0, 8], 己: [0, 8],
81
250
  丙: [11, 9], 丁: [11, 9], 辛: [6, 2], 壬: [3, 5], 癸: [3, 5],
82
251
  };
252
+ KUI_YUE_SCHOOLS.qs1 = KUI_YUE;
83
253
 
84
254
  /** 命主:按命宫地支 */
85
255
  export const MING_ZHU = {
@@ -192,12 +362,31 @@ export function xiaoxianAge(branchIndex, triad, gender) {
192
362
 
193
363
  // ── 主流程 ────────────────────────────────────────────────────
194
364
 
365
+ /**
366
+ * 闰月口径:split 按日切分(1–15 归本月、16 起归次月,主流排盘默认)、
367
+ * next 整月归次月、prev 整月归本月。`leapAsNext: true` 是 next 的旧写法。
368
+ */
369
+ export const LEAP_POLICIES = ['split', 'next', 'prev'];
370
+
371
+ export function leapPolicyOf(input) {
372
+ if (input && LEAP_POLICIES.includes(input.leapMonth)) return input.leapMonth;
373
+ return input && input.leapAsNext ? 'next' : 'split';
374
+ }
375
+
376
+ export function effectiveLunarMonth(raw, policy) {
377
+ if (!raw || !raw.isLeap) return raw ? raw.month : 0;
378
+ if (policy === 'next') return (raw.month % 12) + 1;
379
+ if (policy === 'split') return raw.day <= 15 ? raw.month : (raw.month % 12) + 1;
380
+ return raw.month;
381
+ }
382
+
195
383
  /**
196
384
  * 排紫微斗数命盘
197
- * @param {object} input { year,month,day,hour,minute, gender, leapAsNext? }
385
+ * @param {object} input { year,month,day,hour,minute, gender, leapMonth? }
198
386
  */
199
387
  export function buildZiwei(input) {
200
388
  assertCivil(input);
389
+ const schools = ziweiSchoolsOf(input);
201
390
  const { year: cy, month: cm, day: cd, hour: ch = 0, minute: cmin = 0, gender = 'male' } = input;
202
391
  const useTrueSolar = input.trueSolar !== false;
203
392
  const geo = input.geo || (input.address ? resolveAddress(input.address) : null);
@@ -212,7 +401,9 @@ export function buildZiwei(input) {
212
401
  ? applyTrueSolar(civilJd, longitude)
213
402
  : { offsetMinutes: 0, jd: civilJd, note: '未启用真太阳时' };
214
403
  // 紫微按「时辰」起盘:真太阳时若跨时辰/跨日,命盘随之改变
215
- const eff = jdToParts(solar.jd);
404
+ // 晚子时 next-day:23:00 后起盘日顺延一日,只改起盘日,不改真太阳时显示
405
+ let eff = jdToParts(solar.jd);
406
+ if (schools.lateZishi === 'next-day' && eff.hour >= 23) eff = jdToParts(solar.jd + 1);
216
407
  const year = eff.year;
217
408
  const month = eff.month;
218
409
  const day = eff.day;
@@ -220,9 +411,8 @@ export function buildZiwei(input) {
220
411
  const minute = eff.minute;
221
412
 
222
413
  const lunarRaw = solarToLunar(year, month, day);
223
- const lunarMonth = lunarRaw.isLeap && input.leapAsNext
224
- ? (lunarRaw.month % 12) + 1
225
- : lunarRaw.month;
414
+ const leapPolicy = leapPolicyOf(input);
415
+ const lunarMonth = effectiveLunarMonth(lunarRaw, leapPolicy);
226
416
  const lunarDay = lunarRaw.day;
227
417
 
228
418
  const hb = hourBranchIndex(hour, minute);
@@ -232,6 +422,10 @@ export function buildZiwei(input) {
232
422
  const yearStem = yearGanzhi.stem;
233
423
  const yb = BRANCHES.indexOf(yearGanzhi.branch);
234
424
  const triad = triadOf(yb);
425
+ // 大限顺逆:阳男阴女顺行、阴男阳女逆行(天伤天使的中州派互换也要用它)
426
+ const yearYang = STEMS.indexOf(yearStem) % 2 === 0;
427
+ const forward = (yearYang && gender === 'male') || (!yearYang && gender === 'female');
428
+ const kuiyueTable = KUI_YUE_SCHOOLS[schools.kuiyueSchool] || KUI_YUE;
235
429
 
236
430
  // 命宫 / 身宫
237
431
  const soulIndex = fix(2 + (lunarMonth - 1) - hb);
@@ -247,12 +441,12 @@ export function buildZiwei(input) {
247
441
 
248
442
  // 星曜落宫
249
443
  const starsByBranch = Array.from({ length: 12 }, () => []);
250
- // 安星:主星顺带记下所在宫的庙旺得利平不陷(BRIGHTNESS 以地支序索引,与 fix(branch) 一致)
444
+ // 安星:主星顺带记下所在宫的庙旺得利平不陷(亮度可按流派切换,默认用内置通行表)
251
445
  const put = (branch, name, type) => {
252
446
  const idx = fix(branch);
253
447
  const star = { name, type };
254
448
  if (type === '主星' && BRIGHTNESS[name]) {
255
- const b = BRIGHTNESS[name][idx];
449
+ const b = schoolBrightness(name, idx, schools.brightnessSchool);
256
450
  if (b) star.brightness = b;
257
451
  }
258
452
  starsByBranch[idx].push(star);
@@ -278,7 +472,7 @@ export function buildZiwei(input) {
278
472
  put(lucun, '禄存', '吉星');
279
473
  put(lucun + 1, '擎羊', '煞星');
280
474
  put(lucun - 1, '陀罗', '煞星');
281
- const [kui, yue] = KUI_YUE[yearStem];
475
+ const [kui, yue] = kuiyueTable[yearStem];
282
476
  put(kui, '天魁', '吉星');
283
477
  put(yue, '天钺', '吉星');
284
478
 
@@ -290,7 +484,11 @@ export function buildZiwei(input) {
290
484
  put(6 + yb, '天虚', '煞星');
291
485
  put(4 + yb, '龙池', '吉星');
292
486
  put(10 - yb, '凤阁', '吉星');
293
- put(TIANMA[triad], '天马', '吉星');
487
+ // 天马:默认按年支三合,可切生月三合(正月起寅)
488
+ const maTriad = schools.tianmaSchool === 'month-branch'
489
+ ? triadOf(fix(2 + (lunarMonth - 1)))
490
+ : triad;
491
+ put(TIANMA[maTriad], '天马', '吉星');
294
492
  put(XIANCHI[triad], '咸池', '煞星');
295
493
  put(HUAGAI[triad], '华盖', '杂曜');
296
494
  const dirGroup = directionOf(yb);
@@ -298,6 +496,25 @@ export function buildZiwei(input) {
298
496
  put(GUASU[dirGroup], '寡宿', '煞星');
299
497
  put(posuIndex(yb), '破碎', '煞星');
300
498
 
499
+ // 空曜(默认不排,旧盘保持 49 颗)
500
+ if (schools.tiankongSchool !== 'off') {
501
+ put(schools.tiankongSchool === 'shun-jia-shi' ? yb + hb : yb + 1, '天空', '杂曜');
502
+ }
503
+ if (schools.jiekongSchool !== 'off') {
504
+ const jk = JIEKONG[schools.jiekongSchool][yearStem];
505
+ if (Array.isArray(jk)) {
506
+ put(jk[0], '截空', '杂曜');
507
+ put(jk[1], '副截', '杂曜');
508
+ } else {
509
+ put(jk, '截空', '杂曜');
510
+ }
511
+ // 旬空:本旬十日所空两支(年干支所在旬的末两位)
512
+ const nianIndex = (((lunarRaw.lunarYear - 4) % 60) + 60) % 60;
513
+ const decadeStart = nianIndex - (nianIndex % 10);
514
+ put(decadeStart + 10, '旬空', '杂曜');
515
+ put(decadeStart + 11, '副旬', '杂曜');
516
+ }
517
+
301
518
  // 月系
302
519
  put(9 + (lunarMonth - 1), '天刑', '煞星'); // 酉起正月顺行
303
520
  put(1 + (lunarMonth - 1), '天姚', '煞星'); // 丑起正月顺行
@@ -314,12 +531,13 @@ export function buildZiwei(input) {
314
531
  put(11 + hb, '地劫', '煞星'); // 亥宫起子时,顺行
315
532
  put(11 - hb, '地空', '煞星'); // 亥宫起子时,逆行
316
533
 
317
- // 固定宫位:天伤恒在交友,天使恒在疾厄
318
- put(soulIndex - 7, '天伤', '杂曜');
319
- put(soulIndex - 5, '天使', '杂曜');
534
+ // 固定宫位:天伤恒在交友、天使恒在疾厄;中州派阴男阳女互换
535
+ const swapTianshi = schools.tianshiTianshang === 'zhongzhou' && !forward;
536
+ put(soulIndex - (swapTianshi ? 5 : 7), '天伤', '杂曜');
537
+ put(soulIndex - (swapTianshi ? 7 : 5), '天使', '杂曜');
320
538
 
321
- // 生年四化
322
- const mutagenMap = MUTAGEN[yearStem] ?? {};
539
+ // 生年四化(庚干按所选变体)
540
+ const mutagenMap = mutagenOf(yearStem, schools);
323
541
  for (const list of starsByBranch) {
324
542
  for (const star of list) {
325
543
  if (mutagenMap[star.name]) star.mutagen = mutagenMap[star.name];
@@ -330,13 +548,14 @@ export function buildZiwei(input) {
330
548
  const palaceNames = [];
331
549
  for (let i = 0; i < 12; i++) palaceNames[fix(soulIndex - i)] = PALACE_NAMES[i];
332
550
 
333
- // 大限:阳男阴女顺行、阴男阳女逆行
334
- const yearYang = STEMS.indexOf(yearStem) % 2 === 0;
335
- const forward = (yearYang && gender === 'male') || (!yearYang && gender === 'female');
551
+ // 大限:阳男阴女顺行、阴男阳女逆行(forward 已在安星前算好)
336
552
  const startAge = bureau.number;
337
553
 
338
554
  // 十二神(长生/博士 随大限顺逆,将前/岁前 恒顺行)
339
- const csStart = CHANGSHENG_START[bureau.number];
555
+ // 土五局长生起点按流派:默认申,河洛/火土派取寅
556
+ const csStart = schools.changshengSchool === 'huo-tu' && bureau.number === 5
557
+ ? 2
558
+ : CHANGSHENG_START[bureau.number];
340
559
  const shenshaAt = (branch) => {
341
560
  const d = forward ? 1 : -1;
342
561
  return {
@@ -442,8 +661,8 @@ export function buildZiwei(input) {
442
661
  liulu: BRANCHES[lu],
443
662
  liuyang: BRANCHES[fix(lu + 1)],
444
663
  liutuo: BRANCHES[fix(lu - 1)],
445
- liukui: BRANCHES[KUI_YUE[gz.stem][0]],
446
- liuyue: BRANCHES[KUI_YUE[gz.stem][1]],
664
+ liukui: BRANCHES[kuiyueTable[gz.stem][0]],
665
+ liuyue: BRANCHES[kuiyueTable[gz.stem][1]],
447
666
  mutagen: Object.entries(MUTAGEN[gz.stem] ?? {}).map(([s, k]) => `${s}化${k}`),
448
667
  daxian: dxPalace
449
668
  ? { palace: dxPalace.palace, start: dxPalace.daxian.start, end: dxPalace.daxian.end }
@@ -457,13 +676,16 @@ export function buildZiwei(input) {
457
676
  input: {
458
677
  year: cy, month: cm, day: cd, hour: ch, minute: cmin,
459
678
  gender,
460
- leapAsNext: Boolean(input.leapAsNext),
679
+ leapMonth: leapPolicy,
680
+ leapAsNext: leapPolicy === 'next',
461
681
  longitude: longitude ?? null,
462
682
  address: input.address || null,
463
683
  geo,
464
684
  trueSolar: useTrueSolar,
465
685
  dst: { applied: !!dst.applied, note: dst.note || null },
686
+ schools: { ...schools },
466
687
  },
688
+ schools: { ...schools },
467
689
  solarTime: {
468
690
  civilText: `${cy}-${String(cm).padStart(2, '0')}-${String(cd).padStart(2, '0')} ${String(ch).padStart(2, '0')}:${String(cmin).padStart(2, '0')}`,
469
691
  trueSolarText: formatJd(solar.jd),
@@ -473,10 +695,16 @@ export function buildZiwei(input) {
473
695
  lunar: {
474
696
  year: lunarRaw.lunarYear,
475
697
  month: lunarMonth,
698
+ monthRaw: lunarRaw.month,
476
699
  day: lunarDay,
477
700
  isLeap: lunarRaw.isLeap,
701
+ leapPolicy,
478
702
  monthStart: lunarRaw.monthStart,
479
- ...(lunarRaw.isLeap ? { note: '闰月按本月起盘(可用 leapAsNext 改为按次月)' } : {}),
703
+ ...(lunarRaw.isLeap ? {
704
+ note: leapPolicy === 'split'
705
+ ? `闰${lunarRaw.month}月按日切分:1–15 按本月、16 起按次月;本盘闰月${lunarDay}日按${lunarMonth}月`
706
+ : leapPolicy === 'next' ? '闰月整月按次月起盘' : '闰月整月按本月起盘',
707
+ } : {}),
480
708
  },
481
709
  hourBranch,
482
710
  yearGanzhi: yearGanzhi.name,
@@ -512,8 +740,9 @@ export function buildZiwei(input) {
512
740
  },
513
741
  rules: {
514
742
  ziweiPlacement: '借数法:局数除日数取商,商数自寅宫顺行,借数奇逆偶顺',
515
- mutagen: '生年四化用中州派常用表;庚干各派有异(有以天同化科、太阴化忌者)',
516
- leapMonth: '闰月默认按本月,可切换为按次月',
743
+ mutagen: '生年四化用中州派常用表;庚干可按流派切换(默认阳武阴同,另有同科阴忌、府科、府科相忌、同科相忌)',
744
+ schools: `流派开关:亮度 ${schools.brightnessSchool}、庚干四化 ${schools.gengYearSihua}、天马 ${schools.tianmaSchool}、天空 ${schools.tiankongSchool}、截空 ${schools.jiekongSchool}、魁钺 ${schools.kuiyueSchool}、天伤天使 ${schools.tianshiTianshang}、长生 ${schools.changshengSchool}、晚子时 ${schools.lateZishi};庚干四化只作用于生年四化`,
745
+ leapMonth: '闰月默认按日切分(1–15 归本月、16 起归次月,主流排盘口径);可传 leapMonth: next(整月归次月)或 prev(整月归本月),leapAsNext: true 等同 next',
517
746
  auxStars: '火铃按年支三合起点顺数至生时;羊陀夹禄存;魁钺按年干;地空亥起子时逆行、地劫顺行;三台八座随辅弼;恩光天贵随昌曲退一位;台辅封诰按时支',
518
747
  shensha: '长生、博士随大限顺逆;将前、岁前恒顺行;天伤恒在交友、天使恒在疾厄',
519
748
  masters: '命主按命宫地支,身主按生年地支',
@@ -950,8 +1179,7 @@ export function flowOf(chart, asOf) {
950
1179
  const MI = a.minute === undefined ? now.getMinutes() : a.minute;
951
1180
 
952
1181
  const lr = solarToLunar(Y, M, D);
953
- const leapAsNext = Boolean(chart.input && chart.input.leapAsNext);
954
- const natalMonth = chart.lunar.isLeap && leapAsNext ? (chart.lunar.month % 12) + 1 : chart.lunar.month;
1182
+ const natalMonth = chart.lunar.month; // 已在起盘时按闰月口径换算过,这里不再二次换月
955
1183
  const natalHB = BRANCHES.indexOf(chart.hourBranch);
956
1184
  const hbNow = hourBranchIndex(H, MI);
957
1185
 
package/index.js CHANGED
@@ -92,8 +92,10 @@ export function apply(ctx) {
92
92
  if (next.system === 'xiaoliuren' && next.mode !== 'number' && !next.datetime && !next.date) {
93
93
  next.datetime = `${today}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
94
94
  }
95
- // 风水 natal 的流年飞星默认看今年;显式给了 fortuneYear 就按给的算
96
- if (next.system === 'fengshui' && next.action !== 'advise' && next.fortuneYear === undefined) {
95
+ // 风水 natal 的流年飞星默认看今年;玄空由 period / fortuneYear 自定,不补默认年份,
96
+ // 否则缺省会悄悄把「默认九运」变成「按今年定运」。
97
+ if (next.system === 'fengshui' && (next.action === undefined || next.action === 'natal')
98
+ && next.fortuneYear === undefined) {
97
99
  next.fortuneYear = now.getFullYear();
98
100
  }
99
101
  return compute(next);
@@ -108,7 +110,7 @@ export function apply(ctx) {
108
110
  system: {
109
111
  type: 'string',
110
112
  enum: ['bazi', 'ziwei', 'liuyao', 'tarot', 'meihua', 'daliuren', 'hepan', 'qimen', 'almanac', 'zeri', 'chenggu', 'xiaoliuren', 'star', 'numerology', 'mbti', 'trend', 'fengshui', 'name', 'convert'],
111
- description: 'bazi = 四柱八字(含调候寒暖与《穷通宝鉴》120 格用神);ziwei = 紫微斗数;liuyao = 六爻(纳甲、卦体关系、用神落位与月建日辰旺衰);tarot = 塔罗;meihua = 梅花易数;daliuren = 大六壬;hepan = 合盘(合婚),需同时给 partner;qimen = 奇门遁甲(时家转盘,拆补法);almanac = 黄历(某日宜忌/冲煞/建除/节气);zeri = 择日(某事项在日期区间内逐日打分排序);chenggu = 袁天罡称骨(骨重 + 通行本称骨歌原文 + 白话释义);xiaoliuren = 小六壬(时间或报数起课);star = 星座当日基调;numerology = 生命数字;mbti = 十六型人格(MBTI 风格 24 题问卷:给 answers 出类型,不给则先返回题目);trend = 人生 K 线(大运/流年走势可视化);fengshui = 风水(八宅命卦/流年飞星/门主灶布局);name = 起名(391 字库的用字五行评估与候选生成;未收录字可传 charElements 指定五行);convert = 公农历互转(solar2lunar 公历转农历 / lunar2solar 农历转公历)。',
113
+ description: 'bazi = 四柱八字(含调候寒暖与《穷通宝鉴》120 格用神);ziwei = 紫微斗数;liuyao = 六爻(纳甲、卦体关系、用神落位与月建日辰旺衰);tarot = 塔罗;meihua = 梅花易数;daliuren = 大六壬;hepan = 合盘(合婚),需同时给 partner;qimen = 奇门遁甲(时家转盘,拆补法);almanac = 黄历(某日宜忌/冲煞/建除/节气);zeri = 择日(某事项在日期区间内逐日打分排序);chenggu = 袁天罡称骨(骨重 + 通行本称骨歌原文 + 白话释义);xiaoliuren = 小六壬(时间或报数起课);star = 星座当日基调;numerology = 生命数字;mbti = 十六型人格(MBTI 风格 24 题问卷:给 answers 出类型,不给则先返回题目);trend = 人生 K 线(大运/流年走势可视化);fengshui = 风水(八宅命卦/流年飞星/门主灶布局/玄空下卦,action 三选一);name = 起名(391 字库的用字五行评估与候选生成;未收录字可传 charElements 指定五行);convert = 公农历互转(solar2lunar 公历转农历 / lunar2solar 农历转公历)。',
112
114
  },
113
115
  datetime: {
114
116
  type: 'string',
@@ -275,24 +277,78 @@ export function apply(ctx) {
275
277
  enum: [1, 3, 5],
276
278
  description: '塔罗张数:1 单张指引、3 过去现在未来、5 十字牌阵。默认 3。',
277
279
  },
280
+ leapMonth: {
281
+ type: 'string',
282
+ enum: ['split', 'next', 'prev'],
283
+ description: '紫微斗数:闰月口径。split(默认,按日切分:1–15 归本月、16 起归次月,主流排盘口径)、next(整月归次月)、prev(整月归本月)。',
284
+ },
278
285
  leapAsNext: {
279
286
  type: 'boolean',
280
- description: '紫微斗数:闰月是否按次月起盘(默认按本月起盘)。',
287
+ description: '紫微斗数:旧写法,true 等同 leapMonth: next。',
288
+ },
289
+ brightnessSchool: {
290
+ type: 'string',
291
+ enum: ['default', 'QS', 'XD1', 'ZZ', 'XD2'],
292
+ description: '紫微斗数:星曜亮度流派。default 用内置通行表;QS/XD1 为全书派/现代派七档,ZZ/XD2 为五档。只影响十四主星的庙旺得利平不陷。',
293
+ },
294
+ gengYearSihua: {
295
+ type: 'string',
296
+ enum: ['GYWYT', 'GYWTY', 'GYWFT', 'GYWFX', 'GYWTX'],
297
+ description: '紫微斗数:庚干四化变体,默认 GYWYT(阳武阴同)。仅作用于生年四化。',
298
+ },
299
+ tianmaSchool: {
300
+ type: 'string',
301
+ enum: ['year-branch', 'month-branch'],
302
+ description: '紫微斗数:天马安法。year-branch(默认)按年支三合,month-branch 改按生月三合。',
303
+ },
304
+ tiankongSchool: {
305
+ type: 'string',
306
+ enum: ['off', 'normal', 'shun-jia-shi'],
307
+ description: '紫微斗数:天空安法。off(默认不排)、normal(年支顺行一位)、shun-jia-shi(年支加生时)。',
308
+ },
309
+ jiekongSchool: {
310
+ type: 'string',
311
+ enum: ['off', 'double', 'single', 'zhanyan'],
312
+ description: '紫微斗数:截空/旬空安法。off(默认不排)、single 单星、double 正副双星、zhanyan 占验派;非 off 时同时排旬空、副旬。',
313
+ },
314
+ kuiyueSchool: {
315
+ type: 'string',
316
+ enum: ['qs1', 'qs2', 'zhong-yiming', 'network'],
317
+ description: '紫微斗数:天魁天钺安法,默认 qs1(全书理解一),差异集中在庚辛壬癸年。',
318
+ },
319
+ tianshiTianshang: {
320
+ type: 'string',
321
+ enum: ['normal', 'zhongzhou'],
322
+ description: '紫微斗数:天伤天使安法。normal(默认,天伤交友、天使疾厄)、zhongzhou(阴男阳女互换)。',
323
+ },
324
+ changshengSchool: {
325
+ type: 'string',
326
+ enum: ['yin-yang-shun-ni', 'shui-tu', 'huo-tu'],
327
+ description: '紫微斗数:长生十二神起点流派。默认 yin-yang-shun-ni;huo-tu 把土五局起点从申改寅;shui-tu 与默认同。',
328
+ },
329
+ lateZishi: {
330
+ type: 'string',
331
+ enum: ['current-day', 'next-day', 'all-current', 'day-cur-time-next'],
332
+ description: '紫微斗数:晚子时口径。current-day(默认,23:00 后仍按当日)、next-day(起盘日顺延次日);另两个别名保留为兼容值。',
281
333
  },
282
334
  action: {
283
335
  type: 'string',
284
- enum: ['natal', 'advise', 'assess', 'generate'],
285
- description: '子动作。fengshui:natal(默认,算八宅命卦+流年飞星,需生日与性别)、advise(算宅卦门主灶布局,需 sitting 或 facing,出生信息可选);name:assess(默认,评估已有名字,需 surname + givenName)、generate(生成候选名,需生辰,可选 surname / limit)。',
336
+ enum: ['natal', 'advise', 'xuankong', 'assess', 'generate'],
337
+ description: '子动作。fengshui:natal(默认,算八宅命卦+流年飞星,需生日与性别)、advise(算宅卦门主灶布局,需 sitting 或 facing,出生信息可选)、xuankong(玄空下卦九宫盘,需 sitting 或 facing,运由 period 或 fortuneYear 定,缺省九运并注明);name:assess(默认,评估已有名字,需 surname + givenName)、generate(生成候选名,需生辰,可选 surname / limit)。',
286
338
  },
287
339
  toAge: {
288
340
  type: 'number',
289
341
  description: '仅 trend 使用:算到几岁(10–100,默认 100)。',
290
342
  },
291
- sitting: { type: 'string', description: '仅 fengshui advise:坐山(24 山之一,如 子/午/乾)。与 facing 至少给一个。' },
292
- facing: { type: 'string', description: '仅 fengshui advise:向山(24 山之一)。缺 sitting 时按对宫反推。' },
343
+ sitting: { type: 'string', description: '仅 fengshui advise / xuankong:坐山(24 山之一,如 子/午/乾)。与 facing 至少给一个(玄空按坐向定山盘/向盘)。' },
344
+ facing: { type: 'string', description: '仅 fengshui advise / xuankong:向山(24 山之一)。缺 sitting 时按对宫反推。' },
293
345
  fortuneYear: {
294
346
  type: 'number',
295
- description: '仅 fengshui natal:流年九宫飞星的目标年份,省略则不算飞星(只算命卦与八星)。',
347
+ description: '仅 fengshui natal / xuankong:natal 用它定流年九宫飞星(省略则按今年);xuankong 用它定三元九运(省略按九运)。',
348
+ },
349
+ period: {
350
+ type: 'number',
351
+ description: '仅 fengshui xuankong:三元九运的运(1–9)。优先于 fortuneYear;省略时按 fortuneYear 定,再缺省按九运(2024–2043)并注明。',
296
352
  },
297
353
  surname: { type: 'string', description: '仅 name 使用:姓氏,如「李」(复姓整体写,如「欧阳」)。' },
298
354
  givenName: { type: 'string', description: '仅 name assess 使用:名(1–2 个汉字)。' },
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dsh-metaphysics",
3
- "version": "0.38.2",
3
+ "version": "0.46.5",
4
4
  "type": "module",
5
- "description": "命理排盘(玄学 xuanxue)for DeepSeek Harness: nineteen systems and eleven quick references (almanac, solar–lunar conversion, …), with climate adjustment (tiaohou), the 120 Qiongtong Baojian entries, 391-char name library, classical quotes (Ziping Zhenquan / Di Tian Sui / San Ming Tong Hui), hexagram body relations (六冲/六合/伏吟/反吟/化进神/化退神) plus yongshen placement (用神落位) with month/day strength (月建日辰旺衰) and whole-hexagram per-line strength (全卦逐爻旺衰) for liuyao, plus an MBTI-style 16-type questionnaire and exact dayun handover dates (交运日) with annual suiyun hints (岁运并临/伏吟反吟), palace conditions (击刑/入墓), month states (旺相休囚死) and stem–palace relations (天盘干与宫生克) for qimen, flying four-transformations with chained flights (四化飞星串连) and three-way-four-corner composite flights (三方四正复合飞化) for ziwei, and a bundled offline single-file page (命理排盘.html), and a panel-to-chat handoff that asks the Harness about a finished chart.",
5
+ "description": "命理排盘(玄学 xuanxue)for DeepSeek Harness: nineteen systems and eleven quick references (almanac, solar–lunar conversion, …), with climate adjustment (tiaohou), the 120 Qiongtong Baojian entries, 391-char name library with per-character pinyin, Kangxi stroke counts, five-grid (五格/三才) analysis and mechanical phonetics checks, classical quotes (Ziping Zhenquan / Di Tian Sui / San Ming Tong Hui), hexagram body relations (六冲/六合/伏吟/反吟/化进神/化退神) plus yongshen placement (用神落位) with month/day strength (月建日辰旺衰) and whole-hexagram per-line strength (全卦逐爻旺衰) for liuyao, plus an MBTI-style 16-type questionnaire and exact dayun handover dates (交运日) with annual suiyun hints (岁运并临/伏吟反吟), palace conditions (击刑/入墓), month states (旺相休囚死) and stem–palace relations (天盘干与宫生克) for qimen, flying four-transformations with chained flights (四化飞星串连), three-way-four-corner composite flights (三方四正复合飞化), switchable leap-month policy (闰月按日切分/整月归次月/整月归本月) and nine school switches (亮度/庚干四化/天马/天空/截空旬空/魁钺/天伤天使/长生/晚子时) for ziwei, and a bundled offline single-file page (命理排盘.html), and a panel-to-chat handoff that asks the Harness about a finished chart.",
6
6
  "keywords": [
7
7
  "玄学",
8
8
  "玄学排盘",