dsh-metaphysics 0.34.0 → 0.38.2

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/bazi.js CHANGED
@@ -294,9 +294,12 @@ export function buildBazi(input) {
294
294
  };
295
295
 
296
296
  const dayun = [];
297
+ const dayunWindows = [];
297
298
  for (let i = 1; i <= 8; i++) {
298
299
  const idx = (((monthIdx + (forward ? i : -i)) % 60) + 60) % 60;
299
300
  const g = ganzhiName(idx);
301
+ const fromJd = partsToJd(addYearsClamped(jiaoParts, (i - 1) * 10));
302
+ const toJd = partsToJd(addYearsClamped(jiaoParts, i * 10));
300
303
  dayun.push({
301
304
  step: i,
302
305
  startAge: startAgeYears + (i - 1) * 10,
@@ -306,9 +309,10 @@ export function buildBazi(input) {
306
309
  stemTenGod: tenGod(dayStem, g.stem),
307
310
  nayin: nayin(idx),
308
311
  // 这一步大运的起止交运时刻(与摘要/面板同源,可直接拿来对年表)
309
- fromDate: formatJd(partsToJd(addYearsClamped(jiaoParts, (i - 1) * 10))),
310
- toDate: formatJd(partsToJd(addYearsClamped(jiaoParts, i * 10))),
312
+ fromDate: formatJd(fromJd),
313
+ toDate: formatJd(toJd),
311
314
  });
315
+ dayunWindows.push({ step: i, ganzhi: g.name, fromJd, toJd });
312
316
  }
313
317
 
314
318
  // 流年(默认从「今年」起排 10 年;传 input.nowYear 可钉死基准年,用于复现历史结论)
@@ -461,6 +465,48 @@ export function buildBazi(input) {
461
465
  l.luck = scoreLuck(l.ganzhi, frame);
462
466
  }
463
467
 
468
+ // ── 岁运并临 / 伏吟反吟:只标干支层面的「变动信号」,不判吉凶 ──
469
+ // 流年按立春起算(该年立春到次年立春),当值大运取与这一区间相交的步;
470
+ // 交运落在这一年里时,前后两步都在本年当过值,两条都列出。
471
+ const NATAL_LABEL = { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' };
472
+ const stemOvercomes = (a, b) => OVERCOMES[STEM_ELEMENT[a]] === STEM_ELEMENT[b];
473
+ const tianKeDiChong = (a, b) => {
474
+ if (typeof a !== 'string' || typeof b !== 'string' || a.length < 2 || b.length < 2) return false;
475
+ if (!(a[0] in STEM_ELEMENT) || !(b[0] in STEM_ELEMENT)) return false;
476
+ return (stemOvercomes(a[0], b[0]) || stemOvercomes(b[0], a[0])) && hasPair(BRANCH_CHONG, a[1], b[1]);
477
+ };
478
+ const lichunJd = (y) => {
479
+ const t = termWindow(y, { jieOnly: true }).find((x) => x.index === 2 && jdToParts(x.jd).year === y);
480
+ return t ? t.jd : partsToJd({ year: y, month: 2, day: 4 });
481
+ };
482
+ for (const l of liunian) {
483
+ const yStart = lichunJd(l.year);
484
+ const yEnd = lichunJd(l.year + 1);
485
+ const active = dayunWindows.filter((w) => w.fromJd < yEnd && w.toJd > yStart);
486
+ const signals = [];
487
+ for (const w of active) {
488
+ if (w.ganzhi === l.ganzhi) {
489
+ signals.push({ type: '岁运并临', target: `第${w.step}步大运`, detail: `大运与流年同为${l.ganzhi}` });
490
+ } else if (tianKeDiChong(w.ganzhi, l.ganzhi)) {
491
+ signals.push({ type: '运年反吟', target: `第${w.step}步大运`, detail: `大运${w.ganzhi}与流年${l.ganzhi}天克地冲` });
492
+ }
493
+ }
494
+ for (const key of ['year', 'month', 'day', 'hour']) {
495
+ const g = pillars[key].ganzhi;
496
+ if (!g) continue;
497
+ if (l.ganzhi === g) {
498
+ signals.push({ type: '流年伏吟', target: NATAL_LABEL[key], detail: `流年${l.ganzhi}与${NATAL_LABEL[key]}同干支` });
499
+ } else if (tianKeDiChong(l.ganzhi, g)) {
500
+ signals.push({ type: '流年反吟', target: NATAL_LABEL[key], detail: `流年${l.ganzhi}与${NATAL_LABEL[key]}${g}天克地冲` });
501
+ }
502
+ }
503
+ l.suiyun = {
504
+ dayun: active.map((w) => ({ step: w.step, ganzhi: w.ganzhi })),
505
+ signals,
506
+ note: '只标干支重复与天克地冲这类变动信号,不判吉凶;大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列',
507
+ };
508
+ }
509
+
464
510
  const p = jdToParts(jd);
465
511
 
466
512
  // ── 胎元 / 命宫 / 身宫 ──
@@ -1,10 +1,11 @@
1
- // 古籍原文库:《子平真诠》《滴天髓》里与「格局取用」「十神喜忌」「寒暖燥湿」直接相关的条目。
2
- // 原文照录(公共版权:清·沈孝瞻《子平真诠》、明·京图《滴天髓》通行本),仅供文化研究与参考。
1
+ // 古籍原文库:《子平真诠》《滴天髓》《三命通会》里与「格局取用」「十神喜忌」「寒暖燥湿」直接相关的条目。
2
+ // 原文照录(公共版权:清·沈孝瞻《子平真诠》、明·京图《滴天髓》、明·万民英《三命通会》通行本),仅供文化研究与参考。
3
3
  // 出处以中华典藏网在线本为准(逐条给 url),异文处不合并、不改写;plain 是本站自撰白话,不是原文。
4
4
 
5
5
  export const CLASSIC_BOOKS = {
6
6
  ziping: { name: '子平真诠', author: '清·沈孝瞻', base: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/' },
7
7
  ditian: { name: '滴天髓', author: '明·京图(通行本)', base: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/' },
8
+ sanming: { name: '三命通会', author: '明·万民英', base: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/' },
8
9
  };
9
10
 
10
11
  export const CLASSICS = [
@@ -108,6 +109,26 @@ export const CLASSICS = [
108
109
  plain: '月令用神为主,但必须配气候参看;同样的格局,生在得时的气候里事半功倍,逆着气候就费劲。',
109
110
  url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53634.html',
110
111
  },
112
+ {
113
+ id: 'zp-chengbai',
114
+ book: 'ziping',
115
+ chapter: '九、论用神成败救应',
116
+ scene: '格局',
117
+ tags: [],
118
+ quote: '成中有败,必是带忌;败中有成,全凭救应。',
119
+ plain: '成格不等于一路顺:成中带忌(官逢财又逢伤、官被合之类)就打了折;败格也未必无救——印来解伤、合来清煞,败中还能转成。',
120
+ url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53629.html',
121
+ },
122
+ {
123
+ id: 'zp-bianhua',
124
+ book: 'ziping',
125
+ chapter: '十、论用神变化',
126
+ scene: '格局',
127
+ tags: [],
128
+ quote: '故若丁生亥月,本为正官,支全卯未,则化为印。己生申月,本属伤官。藏庚透壬,则化为财。凡此之类皆用神之变化也。',
129
+ plain: '用神本从月令取,但月令藏干不一:丁生亥月本为正官,支会卯未就化为印;己生申月本属伤官,庚藏壬透就化为财。起点相同,合化一变,取用也跟着变(原文只说变化之由,不断定某盘必化)。',
130
+ url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53630.html',
131
+ },
111
132
  {
112
133
  id: 'dt-hannuan',
113
134
  book: 'ditian',
@@ -198,6 +219,78 @@ export const CLASSICS = [
198
219
  plain: '看精神不能偏执一端,要在损益之间求其中。',
199
220
  url: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/153091.html',
200
221
  },
222
+ // —— 三命通会(明·万民英):同一组十神,子平真诠说「怎么用」,三命通会说「怎么取、怕什么」。
223
+ // 两书并列取用,按同一 tag 各取一条对照读,不合并、不裁定谁对。
224
+ {
225
+ id: 'smt-zhenguan',
226
+ book: 'sanming',
227
+ chapter: '卷五·论正官',
228
+ scene: '格局',
229
+ tags: ['正官'],
230
+ quote: '正官者,乃甲见辛、乙见庚之例。阴阳配合,相制有用,成其道也。故正官为六格之首,止许一位,多则不宜。',
231
+ plain: '正官取「克我而阴阳相配」,有制而能成用;六格以正官居首,只宜一位,多见就杂。',
232
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7582.html',
233
+ },
234
+ {
235
+ id: 'smt-zhengcai',
236
+ book: 'sanming',
237
+ chapter: '卷六·论正财',
238
+ scene: '格局',
239
+ tags: ['财'],
240
+ quote: '正财者,乃甲见己、乙见戊之例。受我克制,为我之妻,譬人娶妻,妻赍财嫁我,我必精神康强,而后可享用,若衰微不振,虽妻财丰厚,但能目视,终不得用。',
241
+ plain: '正财是我克制之物,古人以「娶妻带财」作比:自身精神康强才受用得起,身弱不振,财再厚也只是看着。',
242
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7645.html',
243
+ },
244
+ {
245
+ id: 'smt-yinshou',
246
+ book: 'sanming',
247
+ chapter: '卷六·论印绶',
248
+ scene: '格局',
249
+ tags: ['印'],
250
+ quote: '印绶者,乃五行生我之名。如甲乙在亥子月,丙丁在寅卯月之类,乃我气之源,为生气,为父母,能护我官星,使无伤克',
251
+ plain: '印是生我之气,像源头与父母:它能护住官星,使官不受伤克;反过来说,印重就怕财来破。',
252
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7655.html',
253
+ },
254
+ {
255
+ id: 'smt-shishen',
256
+ book: 'sanming',
257
+ chapter: '卷六·论食神',
258
+ scene: '格局',
259
+ tags: ['食神'],
260
+ quote: '凡命遇财煞之地,食神旺相,煞被食制,不敢为祸,财被食生,充裕不竭,故食神一名寿星,一名爵星,良有以也。',
261
+ plain: '食神旺,一头制煞、一头生财:煞被制住不敢为祸,财被生起不至枯竭;所以食神又叫寿星、爵星。',
262
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7663.html',
263
+ },
264
+ {
265
+ id: 'smt-shangguan',
266
+ book: 'sanming',
267
+ chapter: '卷六·论伤官',
268
+ scene: '格局',
269
+ tags: ['伤官'],
270
+ quote: '伤官虽凶,乃我所生,自家之物,伤尽则能生财,财旺则能生官,造化展转有情。',
271
+ plain: '伤官虽称凶,却是我自己生出来的:伤尽(柱中不见官星)反能生财,财旺再生官,辗转还是有情。',
272
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7662.html',
273
+ },
274
+ {
275
+ id: 'smt-yangren',
276
+ book: 'sanming',
277
+ chapter: '卷六·论阳刃',
278
+ scene: '格局',
279
+ tags: ['阳刃', '劫财'],
280
+ quote: '阳者,阴阳之阳,刃者,刀刃之刃,即禄前一位,言旺越其分,故险。',
281
+ plain: '阳刃指禄前一位,旺过了本分,所以险;甲丙戊庚壬五阳干才有刃,阴干见伤官同论。',
282
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7671.html',
283
+ },
284
+ {
285
+ id: 'smt-jianlu',
286
+ book: 'sanming',
287
+ chapter: '卷六·论建禄',
288
+ scene: '格局',
289
+ tags: ['建禄', '比肩', '月劫'],
290
+ quote: '凡命月令建禄,难招祖业,必主平生见财不聚,却病少寿长,行运再见比肩,克妻妨父损子',
291
+ plain: '月令建禄是日主临官之地,身旺而财官难取:古说难承祖业、见财不聚,但病少寿长;再走比肩运,古书说克妻妨父损子(属于旧说的重话,本插件只记原文、不作断语)。',
292
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7672.html',
293
+ },
201
294
  ];
202
295
 
203
296
  const BY_ID = CLASSICS.reduce((m, e) => { m[e.id] = e; return m; }, {});
@@ -229,8 +322,19 @@ function tagOf(gejuName) {
229
322
  return GEJU_KEY[bare + '格'] || bare;
230
323
  }
231
324
 
232
- // 根据一张八字结果挑原文:先格局(格局条),再调候(寒暖/燥湿),最后用神通则;去重、限量。
233
- export function classicsFor(r, limit = 4) {
325
+ // 同一 tag 各书取一条:子平真诠在前(讲怎么用),三命通会紧随作对照(讲怎么取、怕什么)
326
+ function pick(tag, book) {
327
+ return CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag) && (!book || e.book === book)) || null;
328
+ }
329
+
330
+ // 章节名去掉卷次/序号与开头的「论」,避免 why 里拼成「论论财」
331
+ function chapterTitle(e) {
332
+ return e.chapter.replace(/^.*[、·]/, '').replace(/^论/, '');
333
+ }
334
+
335
+ // 根据一张八字结果挑原文:先格局(子平真诠 + 三命通会对照),再调候(寒暖/燥湿),
336
+ // 最后「用神总纲」一位按盘取:有化气格取论用神变化、见组合取论用神成败救应、其余取通用总纲;去重、限量。
337
+ export function classicsFor(r, limit = 5) {
234
338
  if (!r) return [];
235
339
  const out = [];
236
340
  const seen = new Set();
@@ -243,8 +347,10 @@ export function classicsFor(r, limit = 4) {
243
347
  for (const g of gejuList) {
244
348
  if (!g || (g.kind && g.kind !== '月令格')) continue;
245
349
  const tag = tagOf(g.name);
246
- const hit = CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag));
247
- if (hit) push(hit, `你的月令格局是「${g.name}」,故取${CLASSIC_BOOKS[hit.book].name}论${hit.chapter.replace(/^.*、/, '')}`);
350
+ const primary = pick(tag, 'ziping') || pick(tag);
351
+ if (primary) push(primary, `你的月令格局是「${g.name}」,故取${CLASSIC_BOOKS[primary.book].name}「${chapterTitle(primary)}」一条`);
352
+ const alt = CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag) && (!primary || e.book !== primary.book));
353
+ if (alt) push(alt, `同是「${g.name}」,${CLASSIC_BOOKS[alt.book].name}「${chapterTitle(alt)}」另有取法,可对照读`);
248
354
  }
249
355
  const cli = r.climate || (r.frame && r.frame.yongshen && (r.frame.yongshen.climate || r.frame.yongshen.tiaohou));
250
356
  if (cli && cli.kind && cli.kind !== '平和') {
@@ -254,7 +360,17 @@ export function classicsFor(r, limit = 4) {
254
360
  push(classicOf('zp-qihou'), '气候与月令用神要互参,故取子平真诠「用神配气候」一条');
255
361
  if (hot) push(classicOf('dt-hannuan'), '本盘偏热,寒暖一条正对');
256
362
  }
257
- push(classicOf('zp-yongshen'), '用神从月令取、四吉顺用四凶逆用,是通用总纲');
363
+ // 用神总纲位只放一条:同一个格,化气看「变化」、有组合看「成败救应」,都没有才回到四吉四凶总纲。
364
+ // 不另加位次,是因为格局两条 + 气候两条正好占满 5 条上限;总纲与具体结构二选一,避免泛论挤掉针对这张盘的话。
365
+ const huagi = gejuList.find((g) => g && g.kind === '化气格');
366
+ const combo = gejuList.find((g) => g && g.kind === '组合');
367
+ if (huagi) {
368
+ push(classicOf('zp-bianhua'), `本盘列有「${huagi.name}」:用神由月令合化而变,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神变化`);
369
+ } else if (combo) {
370
+ push(classicOf('zp-chengbai'), `本盘另有「${combo.name}」组合:成格还要看带忌与救应,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神成败救应`);
371
+ } else {
372
+ push(classicOf('zp-yongshen'), '用神从月令取、四吉顺用四凶逆用,是通用总纲');
373
+ }
258
374
  return out;
259
375
  }
260
376
 
@@ -170,4 +170,62 @@ export function followup(r, question) {
170
170
  return { question: q, ...result };
171
171
  }
172
172
 
173
+ /** 体系显示名:追问提示里用,避免把 bazi 这类内部键直接给用户看。 */
174
+ const SYSTEM_LABEL = {
175
+ bazi: '八字(四柱)',
176
+ ziwei: '紫微斗数',
177
+ liuyao: '六爻',
178
+ meihua: '梅花易数',
179
+ daliuren: '大六壬',
180
+ qimen: '奇门遁甲',
181
+ tarot: '塔罗',
182
+ hepan: '合盘',
183
+ chenggu: '称骨',
184
+ almanac: '黄历',
185
+ zeri: '择日',
186
+ star: '星座',
187
+ numerology: '生命数字',
188
+ mbti: '十六型人格',
189
+ trend: '人生 K 线',
190
+ fengshui: '风水',
191
+ name: '起名',
192
+ convert: '公农历互转',
193
+ };
194
+
195
+ /**
196
+ * 把「就这一盘继续问」整理成一段可以发到对话窗口的提问。
197
+ *
198
+ * 这里只做文本组装,不调用模型、不改盘:面板里点「问进对话」时,
199
+ * 客户端把这段文字写进对话输入框并提交,由对话里的模型基于盘面回答。
200
+ *
201
+ * @param {object} r compute 的结果(至少要带 system 与 summary)
202
+ * @param {{question?:string, maxChars?:number}} [opts]
203
+ * @returns {{text:string, question:string, system:string, label:string, chars:number, truncated:boolean}}
204
+ */
205
+ export function askPrompt(r, opts = {}) {
206
+ const label = SYSTEM_LABEL[r && r.system] || (r && r.system) || '未知体系';
207
+ const asked = String((opts && opts.question) || '').trim();
208
+ const question = asked || '请先总览这张盘,指出最值得注意的一两处,并说明各自的依据。';
209
+ const limit = Number.isFinite(opts && opts.maxChars) && opts.maxChars >= 400 ? opts.maxChars : 3000;
210
+ const raw = String((r && r.summary) || '').trim();
211
+ const truncated = raw.length > limit;
212
+ const summary = truncated ? `${raw.slice(0, limit)}…(摘要已截断)` : raw;
213
+
214
+ const lines = [
215
+ '【命理追问】下面这张盘是命理插件刚排好的,请就着它回答我的问题;不用重新排盘,盘面字段已经够用。',
216
+ `体系:${label}`,
217
+ `所问:${question}`,
218
+ '',
219
+ '——盘面摘要——',
220
+ summary || '(这张盘没有摘要字段,请先让我重新排一次。)',
221
+ '',
222
+ '——回答要求——',
223
+ '1) 尽量引用到具体的柱、宫或爻,再说判断,别只给结论;',
224
+ '2) 盘面里没有依据的地方直接说「这盘里看不出来」,不要补外部假设;',
225
+ '3) 用概率和倾向的说法,别给绝对化的断言,也别把传统说法当成事实。',
226
+ ];
227
+ const text = lines.join('\n');
228
+ return { text, question, system: (r && r.system) || '', label, chars: text.length, truncated };
229
+ }
230
+
173
231
  export default followup;
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 } from './liuyao.js';
3
+ import { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, hexagramStrength, jinTuiPower } from './liuyao.js';
4
4
  import { buildZiwei, BRIGHTNESS, flowOf, asOfParts } from './ziwei.js';
5
5
  import { drawTarot } from './tarot.js';
6
6
  import { buildMeihua } from './meihua.js';
@@ -12,7 +12,7 @@ import { geocodeAddress, resolveAddress } from './geo.js';
12
12
  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
- import { followup } from './followup.js';
15
+ import { followup, askPrompt } from './followup.js';
16
16
  import { crossCheck, crossText } from './crosscheck.js';
17
17
  import { pastSignals, verifyFit } from './verify.js';
18
18
  import { rectifyBirth } from './rectify.js';
@@ -136,6 +136,13 @@ function baziText(r) {
136
136
  `大运:${r.dayun.rule}(${r.dayun.direction}),${r.dayun.startAge.years}岁${r.dayun.startAge.months}个月起运${jiao}`,
137
137
  ` ${dayun}`,
138
138
  `流年:${r.liunian.slice(0, 10).map((l) => `${l.year}${l.ganzhi}(${l.stemTenGod})${l.luck ? `·${l.luck.tier}` : ''}`).join(' ')}`,
139
+ (() => {
140
+ const hits = r.liunian.filter((l) => l.suiyun && l.suiyun.signals && l.suiyun.signals.length);
141
+ if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或流年伏吟/反吟(只标干支,不判吉凶)';
142
+ const label = (s) => `${s.type}${s.type.startsWith('流年') && s.target ? `(${s.target})` : ''}`;
143
+ return '岁运提示(只标干支变动,不判吉凶):' + hits
144
+ .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';');
145
+ })(),
139
146
  '(格局、用神、行运吉凶为扶抑+简化调候的量化提示,非断语。)',
140
147
  ].join('\n') + near;
141
148
  }
@@ -174,6 +181,18 @@ function ziweiText(r) {
174
181
  const text = ['禄', '权', '科', '忌'].map(one).filter(Boolean).join(' ');
175
182
  return text ? `【命宫飞星】${text}` : '';
176
183
  })();
184
+ // 三方四正一起飞:四宫的 16 条飞化按落宫归堆,只看会聚
185
+ const flyCompositeLine = (() => {
186
+ const fc = r.flyComposite;
187
+ if (!fc || !Array.isArray(fc.sources) || !fc.sources.length) return '';
188
+ const groups = fc.labels.length ? fc.labels.join(' · ') : '无同宫会聚';
189
+ const detail = ['禄', '权', '科', '忌'].map((t) => {
190
+ const list = ((fc.byType && fc.byType[t]) || []).filter((x) => !x.self);
191
+ const tos = [...new Set(list.map((x) => String(x.to).replace(/宫$/, '')))];
192
+ return tos.length ? `${t}→${tos.join('/')}` : '';
193
+ }).filter(Boolean).join(' ');
194
+ return `【三方四正飞化】${fc.sanfang.join('·')}:${groups}${detail ? ` ${detail}` : ''}`;
195
+ })();
177
196
  return [
178
197
  `【农历】${lunarText}(月首 ${r.lunar.monthStart}) 时支:${r.hourBranch} 年干支:${r.yearGanzhi}`,
179
198
  `【时刻】${r.solarTime ? `${r.solarTime.civilText} → 真太阳时 ${r.solarTime.trueSolarText}${r.input.geo ? '(' + r.input.geo.note + ')' : ''}` : ''}`,
@@ -183,6 +202,7 @@ function ziweiText(r) {
183
202
  `【昌曲】文昌${r.changqu.wenchang.branch}(${r.changqu.wenchang.palace}${r.changqu.wenchang.mutagen ? '·化' + r.changqu.wenchang.mutagen : ''}) 文曲${r.changqu.wenqu.branch}(${r.changqu.wenqu.palace}${r.changqu.wenqu.mutagen ? '·化' + r.changqu.wenqu.mutagen : ''})`,
184
203
  `   ${r.changqu.relation} 入命三方:${r.changqu.inSoulSanfang ? '是' : '否'} 吸收面:${r.changqu.absorb.label}(${r.changqu.absorb.reason})`,
185
204
  flyLine,
205
+ flyCompositeLine,
186
206
  '十二宫:',
187
207
  ...rows,
188
208
  '大限(按起运年龄排序,前 8 限,宫干四化落宫):',
@@ -190,7 +210,7 @@ function ziweiText(r) {
190
210
  '流年(前 6 年,完整 12 年在结构化数据里):',
191
211
  ...liunian,
192
212
  `【格局】${patterns}`,
193
- r.patternSummary ? `【结构统计】吉格 ${r.patternSummary.good.length}、忌格 ${r.patternSummary.bad.length};${r.patternSummary.note}` : '',
213
+ r.patternSummary ? `【结构统计】吉格 ${r.patternSummary.good.length}、中性 ${r.patternSummary.neutral.length}、忌格 ${r.patternSummary.bad.length};${r.patternSummary.note}` : '',
194
214
  r.lunar.note ? `注:${r.lunar.note}` : '',
195
215
  ].filter(Boolean).join('\n');
196
216
  }
@@ -338,6 +358,19 @@ function liuyaoText(r) {
338
358
  } else {
339
359
  out.push('用神:未取(未指明所问之事;用「询问方向」指定后即按六亲取用)');
340
360
  }
361
+ // 全卦逐爻旺衰:不挑用神,六爻都给状态,世应、动爻、旬空另标
362
+ const hexSt = r.hexStrength;
363
+ if (hexSt && Array.isArray(hexSt.labels) && hexSt.labels.length) {
364
+ const m = hexSt.month && hexSt.month.branch
365
+ ? `月建 ${hexSt.month.branch}(${hexSt.month.element}) `
366
+ : '无月建(未给起卦时间) ';
367
+ out.push(`全卦旺衰:${m}${hexSt.labels.join(' · ')}`);
368
+ }
369
+ // 化进/化退的力重:单列一行,免得跟卦体标签混着读
370
+ const jt = r.jinTuiPower;
371
+ if (jt && Array.isArray(jt.lines) && jt.lines.length) {
372
+ out.push(`进退力重:${jt.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type}·${l.verdict}`).join(' · ')}`);
373
+ }
341
374
  return out.filter(Boolean).join('\n');
342
375
  }
343
376
 
@@ -649,18 +682,24 @@ function computeRaw(args = {}) {
649
682
  }
650
683
  // 用神挂在结果上:摘要、面板、解析三层读同一份,避免各自取一次走散
651
684
  const ug = yongshenOf(result, { direction: args.direction, gender: args.gender });
652
- if (ug.liuqin) {
653
- result.useGod = ug;
654
- // 月建按节气换月、与八字同源:从同一个 snapshot 取月支,取不到就只按日辰算
655
- let monthBranch = null;
656
- if (stamp) {
657
- try {
658
- const snap = buildSnapshot({ year: stamp.year, month: stamp.month, day: stamp.day, hour: stamp.hour, minute: stamp.minute });
659
- monthBranch = (snap.monthGanZhi || '')[1] || null;
660
- } catch {
661
- monthBranch = null;
662
- }
685
+ if (ug.liuqin) result.useGod = ug;
686
+ // 月建按节气换月、与八字同源:从同一个 snapshot 取月支,取不到就只按日辰算
687
+ let monthBranch = null;
688
+ if (stamp) {
689
+ try {
690
+ const snap = buildSnapshot({ year: stamp.year, month: stamp.month, day: stamp.day, hour: stamp.hour, minute: stamp.minute });
691
+ monthBranch = (snap.monthGanZhi || '')[1] || null;
692
+ } catch {
693
+ monthBranch = null;
663
694
  }
695
+ }
696
+ // 全卦逐爻旺衰不依赖用神:没指定所问之事也要能看整张卦的气
697
+ const hexStrength = hexagramStrength(result, { monthBranch, dayBranch: result.dayGanZhi.branch });
698
+ if (hexStrength) result.hexStrength = hexStrength;
699
+ // 化进/化退的力重:与旺衰同一套五态/五关系,只说走不走得动
700
+ const jtPower = jinTuiPower(result, { monthBranch, dayBranch: result.dayGanZhi.branch });
701
+ if (jtPower) result.jinTuiPower = jtPower;
702
+ if (ug.liuqin) {
664
703
  const strength = yongshenStrength(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
665
704
  if (strength) result.useGodStrength = strength;
666
705
  }
@@ -1067,7 +1106,7 @@ export function renderText(result) {
1067
1106
  export { geocodeAddress, resolveAddress };
1068
1107
 
1069
1108
  export { buildBazi, buildLiuyao, tossLines };
1070
- export { followup };
1109
+ export { followup, askPrompt };
1071
1110
  export { pastSignals, verifyFit, rectifyBirth };
1072
1111
  export { crossCheck, crossText };
1073
1112
  export { buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert };
@@ -66,10 +66,10 @@ export const KNOWLEDGE = [
66
66
  id: 'bazi-suiyun',
67
67
  system: 'bazi', dims: ['overall', 'career'],
68
68
  gist: '大运管十年基调,流年管当年触发。',
69
- detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(大运与流年干支相同)与伏吟反吟(干支重复或与原局对冲)是传统上标记「变动较大」的年份,属于提示而非断语。',
69
+ detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。',
70
70
  source: '岁运并临、伏吟反吟见《三命通会》《渊海子平》',
71
71
  status: 'variant',
72
- note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶。',
72
+ note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与原局之间的伏吟反吟未逐年标注,四柱十神层面的引化也未叠入。',
73
73
  },
74
74
  {
75
75
  id: 'bazi-liuqin',
@@ -98,6 +98,34 @@ export const KNOWLEDGE = [
98
98
  status: 'settled',
99
99
  },
100
100
 
101
+ {
102
+ id: 'bazi-sanming-duizhao',
103
+ system: 'bazi', dims: ['overall', 'career'],
104
+ gist: '同一个格,子平真诠讲怎么用,三命通会讲怎么取。',
105
+ detail: '两书不是谁替代谁:子平真诠以月令取用神、论顺逆成败,重「用」;三命通会按十神逐格立论,重「取」与「忌」。同看正官格,前者问财印护官、伤煞混杂,后者先说「止许一位,多则不宜」。本插件把两条并列,不合并成一句断语。',
106
+ source: '《子平真诠》论正官等篇;《三命通会》卷五、卷六论十神',
107
+ status: 'variant',
108
+ note: '两书成书年代与流派不同,用语有出入;本插件照录通行本,不做校勘改字,也不替两家调和。',
109
+ },
110
+ {
111
+ id: 'bazi-geju-chengbai',
112
+ system: 'bazi', dims: ['overall', 'career'],
113
+ gist: '格有成有败:成中带忌会打折,败中有救还能转成。',
114
+ detail: '月令取到格只是起点。成格要看有没有带忌——官逢财又逢伤、官透而被合、财旺生官又逢伤,都会把成格打折;败格也看有没有救应——印来解伤、合来清煞、劫来解财,败中可转。所以判格不能只报一个格名。',
115
+ source: '《子平真诠》九、论用神成败救应',
116
+ status: 'variant',
117
+ note: '各家对成格条件与带忌轻重的排序不同(有先看刑冲、有先看合化),本插件只把原文与成败方向并列摆出,不逐盘判成破。',
118
+ },
119
+ {
120
+ id: 'bazi-huagi-bianhua',
121
+ system: 'bazi', dims: ['overall', 'career'],
122
+ gist: '月令用神会因合化而变:同一个起点,取用可以不同。',
123
+ detail: '用神本从月令取,但月令藏干不一,透出的字不同、支会合局不同,取用就跟着变:丁生亥月本为正官,支全卯未化为印;己生申月本属伤官,藏庚透壬化为财。所以看到化气格,要回头核对月令与合局,不能只按本气定格。',
124
+ source: '《子平真诠》十、论用神变化;化气条件各家另论',
125
+ status: 'variant',
126
+ note: '化气格成立条件各家宽严不一(是否要求月令为化神、日主是否允许有根),本插件只列条件、附原文,不据此断定化气已成。',
127
+ },
128
+
101
129
  // ── 紫微 ──────────────────────────────────────────────
102
130
  {
103
131
  id: 'ziwei-sanfang',
@@ -158,7 +186,16 @@ export const KNOWLEDGE = [
158
186
  detail: '生年四化是先天带的,飞星四化是宫位自己去找的:每一宫的宫干定出禄权科忌四颗星,那几颗星落在哪一宫,就是这一宫的力气往哪边使。所以同一个「化忌」,从命宫飞出去和从财帛飞出去,说的是两件事。',
159
187
  source: '《紫微斗数全书》四化篇;中州派宫干四化传法',
160
188
  status: 'variant',
161
- note: '飞星派(如钦天四化)把宫干四化当主线,三合派只作辅助;庚干四化各派另有异说,本插件取中州派常用表。',
189
+ note: '飞星派(如钦天四化)把宫干四化当主线,三合派只作辅助;庚干四化各派另有异说,本插件取中州派常用表。飞星串连与三方四正复合飞化现已给出,但未叠大限流年四化。',
190
+ },
191
+ {
192
+ id: 'ziwei-sanfang-fei',
193
+ system: 'ziwei', dims: ['overall', 'career'],
194
+ gist: '三方四正一起飞,看四股力会到哪一宫。',
195
+ detail: '单宫飞化只看本宫宫干把四化送去哪里;三方四正复合飞化把本宫、对宫与两个三合宫共四宫的宫干四化同时摊开,约 16 条,按落宫归堆,落到同一宫的就是会照。会在一处的化越多,那一宫被牵动的力道越集中;两忌以上会宫、禄忌同宫、三化以上会宫、宫干化出又落回本宫(自化),是这一层最常取的几种情形。',
196
+ source: '《紫微斗数全书》四化篇;飞星派三方四正同飞说',
197
+ status: 'variant',
198
+ note: '飞星派对「三方四正一起飞」是否成立、会照要不要折成分数,各家口径不一;本插件只做落宫归堆与会照标记,不折分数,也未叠大限流年四化。',
162
199
  },
163
200
  {
164
201
  id: 'ziwei-jichuanji',
@@ -213,6 +250,15 @@ export const KNOWLEDGE = [
213
250
  status: 'variant',
214
251
  note: '月破、暗动、冲空三者各家取法不完全一致(有派别把日冲动爻也叫暗动,有派别只认旺相之爻暗动);本插件按「月建冲=月破、日冲静爻=暗动、日冲旬空=冲空」这一套给标签,并注明用了哪一支月建。',
215
252
  },
253
+ {
254
+ id: 'liuyao-quanggu-shuai',
255
+ system: 'liuyao', dims: ['overall', 'work'],
256
+ gist: '全卦逐爻看月建,才看得出气聚在哪一头。',
257
+ detail: '月建管这一段时令的旺衰、日辰管当天的扶克,落到全部六爻逐爻判定:每爻都出月令五态(旺相休囚死)与日辰五关系,并标月破、暗动、冲空。世爻、应爻、动爻与旬空另标,五态分布能看出气是聚在世应还是散在旁爻——用神只是其中一处落点,不看全卦容易把一处当全局。',
258
+ source: '《增删卜易》月建章、日辰章;《卜筮正宗》六爻旺衰论',
259
+ status: 'variant',
260
+ note: '各家对月破、暗动、冲空的取法不完全一致,是否给六爻折力重(分数)分歧更大;本插件只给状态标签与五态分布,不折分数。',
261
+ },
216
262
  {
217
263
  id: 'liuyao-yingqi',
218
264
  system: 'liuyao', dims: ['overall', 'career'],
@@ -278,7 +324,7 @@ export const KNOWLEDGE = [
278
324
  id: 'liuyao-jintui-yong',
279
325
  system: 'liuyao', dims: ['overall', 'work'],
280
326
  gist: '化进化退要看是用神还是忌神,还要叠月建日辰。',
281
- detail: '进神退神本身只说明这一爻的向外趋势:用神化进为得力,忌神化进则为患加剧;用神化退是力衰,忌神化退反而是解脱。传统还要看它在本月令衰旺、有没有日辰生扶,空亡或受克时进者未必能进。所以本插件只标出化进/化退与爻位,力重与吉凶仍留给用神一节判断。',
327
+ detail: '进神退神本身只说明这一爻的向外趋势:用神化进为得力,忌神化进则为患加剧;用神化退是力衰,忌神化退反而是解脱。传统还要看它在本月令衰旺、有没有日辰生扶,空亡或受克时进者未必能进。本插件把进退与爻位标出来,再按月建五态给一档力重——得令则进得动、退得慢,失令则进得虚、退得实,月破压过一切;只给档位,不折成加权分数,吉凶仍落在用神与忌神那一层判。',
282
328
  source: '《增删卜易》进神退神章、用神章',
283
329
  status: 'variant',
284
330
  note: '土之进退次序各家说法不完全一致,有以丑→辰→未→戌为进、也有只取辰戌丑未相对者;本插件按丑辰未戌成环处理,并用《增删卜易》卦例中「未土化戌土为进」「戌土化未土为退」对拍。',
@@ -65,6 +65,43 @@ function useStrengthPlain(st) {
65
65
  return `${when}对这件事有帮有压,一半的力要自己挣——结果更看你怎么走。`;
66
66
  }
67
67
 
68
+ // 化进 / 化退的力重 → 一句白话:方向之外,时令托不托着
69
+ function jinTuiPlain(r) {
70
+ const jp = r && r.jinTuiPower;
71
+ if (!jp || !Array.isArray(jp.lines) || !jp.lines.length) return '';
72
+ const sayOf = (l) => (l.verdict === '后劲足' ? '时令托着,后劲足'
73
+ : l.verdict === '后劲有限' ? '时令不太托,后劲有限'
74
+ : l.verdict === '进得虚' ? '时令最背,进也是虚进'
75
+ : l.verdict === '退得缓' ? '时令还托着,退得慢'
76
+ : l.verdict === '退得实' ? '时令也不托,退得实'
77
+ : l.verdict === '月破失据' ? '又正好逢月破,进退都站不稳'
78
+ : '缺月建,先只当方向看');
79
+ return `动爻的进退还要看时令:${jp.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type},${sayOf(l)}`).join(';')}。`;
80
+ }
81
+
82
+ // 全卦逐爻旺衰 → 一句白话:整张卦的气偏在哪一头(与用神那一句分开说)
83
+ function hexStrengthPlain(hs) {
84
+ if (!hs || !hs.counts) return '';
85
+ const up = (hs.counts.旺 || 0) + (hs.counts.相 || 0);
86
+ const down = (hs.counts.囚 || 0) + (hs.counts.死 || 0);
87
+ const when = hs.month && hs.month.branch ? `按${hs.month.branch}月这段时令看` : '按起卦这天看';
88
+ const shi = hs.shi && hs.shi.monthState !== 'unknown' ? `代表你的世爻在${hs.shi.monthState}` : '';
89
+ if (up >= 4 && down === 0) return `${when},整张卦六爻的气多数是顺的,${shi},眼下算是好推的时候。`;
90
+ if (down >= 4 && up === 0) return `${when},整张卦六爻的气多数被压着,${shi},这几天更适合等而不是冲。`;
91
+ return `${when},整张卦六爻有顺有逆(旺相 ${up} 爻、囚死 ${down} 爻)${shi ? `,${shi}` : ''},成不成要看具体哪一爻在动。`;
92
+ }
93
+
94
+ // 三方四正复合飞化 → 一句白话:四股力有没有挤到同一处
95
+ function flyCompositePlain(fc) {
96
+ if (!fc || !Array.isArray(fc.targets)) return '';
97
+ const hub = fc.targets.find((t) => t.count >= 2);
98
+ const self = fc.groups && fc.groups.selfHua && fc.groups.selfHua.length
99
+ ? '其中有的宫自己化出去又落回自己,容易表现为「想得多、落得少」。'
100
+ : '';
101
+ if (!hub) return `把命宫连同对宫、两个三合宫一起看,四股力飞出去没有挤到同一处,这几件事之间牵连不深。${self}`;
102
+ return `把命宫连同对宫与两个三合宫一起看,${hub.incoming.length} 股力最后挤在「${String(hub.palace).replace(/宫$/, '')}」这件事上——这件事会把好几个方面的劲都吸过来。${self}`;
103
+ }
104
+
68
105
  /**
69
106
  * 六爻卦体 → 一句白话。
70
107
  * 标签可能带爻位(「化进神 5」),所以按前缀匹配;
@@ -135,6 +172,7 @@ export function buildAnswer(r, ask, ctx = {}) {
135
172
  `${bureau.name ? `、${bureau.name}` : ''}${md ? `,命主${md}` : ''}` +
136
173
  `${body.lands ? `,身宫落在「${body.lands}」——这张盘的重心在这件事上` : ''}。`,
137
174
  plainFly,
175
+ flyCompositePlain(r.flyComposite),
138
176
  `具体到你问的这件事,重点看${where}。`,
139
177
  ].filter(Boolean).join('');
140
178
  }
@@ -150,7 +188,7 @@ export function buildAnswer(r, ask, ctx = {}) {
150
188
  head + `先说结论:本卦是${s(ben.name, '—')}` +
151
189
  `${ben.palace ? `(${ben.palace})` : ''}` +
152
190
  `${moving.length ? `,第 ${moving.join('、')} 爻动,变为${s(bian.name, '—')}` : ',六爻安静没有动爻'}。` +
153
- `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}`,
191
+ `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
154
192
  '断卦的落点在「世爻」(代表你)和「动爻」(事情的推动点),下面「看盘顺序」就按这个次序展开。',
155
193
  ].join('');
156
194
  }
@@ -168,9 +206,13 @@ export function buildPlain(r, ask, ctx = {}) {
168
206
  const avoid = join(ys.avoid);
169
207
  const strength = STRENGTH_PLAIN[st.level] || '力量偏中性';
170
208
  const tiaohou = ys.tiaohou && ys.tiaohou.note ? `另外,${ys.tiaohou.note},所以「暖一点、亮一点」的环境通常更舒服。` : '';
209
+ const sy = (r.liunian || []).find((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
210
+ const syPlain = sy
211
+ ? `再往后看,${sy.year} 年${sy.ganzhi}是${sy.suiyun.signals.map((x) => x.type).join('、')}——传统上把它当作「变化感强一点」的提示,既不是好消息也不是坏消息,遇上时多留个心眼就好。`
212
+ : '';
171
213
  return `你的命局属于「${s(st.level, '中性')}」:${strength}。` +
172
214
  `${favor ? `能补${favor}的环境(行业、方位、作息、相处的人)会让你省力;${avoid ? `而${avoid}偏重的场合容易空耗,能绕就绕。` : ''}` : ''}` +
173
- tiaohou;
215
+ tiaohou + syPlain;
174
216
  }
175
217
  if (r.system === 'ziwei') {
176
218
  const soul = r.soul || {};
@@ -184,6 +226,7 @@ export function buildPlain(r, ask, ctx = {}) {
184
226
  `紫微看的是「你与环境怎么互动」。命宫落${s(soul.branch, '—')}宫,说明你做事的第一反应是从这个宫位的气质出发` +
185
227
  `${body.lands ? `;身宫在「${body.lands}」,意味着真正让你上心的,是${body.lands}这件事` : ''}。`,
186
228
  plainFly,
229
+ flyCompositePlain(r.flyComposite),
187
230
  ].filter(Boolean).join('');
188
231
  }
189
232
  if (r.system === 'liuyao') {
@@ -193,7 +236,7 @@ export function buildPlain(r, ask, ctx = {}) {
193
236
  const plainBody = bodyPlainOf(labels);
194
237
  return `${s(ben.name, '这一卦')}讲的是一个「${moving.length ? '正在动、还会变' : '暂时不动、先看清'}」的局面——` +
195
238
  `${moving.length ? '有动爻,说明事情还有转圜空间,不是死局。' : '六爻安静,说明当下更该观察而不是硬推。'}` +
196
- `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}`;
239
+ `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
197
240
  }
198
241
  return '';
199
242
  }