dsh-metaphysics 0.46.5 → 0.50.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/geju.js CHANGED
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { judgeClimate, arbitrate } from './tiaohou.js';
11
11
  import { qtOf } from './qiongtong.js';
12
+ import { sanheGroupOf } from './sanhe.js';
12
13
 
13
14
  const STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
14
15
  const BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
@@ -87,6 +88,25 @@ export function elementRoles(dayStem) {
87
88
 
88
89
  // ── 月令格 ───────────────────────────────────────────────────
89
90
 
91
+ // 四墓(杂气)月:辰戌丑未所藏多、用神不一,取用先看透干,再看会支。
92
+ // 会支只认三合局——子平真诠十六「逢申与子会局」的例子即三合,三会方与兼透兼用仍不纳入。
93
+ const ZA_MONTHS = ['辰', '未', '戌', '丑'];
94
+
95
+ function huiCandidate(pillars, monthBranch, cands) {
96
+ if (!ZA_MONTHS.includes(monthBranch)) return null;
97
+ const group = sanheGroupOf(monthBranch);
98
+ if (!group) return null;
99
+ const others = ['year', 'day', 'hour'];
100
+ const need = group.branches.filter((b) => b !== monthBranch);
101
+ if (!need.every((b) => others.some((k) => pillars[k].branch === b))) return null;
102
+ const cand = cands.find((c) => STEM_ELEMENT[c.stem] === group.element);
103
+ if (!cand) return null;
104
+ const where = others
105
+ .filter((k) => need.includes(pillars[k].branch))
106
+ .map((k) => `${({ year: '年', day: '日', hour: '时' })[k]}支${pillars[k].branch}`);
107
+ return { ...cand, group, where };
108
+ }
109
+
90
110
  function monthPattern(pillars, dayStem, monthBranch) {
91
111
  const hidden = HIDDEN[monthBranch] || [];
92
112
  const cands = hidden.map((stem, i) => ({
@@ -98,10 +118,13 @@ function monthPattern(pillars, dayStem, monthBranch) {
98
118
  const others = ['year', 'month', 'hour'];
99
119
  const exposedKey = others.find((k) => cands.some((c) => c.stem === pillars[k].stem));
100
120
  const base = cands[0];
101
- const use = exposedKey ? cands.find((c) => c.stem === pillars[exposedKey].stem) : base;
121
+ const hui = exposedKey ? null : huiCandidate(pillars, monthBranch, cands);
122
+ const use = exposedKey ? cands.find((c) => c.stem === pillars[exposedKey].stem) : (hui || base);
123
+ const usedBy = exposedKey ? '透干' : (hui ? '会支' : '本气');
102
124
  const exposedPos = exposedKey ? { year: '年', month: '月', hour: '时' }[exposedKey] : null;
103
125
 
104
- if (base.god === '比肩') {
126
+ // 建禄只认日干的禄位;戊土辰月、己土未月这类「本气比肩但不在禄位」的杂气月不再冒称建禄
127
+ if (base.god === '比肩' && LUSHEN_BRANCH[dayStem] === monthBranch) {
105
128
  return {
106
129
  name: '建禄格',
107
130
  kind: '月令格',
@@ -118,10 +141,31 @@ function monthPattern(pillars, dayStem, monthBranch) {
118
141
  }
119
142
  return { name: '月劫格', kind: '月令格', evidence: `月令${monthBranch}本气${base.stem}为劫财` };
120
143
  }
144
+ if (usedBy === '本气' && base.god === '比肩') {
145
+ return {
146
+ name: '比肩格',
147
+ kind: '月令格',
148
+ evidence: `月令${monthBranch}藏${hidden.join('')},本气${base.stem}为比肩,但${dayStem}禄在${LUSHEN_BRANCH[dayStem]}、不在${monthBranch},属杂气月比肩当权`,
149
+ };
150
+ }
151
+ // 子平十六:「透乙则用月劫」——杂气月由透干或会支取到劫财时,仍作月劫论
152
+ if (usedBy !== '本气' && use.god === '劫财') {
153
+ const via = exposedKey ? `透于${exposedPos}干` : `${hui.where.join('、')}会成${hui.group.element}局`;
154
+ return {
155
+ name: '月劫格',
156
+ kind: '月令格',
157
+ evidence: `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(劫财)${via},劫财为用,作月劫论`,
158
+ };
159
+ }
121
160
  const name = `${use.god}格`;
122
- const ex = exposedKey
123
- ? `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(${use.god})透于${exposedPos}干`
124
- : `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,以月令本气取格`;
161
+ let ex;
162
+ if (exposedKey) {
163
+ ex = `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(${use.god})透于${exposedPos}干`;
164
+ } else if (hui) {
165
+ ex = `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,而${hui.where.join('、')}会成${hui.group.element}局,取${use.pos}${use.stem}(${use.god})为用`;
166
+ } else {
167
+ ex = `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,以月令本气取格`;
168
+ }
125
169
  return { name, kind: '月令格', evidence: ex };
126
170
  }
127
171
 
@@ -236,11 +280,17 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
236
280
  const me = roles.me;
237
281
  const weak = strength.level === '身弱' || strength.level === '偏弱';
238
282
  const strong = strength.level === '身强' || strength.level === '偏强';
283
+ const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
284
+ : el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
285
+ const ROLE_VERB = { 比劫: '帮身', 印: '生身', 食伤: '泄秀', 财: '耗', 官杀: '制' };
239
286
 
240
287
  const baseFavor = weak ? [roles.inMe, me] : strong ? [roles.shi, roles.cai, roles.guan] : [roles.inMe, me, roles.shi];
241
288
  const baseAvoid = weak ? [roles.shi, roles.cai, roles.guan] : strong ? [me, roles.inMe] : [roles.guan, roles.cai];
242
289
  let favor = baseFavor.slice();
243
290
  let avoid = baseAvoid.slice();
291
+ // 调候所忌若正好是扶抑所喜(弱土遇燥的火、寒湿盘的强土遇水),两边不能同时列。
292
+ // 本表保留在喜用、不列入忌,并单列「有争议」;否则摘要会出现同一行既喜又忌。
293
+ const disputed = [];
244
294
 
245
295
  // 先判气候,再决定要不要让调候压过扶抑
246
296
  const climate = judgeClimate({ monthBranch, dayStem, wuxing, branches });
@@ -251,27 +301,39 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
251
301
  const needEl = climate.need.element;
252
302
  if (!favor.includes(needEl)) favor.unshift(needEl);
253
303
  avoid = avoid.filter((e) => e !== needEl);
254
- if (climate.avoidElement && !avoid.includes(climate.avoidElement)) avoid.unshift(climate.avoidElement);
304
+ const badEl = climate.avoidElement;
305
+ if (badEl && favor.includes(badEl)) {
306
+ disputed.push({
307
+ element: badEl,
308
+ favorRole: roleName(badEl),
309
+ favorVerb: ROLE_VERB[roleName(badEl)],
310
+ climate: climate.label,
311
+ note: `调候忌${badEl}(${climate.label}),但它在扶抑里为${roleName(badEl)};本表保留在喜用,不列入忌。`,
312
+ });
313
+ } else if (badEl && !avoid.includes(badEl)) {
314
+ avoid.unshift(badEl);
315
+ }
255
316
  }
256
317
 
257
- const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
258
- : el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
259
318
  const expand = {
260
319
  比劫: ['比肩', '劫财'], 印: ['正印', '偏印'], 食伤: ['食神', '伤官'],
261
320
  财: ['正财', '偏财'], 官杀: ['正官', '七杀'],
262
321
  };
263
322
  const favorTenGods = Array.from(new Set(favor.flatMap((el) => expand[roleName(el)] || [])));
264
323
  const avoidTenGods = Array.from(new Set(avoid.flatMap((el) => expand[roleName(el)] || [])));
324
+ const disputedTenGods = Array.from(new Set(disputed.flatMap((d) => expand[roleName(d.element)] || [])));
265
325
  const tongguan = tongguanOf(favor, avoid, wuxing);
266
326
 
327
+ const describe = (el) => `${el}(${roleName(el)}·${ROLE_VERB[roleName(el)]})`;
328
+ const disputedText = disputed
329
+ .map((d) => `${d.element}(扶抑为${d.favorRole}·${d.favorVerb};${d.climate}盘调候忌${d.element},本表保留在喜用)`)
330
+ .join('、');
267
331
  const detail = [
268
332
  `日主${dayStem}${me},${strength.level}(${strength.score} 分):`,
269
- weak
270
- ? `喜${roles.inMe}(印)生身、${me}(比劫)帮身;忌${roles.shi}(食伤)泄、${roles.cai}(财)耗、${roles.guan}(官杀)克。`
271
- : strong
272
- ? `喜${roles.shi}(食伤)泄秀、${roles.cai}(财)耗、${roles.guan}(官杀)制;忌${me}(比劫)与${roles.inMe}(印)再助。`
273
- : '身中和,先取调候与通关,喜忌随大运流年动态变化。',
274
- `调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}` : ',气候平和'}。`,
333
+ `喜${favor.map(describe).join('、') || '—'};忌${avoid.map(describe).join('、') || '—'}。`,
334
+ disputed.length ? `有争议:${disputedText}。` : '',
335
+ !weak && !strong ? '身中和,先取调候与通关,喜忌随大运流年动态变化。' : '',
336
+ `调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}${disputed.some((d) => d.element === climate.avoidElement) ? '(有争议)' : ''}` : ',气候平和'}。`,
275
337
  qt ? `穷通宝鉴:${qt.month}${dayStem}${me},用神${qt.yong.join('、')}(首取${qt.primary})。` : '',
276
338
  tongguan
277
339
  ? `通关:忌${tongguan.foe}克用${tongguan.use},五行以${tongguan.bridge}为关;局中${tongguan.level === '可通'
@@ -291,10 +353,12 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
291
353
  level: strength.level,
292
354
  favor,
293
355
  avoid,
356
+ disputed,
294
357
  favorRoles: favor.map(roleName),
295
358
  avoidRoles: avoid.map(roleName),
296
359
  favorTenGods,
297
360
  avoidTenGods,
361
+ disputedTenGods,
298
362
  tiaohou: { ...climate, ...arb, element: climate.need ? climate.need.element : null },
299
363
  climate,
300
364
  qt,
package/engine/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // 统一入口:解析参数、分派到各体系、生成可读摘要
2
2
  import { buildBazi } from './bazi.js';
3
- import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower } from './liuyao.js';
3
+ import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower, yingqiWindow } from './liuyao.js';
4
4
  import { buildZiwei, BRIGHTNESS, flowOf, asOfParts, ZIWEI_SCHOOL_DEFAULTS } from './ziwei.js';
5
5
  import { drawTarot } from './tarot.js';
6
6
  import { buildMeihua } from './meihua.js';
@@ -13,7 +13,7 @@ import { toJDN, ganzhiName, dayGanzhiIndex } from './calendar.js';
13
13
  import { randomOf, isSeed } from './random.js';
14
14
  import { interpret, DIRECTIONS, DIRECTION_DEFAULT } from './interpret.js';
15
15
  import { followup, askPrompt } from './followup.js';
16
- import { crossCheck, crossText } from './crosscheck.js';
16
+ import { crossCheck, crossText, crossSection } from './crosscheck.js';
17
17
  import { pastSignals, verifyFit } from './verify.js';
18
18
  import { rectifyBirth } from './rectify.js';
19
19
  import { buildAlmanac } from './almanac.js';
@@ -24,7 +24,8 @@ import { buildStar } from './star.js';
24
24
  import { buildNumerology } from './numerology.js';
25
25
  import { buildMbti, mbtiPaper } from './mbti.js';
26
26
  import { buildConvert } from './convert.js';
27
- import { classicsFor, classicsText } from './classics.js';
27
+ import { classicsFor, classicsText, suiyunClassicOf, classicOf, CLASSIC_BOOKS } from './classics.js';
28
+ import { diffObjects, summarize, sortDiffs, labelOf, rootOf, categoryOf, formatValue, CALIBER_SWITCHES } from './diff.js';
28
29
  import { buildTrend } from './trend.js';
29
30
  import { buildFengshui } from './fengshui.js';
30
31
  import { buildName } from './name.js';
@@ -126,12 +127,22 @@ function baziText(r) {
126
127
  ? `神煞(${r.shensha.count} 见):` + r.shensha.groups.map((g) =>
127
128
  `${g.cat}|` + g.items.map((i) => `${i.name}(${i.pillars.map((k) => SHORT[k]).join('')})`).join('、')).join(';')
128
129
  : '神煞:—';
130
+ const ry = r.renyuan;
131
+ const rySrc = classicOf('smt-renyuan');
132
+ const renyuanLine = ry
133
+ ? `人元:${ry.month}月第${ry.day}日,${ry.active}${ry.activeElement}司权(${ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' ')})${ry.beyond ? ';已过 30 日表,按本气算' : ''}${rySrc ? `|原文见《${CLASSIC_BOOKS[rySrc.book].name}·${rySrc.chapter}》` : ''}`
134
+ : '';
135
+ const ysd = (r.frame && r.frame.yongshen && r.frame.yongshen.disputed) || [];
136
+ const disText = ysd.length
137
+ ? ` 有争议:${ysd.map((d) => `${d.element}(${d.favorRole};${d.climate}盘调候忌${d.element})`).join('、')}`
138
+ : '';
139
+ const disEls = new Set(ysd.map((d) => d.element));
129
140
  const gejuLine = r.frame ? [
130
141
  `格局:${r.frame.primary ? `${r.frame.primary.name}(${r.frame.primary.evidence})` : '—'}`,
131
142
  `   ${r.frame.geju.map((g) => `${g.name}[${g.kind}]`).join(' ')}`,
132
- `用神:喜${r.frame.yongshen.favor.join('')}(${r.frame.yongshen.favorTenGods.join('、')}) 忌${r.frame.yongshen.avoid.join('')}(${r.frame.yongshen.avoidTenGods.join('、')}) ${r.frame.yongshen.method}`,
143
+ `用神:喜${r.frame.yongshen.favor.join('')}(${r.frame.yongshen.favorTenGods.join('、')}) 忌${r.frame.yongshen.avoid.join('')}(${r.frame.yongshen.avoidTenGods.join('、')})${disText} ${r.frame.yongshen.method}`,
133
144
  `   ${r.frame.yongshen.detail}`,
134
- `寒暖:${r.climate ? `${r.climate.label}(${(r.climate.evidence || []).join('、')})${r.climate.need ? ` 调候取${r.climate.need.stem}${r.climate.need.element} 忌${r.climate.avoidElement}` : ''}` : '—'}` +
145
+ `寒暖:${r.climate ? `${r.climate.label}(${(r.climate.evidence || []).join('、')})${r.climate.need ? ` 调候取${r.climate.need.stem}${r.climate.need.element} 忌${r.climate.avoidElement}${disEls.has(r.climate.avoidElement) ? '(有争议)' : ''}` : ''}` : '—'}` +
135
146
  (r.frame.yongshen.qt ? ` 穷通:${r.frame.yongshen.qt.month}${r.frame.yongshen.qt.stem}取${r.frame.yongshen.qt.yong.join('、')}(首取${r.frame.yongshen.qt.primary})` : ''),
136
147
  `十神权重:${Object.entries(r.frame.godStats.groups).map(([k, v]) => `${k}${v}`).join(' ')}`,
137
148
  ] : [];
@@ -148,15 +159,18 @@ function baziText(r) {
148
159
  shenshaLine,
149
160
  `真太阳时:${r.solarTime.trueSolarText}(${r.input.geo ? r.input.geo.note + ';' : ''}${r.solarTime.note}) 晚子时:${r.input.lateZi ? '23:00 后算次日' : '23:00 后仍算当日'}`,
150
161
  `节气:上一节 ${r.jieqi.previous.name} ${r.jieqi.previous.time} 下一节 ${r.jieqi.next.name} ${r.jieqi.next.time}`,
162
+ ...(renyuanLine ? [renyuanLine] : []),
151
163
  `大运:${r.dayun.rule}(${r.dayun.direction}),${r.dayun.startAge.years}岁${r.dayun.startAge.months}个月起运${jiao}`,
152
164
  ` ${dayun}`,
153
165
  `流年:${r.liunian.slice(0, 10).map((l) => `${l.year}${l.ganzhi}(${l.stemTenGod})${l.luck ? `·${l.luck.tier}` : ''}`).join(' ')}`,
154
166
  (() => {
155
167
  const hits = r.liunian.filter((l) => l.suiyun && l.suiyun.signals && l.suiyun.signals.length);
156
- if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或流年伏吟/反吟(只标干支,不判吉凶)';
157
- const label = (s) => `${s.type}${s.type.startsWith('流年') && s.target ? `(${s.target})` : ''}`;
168
+ if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或大运/流年伏吟、反吟(只标干支,不判吉凶)';
169
+ const label = (s) => `${s.type}${(s.type.startsWith('流年') || s.type.startsWith('大运')) && s.target ? `(${s.target})` : ''}`;
170
+ const e = suiyunClassicOf(r);
171
+ const src = e ? `|古籍见《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》` : '';
158
172
  return '岁运提示(只标干支变动,不判吉凶):' + hits
159
- .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';');
173
+ .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';') + src;
160
174
  })(),
161
175
  '(格局、用神、行运吉凶为扶抑+简化调候的量化提示,非断语。)',
162
176
  ].join('\n') + near;
@@ -408,6 +422,11 @@ function liuyaoText(r) {
408
422
  if (jt && Array.isArray(jt.lines) && jt.lines.length) {
409
423
  out.push(`进退力重:${jt.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type}·${l.verdict}`).join(' · ')}`);
410
424
  }
425
+ // 应期线索:只列地支与快慢,不给日期
426
+ const yq = r.yingqi;
427
+ if (yq && Array.isArray(yq.triggers) && yq.triggers.length) {
428
+ out.push(`应期线索:${yq.tempo} · ${yq.triggers.map((t) => `${t.labels.join('/')}${t.branch}`).join(' · ')}(只列地支,不出日期)`);
429
+ }
411
430
  return out.filter(Boolean).join('\n');
412
431
  }
413
432
 
@@ -428,6 +447,14 @@ export function compute(args = {}) {
428
447
  r.classics = classicsFor(r);
429
448
  const ct = classicsText(r.classics);
430
449
  if (ct) r.summary = `${r.summary}\n【古籍】\n${ct}`;
450
+ // 岁运提示的原文不占五条上限:只在盘面真有岁运信号时挂到结果上,摘要、解析层与面板各自取用
451
+ const syc = suiyunClassicOf(r);
452
+ if (syc) r.suiyunClassic = { ...syc, bookName: CLASSIC_BOOKS[syc.book].name };
453
+ // 人元司事的原文同样不进五条取条表:每张八字盘都有人元,随「人元司权」一节给出
454
+ if (r.renyuan) {
455
+ const ryc = classicOf('smt-renyuan');
456
+ if (ryc) r.renyuanClassic = { ...ryc, bookName: CLASSIC_BOOKS[ryc.book].name };
457
+ }
431
458
  }
432
459
  // 超出 ΔT 覆盖范围时把精度提示顶到摘要最前,别让远期盘悄悄出错
433
460
  if (r && r.precision && r.precision.level === 'approx' && r.summary) {
@@ -462,7 +489,7 @@ export function compute(args = {}) {
462
489
  : buildBazi({ ...common, lateZi: args.lateZi !== false, nowYear: asOf ? asOf.year : undefined });
463
490
  r.cross = crossCheck(r.system === 'bazi' ? r : other, r.system === 'ziwei' ? r : other);
464
491
  if (r.interpret) {
465
- r.interpret.sections.push({ key: 'crossSystem', label: '八字 × 紫微 交叉验证', text: crossText(r.cross) });
492
+ r.interpret.sections.push(crossSection(r.cross));
466
493
  }
467
494
  }
468
495
  // 验前事:把过去若干年的流年信号摊开(只读,不改盘)
@@ -548,6 +575,69 @@ export function interpretText(i) {
548
575
  return out.join('\n');
549
576
  }
550
577
 
578
+ /** 对照时不参与计算的参数:它们只影响解析/附加模块,不影响盘面 */
579
+ const CALIBER_IGNORE_ARGS = ['cross', 'verifyPast', 'withInterpret', 'question', 'direction', 'ask', 'geocode', 'rectify', 'events'];
580
+
581
+ /**
582
+ * 口径对照:同一组输入,只把某一个口径开关换成它的另一档,逐字段列出变化。
583
+ * 这一步不新增断法,也不改盘;返回根因字段、分类计数与一句话总结,供面板/独立页/工具共用。
584
+ */
585
+ export function caliberCompare(args = {}, opts = {}) {
586
+ const system = opts.system || args.system;
587
+ const switches = opts.switches || CALIBER_SWITCHES[system] || [];
588
+ if (!switches.length) return { system, switches: [], affected: 0, total: 0 };
589
+ const base = { ...args };
590
+ for (const k of CALIBER_IGNORE_ARGS) delete base[k];
591
+ const rows = switches.map((sw) => {
592
+ const current = sw.read(base);
593
+ const alt = sw.alt(current);
594
+ let diffs = [];
595
+ let error = '';
596
+ try {
597
+ diffs = diffObjects(
598
+ compute({ ...base, [sw.key]: current }),
599
+ compute({ ...base, [sw.key]: alt }),
600
+ );
601
+ } catch (e) {
602
+ error = (e && e.message) || String(e);
603
+ }
604
+ const roots = sortDiffs(diffs.filter((d) => rootOf(d.path))).map((d) => ({
605
+ path: d.path,
606
+ label: labelOf(d.path),
607
+ from: formatValue(d.from),
608
+ to: formatValue(d.to),
609
+ }));
610
+ const counts = new Map();
611
+ for (const d of diffs) {
612
+ const c = categoryOf(d.path) || '其他';
613
+ counts.set(c, (counts.get(c) || 0) + 1);
614
+ }
615
+ const categories = [...counts.entries()]
616
+ .map(([name, count]) => ({ name, count }))
617
+ .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name, 'zh-Hans-CN'));
618
+ return {
619
+ key: sw.key,
620
+ title: sw.title,
621
+ short: sw.short,
622
+ current,
623
+ alt,
624
+ currentLabel: sw.label(current),
625
+ altLabel: sw.label(alt),
626
+ total: diffs.length,
627
+ summary: error ? '' : summarize(diffs),
628
+ roots,
629
+ categories,
630
+ error,
631
+ };
632
+ });
633
+ return {
634
+ system,
635
+ switches: rows,
636
+ affected: rows.filter((r) => !r.error && r.total > 0).length,
637
+ total: rows.reduce((n, r) => n + r.total, 0),
638
+ };
639
+ }
640
+
551
641
  const JU_METHODS = ['chaibu', 'chaoshen', 'zhirun'];
552
642
  const TAROT_COUNTS = [1, 3, 5];
553
643
  const LATE_ZI_MODES = ['same-day', 'next-day'];
@@ -745,6 +835,9 @@ function computeRaw(args = {}) {
745
835
  // 伏神出伏条件:飞伏生克叠月建日辰,只判条件、不出应期
746
836
  const chufu = fushenAccess(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
747
837
  if (chufu) result.fushenAccess = chufu;
838
+ // 应期线索:只列逢值/逢冲/逢合与旬空的出空/冲空,再给快慢方向,不换算日期
839
+ const yq = yingqiWindow(result, ug, { monthBranch });
840
+ if (yq) result.yingqi = yq;
748
841
  }
749
842
  if (args.seed !== undefined && args.seed !== null && args.seed !== '') result.seed = Number(args.seed);
750
843
  return { ...result, ...(asOf ? { asOf } : {}), summary: liuyaoText(result) };
@@ -1170,6 +1263,15 @@ export function renderText(result) {
1170
1263
  if (extra) parts.push(extra);
1171
1264
  }
1172
1265
  if (result.cross) parts.push(`【八字 × 紫微 交叉验证】\n${crossText(result.cross)}`);
1266
+ if (result.caliber && Array.isArray(result.caliber.switches) && result.caliber.switches.length) {
1267
+ const c = result.caliber;
1268
+ parts.push('【口径对照】同一组输入,只换一个口径(不判哪套更准,也不改盘面)\n'
1269
+ + c.switches.map((s) => {
1270
+ if (s.error) return `· ${s.title}:未能对照(${s.error})`;
1271
+ if (!s.total) return `· ${s.title}:${s.currentLabel} → ${s.altLabel},本盘无变化`;
1272
+ return `· ${s.title}:${s.currentLabel} → ${s.altLabel}\n ${s.summary}`;
1273
+ }).join('\n'));
1274
+ }
1173
1275
  if (result.followup) {
1174
1276
  const f = result.followup;
1175
1277
  parts.push(`【追问】${f.question}\n${f.answer}${f.evidence && f.evidence.length ? `\n 依据:${f.evidence.join(';')} 置信度:${f.confidence}` : ''}`);
@@ -1196,7 +1298,7 @@ export { geocodeAddress, resolveAddress };
1196
1298
  export { buildBazi, buildLiuyao, tossLines };
1197
1299
  export { followup, askPrompt };
1198
1300
  export { pastSignals, verifyFit, rectifyBirth };
1199
- export { crossCheck, crossText };
1301
+ export { crossCheck, crossText, crossSection };
1200
1302
  export { buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert };
1201
1303
  export { buildTrend, buildFengshui, buildName };
1202
1304
 
@@ -15,10 +15,10 @@ export const KNOWLEDGE = [
15
15
  id: 'bazi-yueling-siquan',
16
16
  system: 'bazi', dims: ['overall', 'career'],
17
17
  gist: '月令不是「一个月」,是当月最有权的那一个字。',
18
- detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。',
19
- source: '《子平真诠》论用神;人元司权分日之说见《渊海子平》',
18
+ detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。本插件按 30 日表算出「生在本月第几日、哪一干当权」,给的是月内轮值这一层细节;旺衰与取格仍以月令本气、透干与会支为主,不随轮值改判。三命通会卷二·论人元司事另列一套日数(寅月五日/五日/二十日),与本插件取的通行表不同;原文并列,不改判。',
19
+ source: '《子平真诠》论用神;人元司权分日之说见《渊海子平》;三命通会卷二·论人元司事(典藏网 7529)另列一套日数',
20
20
  status: 'variant',
21
- note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说),本插件只取「本气当权」这一层,未细分日数。',
21
+ note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说);本插件固定通行 30 日表(如寅月戊7丙7甲16),节当日为第 1 日,第 31 日以后按本气算、不硬分摊。三命通会此本寅月作 5/5/20、卯月作 7/23,与通行表不同,两表各照原样。司权表与本柱藏干表口径不同(子月壬、卯月甲、午月丙、酉月庚、亥月戊只出现在司权表里),两表各照原样;取格不随轮值改判。',
22
22
  },
23
23
  {
24
24
  id: 'bazi-tonggen-tougan',
@@ -66,10 +66,10 @@ export const KNOWLEDGE = [
66
66
  id: 'bazi-suiyun',
67
67
  system: 'bazi', dims: ['overall', 'career'],
68
68
  gist: '大运管十年基调,流年管当年触发。',
69
- detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。',
70
- source: '岁运并临、伏吟反吟见《三命通会》《渊海子平》',
69
+ detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、大运伏吟(大运与原局某柱干支相同)、大运反吟(大运与原局某柱天克地冲)、流年伏吟(流年与原局某柱干支相同)、流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出六类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;大运与本命四柱的伏吟/反吟整步大运都在,会连续多年重复列出;只标干支重复与对冲,不折分数。原文里「返吟」指岁运与日柱相对、「伏吟」指岁运压日,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义,两者并列、不合并。',
70
+ source: '《三命通会》卷二·总论岁运(https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7539.html);岁运并临之说出自《渊海子平》',
71
71
  status: 'variant',
72
- note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与原局之间的伏吟反吟未逐年标注,四柱十神层面的引化也未叠入。',
72
+ note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与本命四柱的伏吟/反吟会连续多年出现,不逐年加重;四柱十神层面的引化也未叠入。',
73
73
  },
74
74
  {
75
75
  id: 'bazi-liuqin',
@@ -125,6 +125,15 @@ export const KNOWLEDGE = [
125
125
  status: 'variant',
126
126
  note: '化气格成立条件各家宽严不一(是否要求月令为化神、日主是否允许有根),本插件只列条件、附原文,不据此断定化气已成。',
127
127
  },
128
+ {
129
+ id: 'bazi-zaji-quyong',
130
+ system: 'bazi', dims: ['overall', 'career'],
131
+ gist: '辰戌丑未是杂气月:先看透干,再看会支。',
132
+ detail: '四墓(辰戌丑未)每个字都藏着不止一气,用神不一,所以叫杂气。子平真诠的取法是:藏干透到天干的先用(甲生辰月,透戊用偏财、透癸用正印、透乙用月劫);没有透干,再看地支有没有与月支会成三合局(辰月逢申子会水,就取水印)。本插件按「透干 → 三合会支 → 本气」的次序取格,依据里写明走的是哪一条,并把《论杂气如何取用》附在结果里。',
133
+ source: '《子平真诠》十六、论杂气如何取用',
134
+ status: 'variant',
135
+ note: '原文说「兼透则兼用」,但只给一个格名时总要落到一个藏干;本插件只取最先透出的那一干,会支也只认三合、不认三会方,且不判原文所说的「有情 / 无情」,取舍次序各家另有说法。',
136
+ },
128
137
  {
129
138
  id: 'bazi-zhuanwang-yuanwen',
130
139
  system: 'bazi', dims: ['overall', 'career'],
@@ -290,10 +299,10 @@ export const KNOWLEDGE = [
290
299
  id: 'liuyao-yingqi',
291
300
  system: 'liuyao', dims: ['overall', 'career'],
292
301
  gist: '应期是「什么时候见分晓」,不是「哪天」。',
293
- detail: '近事以日计、远事以月计;常见取法是等用神逢值、逢冲、逢合之期,或等忌神受制之期。应期法各家互有出入,同一卦不同取法可以差出很远,所以只作时间窗口的提示。',
302
+ detail: '近事以日计、远事以月计;常见取法是等用神逢值、逢冲、逢合之期,或等忌神受制之期。本插件只做前一半:把用神(不上卦则取伏神)的逢值、逢冲、逢合三支列出,旬空另标出空与冲空,再按月建五态给「偏速 / 偏缓 / 待解 / 未定」一个快慢方向;忌神受制与具体日期仍未做。应期法各家互有出入,同一卦不同取法可以差出很远,所以只作时间窗口的提示。',
294
303
  source: '《卜筮正宗》应期章;《增删卜易》',
295
304
  status: 'variant',
296
- note: '应期取法(值日、冲、合、填实、旬空出空)各家优先级不同,本插件不做具体日期推算。',
305
+ note: '应期取法(值日、冲、合、填实、旬空出空)各家优先级不同,本插件只并列触发支、不排先后,也不换算日期;近事以日计、远事以月计,用哪把尺由事情远近决定,不代选。',
297
306
  },
298
307
  {
299
308
  id: 'liuyao-liushen',
@@ -450,10 +459,10 @@ export const KNOWLEDGE = [
450
459
  id: 'liuyao-yingqi-exact',
451
460
  system: 'liuyao', dims: [],
452
461
  gist: '具体应期(哪一天应验)本插件尚未推算。',
453
- detail: '应期需要综合用神旺衰、动变、旬空出空、日月冲合多层条件,且各家取法不同。当前只给时间窗口的方向性提示,不给具体日期——与其给一个看起来精确、实则拍脑袋的日子,不如留白。',
462
+ detail: '应期需要综合用神旺衰、动变、旬空出空、日月冲合多层条件,且各家取法不同。当前只给时间窗口的方向性提示(逢值 / 逢冲 / 逢合的触发支,加一个快慢方向),不给具体日期——与其给一个看起来精确、实则拍脑袋的日子,不如留白。',
454
463
  source: '待补:《卜筮正宗》应期章逐条实现 + 已录 128 条卦例对拍(源 138 条)',
455
464
  status: 'todo',
456
- note: '扩展方向:用《卜筮正宗》卦例里有明确应验日期的条目做回归,先验证再输出。',
465
+ note: '扩展方向:用《卜筮正宗》卦例里有明确应验日期的条目做回归,先验证再输出。0.50.0 已给出触发支与快慢方向,仍不输出日期。',
457
466
  },
458
467
  ];
459
468
 
@@ -90,6 +90,19 @@ function jinTuiPlain(r) {
90
90
  return `动爻的进退还要看时令:${jp.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type},${sayOf(l)}`).join(';')}。`;
91
91
  }
92
92
 
93
+ // 应期线索 → 一句白话:只给快慢与可留意的支,不换算日期
94
+ function yingqiPlain(r) {
95
+ const yq = r && r.yingqi;
96
+ if (!yq || !Array.isArray(yq.triggers) || !yq.triggers.length) return '';
97
+ const when = yq.tempo === '偏速' ? '大体偏近,近事更容易先见分晓'
98
+ : yq.tempo === '偏缓' ? '大体偏远,别指望立刻见分晓'
99
+ : yq.tempo === '待解' ? '眼下还有月破或旬空,得先等条件解除'
100
+ : '缺月建,快慢暂不定';
101
+ const branches = yq.triggers.map((t) => t.branch).join('、');
102
+ const fu = yq.viaFushen ? '(用神是伏神,先要出得来)' : '';
103
+ return `应期上${when};传统会留意${branches}这几个地支${fu}——这只是提示,不换算成具体哪天。`;
104
+ }
105
+
93
106
  // 全卦逐爻旺衰 → 一句白话:整张卦的气偏在哪一头(与用神那一句分开说)
94
107
  function hexStrengthPlain(hs) {
95
108
  if (!hs || !hs.counts) return '';
@@ -200,7 +213,7 @@ export function buildAnswer(r, ask, ctx = {}) {
200
213
  head + `先说结论:本卦是${s(ben.name, '—')}` +
201
214
  `${ben.palace ? `(${ben.palace})` : ''}` +
202
215
  `${moving.length ? `,第 ${moving.join('、')} 爻动,变为${s(bian.name, '—')}` : ',六爻安静没有动爻'}。` +
203
- `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
216
+ `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${yingqiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
204
217
  '断卦的落点在「世爻」(代表你)和「动爻」(事情的推动点),下面「看盘顺序」就按这个次序展开。',
205
218
  ].join('');
206
219
  }
@@ -226,6 +239,9 @@ export function buildPlain(r, ask, ctx = {}) {
226
239
  const ys = (r.frame && r.frame.yongshen) || {};
227
240
  const favor = join(ys.favor);
228
241
  const avoid = join(ys.avoid);
242
+ const dis = Array.isArray(ys.disputed) && ys.disputed.length
243
+ ? `有一点要说明:${ys.disputed.map((d) => d.element).join('、')}在扶抑里算${ys.disputed.map((d) => d.favorRole).join('、')}(能${ys.disputed.map((d) => d.favorVerb).join('、')}),按调候看又偏忌(${ys.disputed.map((d) => `${d.climate}盘忌${d.element}`).join('、')}),两派说法不一;实际用在什么时候,看季节和环境取舍,不必当成绝对的好或坏。`
244
+ : '';
229
245
  const strength = STRENGTH_PLAIN[st.level] || '力量偏中性';
230
246
  const tiaohou = ys.tiaohou && ys.tiaohou.note ? `另外,${ys.tiaohou.note},所以「暖一点、亮一点」的环境通常更舒服。` : '';
231
247
  const sy = (r.liunian || []).find((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
@@ -234,7 +250,7 @@ export function buildPlain(r, ask, ctx = {}) {
234
250
  : '';
235
251
  return `你的命局属于「${s(st.level, '中性')}」:${strength}。` +
236
252
  `${favor ? `能补${favor}的环境(行业、方位、作息、相处的人)会让你省力;${avoid ? `而${avoid}偏重的场合容易空耗,能绕就绕。` : ''}` : ''}` +
237
- tiaohou + syPlain;
253
+ dis + tiaohou + syPlain;
238
254
  }
239
255
  if (r.system === 'ziwei') {
240
256
  const body = r.body || {};
@@ -268,7 +284,7 @@ export function buildPlain(r, ask, ctx = {}) {
268
284
  const plainBody = bodyPlainOf(labels);
269
285
  return `${s(ben.name, '这一卦')}讲的是一个「${moving.length ? '正在动、还会变' : '暂时不动、先看清'}」的局面——` +
270
286
  `${moving.length ? '有动爻,说明事情还有转圜空间,不是死局。' : '六爻安静,说明当下更该观察而不是硬推。'}` +
271
- `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
287
+ `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${yingqiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
272
288
  }
273
289
  return '';
274
290
  }
@@ -170,6 +170,7 @@ function baziInterpret(r) {
170
170
  const level = pick(st, 'level', '中和');
171
171
  const favor = Array.isArray(ys.favor) ? ys.favor : [];
172
172
  const avoid = Array.isArray(ys.avoid) ? ys.avoid : [];
173
+ const disputed = Array.isArray(ys.disputed) ? ys.disputed : [];
173
174
  const primary = pick(frame, 'primaryName', '未取定');
174
175
  const gender = pick(r.input, 'gender', '');
175
176
  const isMale = gender === 'male';
@@ -395,6 +396,21 @@ function baziInterpret(r) {
395
396
  `⑤ 落到维度:工作看喜用五行与十神权重;感情看配偶星与日支;健康看最旺与最弱的五行。五维度都只是这套推导的应用,不是另一套算法。`,
396
397
  ].join('\n'),
397
398
  },
399
+ ...(r.renyuan ? [(() => {
400
+ const ry = r.renyuan;
401
+ const table = ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' / ');
402
+ const nextSeg = ry.next ? `,第 ${ry.next.from} 日起交 ${ry.next.stem}${ry.next.element}` : '';
403
+ const outside = ry.inHidden ? '' : ` 表里的 ${ry.active} 不在本柱藏干表中(属余气口径),两表各照原样,不互相改写。`;
404
+ const beyond = ry.beyond ? ' 你生在第 31 日以后,已超出 30 日表,按本气算,不硬分摊。' : '';
405
+ const ryc = r.renyuanClassic || classicOf('smt-renyuan');
406
+ const src = ryc ? `\n原文:《${CLASSIC_BOOKS[ryc.book].name}·${ryc.chapter}》「${ryc.quote}」\n白话:${ryc.plain}` : '';
407
+ return {
408
+ key: 'renyuan', label: '人元司权',
409
+ text: `生于${ry.month}月第 ${ry.day} 日(从${ry.term ? ry.term.name : '节'}起算,节当日为第 1 日,日差按本盘所用时刻所在日计):这一日由 **${ry.active}${ry.activeElement}** 司权,第 ${ry.segment.from}–${ry.segment.to} 日属它${nextSeg}。\n`
410
+ + '月令这一个月里,余气、中气、本气轮值,不是整月同一个五行当值——所以「月令是什么」,还要看生在第几日。\n'
411
+ + `**口径**:本插件固定通行 30 日表(${table});另有 7/7/16、9/3/18 等分法,三命通会此本又作 5/5/20(见下原文),各家不一。分日只作解释层的一层细节,**不改取格**:取格仍按本气、透干与会支判定。${outside}${beyond}` + src,
412
+ };
413
+ })()] : []),
398
414
  {
399
415
  key: 'timeline', label: '时间窗',
400
416
  text: (() => {
@@ -415,6 +431,8 @@ function baziInterpret(r) {
415
431
  { key: 'advice', label: '专业建议', text: adviceText },
416
432
  (() => {
417
433
  const hits = (r.liunian || []).filter((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
434
+ const e = suiyunClassicOf(r);
435
+ const src = e ? `\n原文:《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》「${e.quote}」\n白话:${e.plain}` : '';
418
436
  return {
419
437
  key: 'suiyun', label: '岁运提示',
420
438
  text: hits.length
@@ -422,8 +440,8 @@ function baziInterpret(r) {
422
440
  const stem = (l.suiyun.dayun || []).map((d) => `第${d.step}步${d.ganzhi}`).join('、');
423
441
  return `${l.year} ${l.ganzhi}(${stem || '未到起运'}):${l.suiyun.signals.map((s) => s.detail).join(';')}`;
424
442
  }).join('\n') +
425
- '\n读法:岁运并临、运年反吟与流年伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。'
426
- : '近十年没有岁运并临、运年反吟或流年伏吟/反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
443
+ '\n读法:岁运并临、运年反吟、大运与流年的伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。大运与本命四柱的伏吟/反吟整步大运都在,会连续多年出现,不当成逐年加重。' + src
444
+ : '近十年没有岁运并临、运年反吟或大运/流年伏吟、反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
427
445
  };
428
446
  })(),
429
447
  {
@@ -460,7 +478,7 @@ function baziInterpret(r) {
460
478
  ...(cli ? [{
461
479
  key: 'climate', label: '寒暖燥湿',
462
480
  text: `${cli.narration}${cli.need
463
- ? `调候取${cli.need.stem}${cli.need.element},忌${cli.avoidElement};${cli.clash ? '与扶抑冲突,本盘以调候为先' : '与扶抑同向'}`
481
+ ? `调候取${cli.need.stem}${cli.need.element},忌${cli.avoidElement}${disputed.some((d) => d.element === cli.avoidElement) ? '(有争议:该行在扶抑中为喜,见用神节)' : ''};${cli.clash ? '与扶抑冲突,本盘以调候为先' : disputed.some((d) => d.element === cli.avoidElement) ? '调候所忌在扶抑里为喜,两说并存' : '与扶抑同向'}`
464
482
  : '气候平和,调候不作主取'}。`,
465
483
  }] : []),
466
484
  ...(ys.qt ? [{
@@ -1207,10 +1225,32 @@ function liuyaoInterpret(r, ask) {
1207
1225
  useText.push(`时令对用神:${head},日辰 ${strength.day ? `${strength.day.branch}(${strength.day.element})` : '—'}。${body.join(';')}。`);
1208
1226
  useText.push('月建看这一段时令站在哪一边,日辰看当天那股力量是扶还是克;两处同时看,只报状态,不折成分数。');
1209
1227
  }
1210
- useText.push('这一层只把用神、卦体与时令对起来,应期与成败还要看动爻变化与月建日辰的进退节奏,所以这里不折成分数、不断成败。');
1228
+ useText.push('这一层只把用神、卦体与时令对起来,不折成分数、不断成败;应期另在「应期线索」一节只给触发支与快慢。');
1211
1229
  sections.push({ key: 'yongshen', label: '用神落位与卦体', text: useText.join('\n') });
1212
1230
  }
1213
1231
 
1232
+ // 应期线索:只给触发支与快慢方向,不给日期
1233
+ {
1234
+ const yq = r.yingqi;
1235
+ if (yq && Array.isArray(yq.items) && yq.items.length) {
1236
+ const rows = yq.items.map((it) => {
1237
+ const flags = [
1238
+ it.monthState && it.monthState !== 'unknown' ? `月建${it.monthState}` : '',
1239
+ it.monthChong ? '月破' : '',
1240
+ it.kong ? '旬空' : '',
1241
+ ].filter(Boolean).join('、');
1242
+ return `${it.position}爻${it.branch}${it.element}${flags ? `(${flags})` : ''}:${it.tempoSay}`;
1243
+ });
1244
+ sections.push({
1245
+ key: 'yingqi',
1246
+ label: '应期线索',
1247
+ text: `应期快慢:${yq.tempoSay}。${rows.join(';')}。可留意的地支:${yq.triggers.map((t) => `${t.branch}(${t.labels.join('、')})`).join(';')}。` +
1248
+ `${yq.viaFushen ? '用神不上卦,这里的支取自伏神;伏神要先得出才算数。' : ''}` +
1249
+ '这一节只把传统的「逢值、逢冲、逢合」与旬空的出空、冲空摆出来,不换算成具体日期、不排先后、也不判忌神受制;近事以日计、远事以月计,用哪把尺取决于事情本身。',
1250
+ });
1251
+ }
1252
+ }
1253
+
1214
1254
  // 全卦逐爻旺衰:不挑用神,六爻都过一遍,看整张卦的气压在哪一头
1215
1255
  {
1216
1256
  const hs = r.hexStrength;
@@ -1240,7 +1280,7 @@ function liuyaoInterpret(r, ask) {
1240
1280
 
1241
1281
  sections.push({
1242
1282
  key: 'limits', label: '未纳入',
1243
- text: '本卦没有计算:用神两现时该取哪一爻的权重、伏神出伏的应期(出伏条件已判,应期仍未做)。化进/化退的力重只按月建五态给「得令/失令」这类档位,不折成加权分数,也不叠动变连环;这些都会改变"旺不旺"的判断,所以这里只给取象与方向。',
1283
+ text: '本卦没有计算:用神两现时该取哪一爻的权重、应期的具体日期(只给逢值/逢冲/逢合的触发支与快慢方向)、忌神受制的时点、动变连环。化进/化退的力重只按月建五态给「得令/失令」这类档位,不折成加权分数;这些都会改变"旺不旺"的判断,所以这里只给取象与方向。',
1244
1284
  });
1245
1285
 
1246
1286
  const dimensions = [
@@ -1666,7 +1706,7 @@ const BUILDERS = {
1666
1706
  import { interpretQimen } from './qimen-interpret.js';
1667
1707
  import { buildAnswer, buildPlain, buildGlossary, buildFollowups, dimensionPlain } from './interpret-plain.js';
1668
1708
  import { knowledgeFor, caveatsFor, STATUS_LABEL } from './interpret-knowledge.js';
1669
- import { CLASSIC_BOOKS } from './classics.js';
1709
+ import { CLASSIC_BOOKS, suiyunClassicOf, classicOf } from './classics.js';
1670
1710
 
1671
1711
  /**
1672
1712
  * 给任意体系的解析结果补上「白话四层」:直答 / 说人话 / 每维一句话 / 追问建议。