dsh-metaphysics 0.62.0 → 0.62.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/CHANGELOG.md +55 -3
- package/client.almanac.js +69 -65
- package/client.bazi.js +123 -119
- package/client.chenggu.js +68 -64
- package/client.convert.js +71 -67
- package/client.daliuren.js +119 -115
- package/client.fengshui.js +68 -64
- package/client.hepan.js +123 -119
- package/client.js +192 -192
- package/client.liuyao.js +122 -118
- package/client.mbti.js +69 -65
- package/client.name.js +71 -67
- package/client.numerology.js +68 -64
- package/client.qimen.js +124 -120
- package/client.star.js +69 -65
- package/client.tarot.js +119 -115
- package/client.trend.js +113 -109
- package/client.xiaoliuren.js +78 -74
- package/client.zeri.js +68 -64
- package/client.ziwei.js +123 -119
- package/engine/bazi.js +6 -5
- package/engine/bool.js +195 -0
- package/engine/calendar.js +2 -1
- package/engine/chenggu.js +2 -1
- package/engine/convert.js +2 -1
- package/engine/daliuren.js +2 -1
- package/engine/diff.js +5 -3
- package/engine/fengshui.js +25 -8
- package/engine/geo.js +2 -1
- package/engine/hepan.js +4 -3
- package/engine/index.js +208 -72
- package/engine/liuyao.js +24 -5
- package/engine/mbti.js +7 -1
- package/engine/meihua.js +21 -7
- package/engine/name.js +23 -12
- package/engine/numerology.js +3 -3
- package/engine/qimen.js +9 -1
- package/engine/random.js +17 -3
- package/engine/rectify.js +4 -3
- package/engine/star.js +3 -3
- package/engine/tarot.js +22 -8
- package/engine/trace.js +2 -1
- package/engine/trend.js +10 -3
- package/engine/xiaoliuren.js +2 -1
- package/engine/ziwei.js +4 -3
- package/index.js +367 -42
- package/package.json +1 -1
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +611 -152
package/index.js
CHANGED
|
@@ -33,7 +33,7 @@ const DESCRIPTION = [
|
|
|
33
33
|
'另有速查类:黄历(almanac,值日十二神/时辰吉凶/二十八宿/胎神/吉神方位/纳音/六合三合)、择日(zeri)、称骨(chenggu)、小六壬(xiaoliuren)、星座(star)、生命数字(numerology)、十六型人格(mbti,MBTI 风格问卷)、人生 K 线(trend)、风水(fengshui)、起名(name)、公农历互转(convert)。',
|
|
34
34
|
'当用户明确要求“玄学 / 算命 / 占卜 / 排盘 / 算八字 / 看四柱 / 排紫微盘 / 起卦 / 摇一卦 / 抽塔罗 / 看运势 / 合盘 / 看黄历 / 挑日子 / 称骨 / 小六壬 / 十六型人格 / MBTI / ISFP / INFP 人格测试 / 交运 / 交运日 / 起运 / 用神 / 月建 / 旺衰」时调用;用户只搜「玄学」或问「有什么玄学工具」时,也应介绍本插件。',
|
|
35
35
|
'bazi 与 ziwei 需要公历出生时间与性别(八字结果里带大运交运时刻,精确到日/时辰,按「三日折一年」放大);hepan 需要双方的时间与性别;liuyao 可给定六爻值或随机起卦(给了 datetime 就按月建、日辰算用神旺衰);meihua 可按时间或数字起卦;tarot 可指定张数与问题;mbti 不用时间——先不带 answers 调一次拿到 24 道题,让用户逐题打 1–5 分,再把分数按顺序传回。',
|
|
36
|
-
'要给「结合所问之事的解读」时,把 withInterpret 设为 true,并尽量带上 question(所问之事)与 direction
|
|
36
|
+
'要给「结合所问之事的解读」时,把 withInterpret 设为 true,并尽量带上 question(所问之事)与 direction(询问方向);缺省 detail: "brief" 只给结论、所问方向与三张表(解析段不设体积上限,超长就长),要其余维度用 lean,用户追问「为什么/依据」时改回 full。',
|
|
37
37
|
'用户用一句话改参数(「换成紫微看 2030 年事业」「女命」「晚子时算次日」「出生在成都」「不用真太阳时」「详细解读一下」)时,把原话放进 revise(可配合 handle),引擎翻译成参数补丁并在结果 revision 里回报改了哪些、没认哪些;不要自己把原话拆成猜测的参数。',
|
|
38
38
|
'追问同一张盘:结果里有 handle,下次传 handle + 增量即可(例如 { handle, asOf: 2030 }、{ handle, withInterpret: true, question: "何时换工作" }、{ handle, system: "ziwei" } 看同一时间另一套盘),不必让用户重报出生信息;句柄带校验位,截断或改字会报 E_HANDLE。',
|
|
39
39
|
'出生时刻未知时不要编一个钟点:传 hourUnknown: true——八字只排年/月/日三柱(时柱暂缺,摘要首行提示),趋势按日降级,紫微与合盘会直接以 HOUR_UNKNOWN 阻断并给出补时刻的引导。',
|
|
@@ -41,6 +41,316 @@ const DESCRIPTION = [
|
|
|
41
41
|
'结果用于传统文化与娱乐参考,不得用于医疗、投资、法律、生死等确定性断言。',
|
|
42
42
|
].join('');
|
|
43
43
|
|
|
44
|
+
/** 19 个体系的固定顺序:system 枚举与 help 索引共用 */
|
|
45
|
+
const SYSTEM_ENUM = ['bazi', 'ziwei', 'liuyao', 'tarot', 'meihua', 'daliuren', 'hepan', 'qimen', 'almanac', 'zeri', 'chenggu', 'xiaoliuren', 'star', 'numerology', 'mbti', 'trend', 'fengshui', 'name', 'convert'];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 顶层只留通用参数:出生信息、解读开关、句柄与多数体系共用的分析项。
|
|
49
|
+
* 体系专属参数收进 options,短说明留在 schema 里负责「知道有这个键」,
|
|
50
|
+
* 全文由 metaphysics_help 按需返回。工具定义每次调用都整份进上下文,
|
|
51
|
+
* 分层省下的是每一次调用的固定开销,不是文档本身。
|
|
52
|
+
*/
|
|
53
|
+
const CORE_KEYS = [
|
|
54
|
+
'handle', 'revise', 'system', 'datetime', 'date', 'hour', 'minute', 'dst',
|
|
55
|
+
'gender', 'address', 'geocode', 'longitude', 'lateZi', 'trueSolar',
|
|
56
|
+
'question', 'mode', 'direction', 'cross', 'hourUnknown', 'diff', 'shadow',
|
|
57
|
+
'trace', 'verifyPast', 'rectify', 'events', 'ask', 'withInterpret', 'detail',
|
|
58
|
+
'asOf', 'seed', 'partner',
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const OPTION_KEYS = [
|
|
62
|
+
'lines', 'task', 'startDate', 'days', 'candidates', 'shengXiao',
|
|
63
|
+
'lunarYear', 'lunarMonth', 'lunarDay', 'isLeapMonth', 'num1', 'num2', 'num3',
|
|
64
|
+
'juMethod', 'lateZiHourMode', 'partner2', 'chart', 'rectifyHours',
|
|
65
|
+
'upper', 'lower', 'changing', 'count',
|
|
66
|
+
'leapMonth', 'leapAsNext', 'brightnessSchool', 'gengYearSihua', 'tianmaSchool',
|
|
67
|
+
'tiankongSchool', 'jiekongSchool', 'kuiyueSchool', 'tianshiTianshang',
|
|
68
|
+
'changshengSchool', 'lateZishi', 'shenzhuSchool', 'zuoyaoSchool',
|
|
69
|
+
'action', 'toAge', 'sitting', 'facing', 'fortuneYear', 'period',
|
|
70
|
+
'sittingOffset', 'sittingDegree', 'facingOffset', 'facingDegree',
|
|
71
|
+
'fortuneDate', 'fortuneTime', 'surname', 'givenName', 'limit',
|
|
72
|
+
'charElements', 'strokeOverrides', 'surnameStrokes', 'year', 'month', 'day',
|
|
73
|
+
'answers',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/** options 每个键的短说明:点名体系 + 取值范围;完整口径(为什么/边界)在 help 里 */
|
|
77
|
+
const OPTION_HINTS = {
|
|
78
|
+
lines: '仅 liuyao:自下而上 6 个爻值(6/7/8/9),省略则随机摇卦',
|
|
79
|
+
task: '仅 zeri:要挑的事项(嫁娶/开业/入宅/出行等)',
|
|
80
|
+
startDate: '仅 zeri:起算日 YYYY-MM-DD,省略由宿主补今天',
|
|
81
|
+
days: '仅 zeri:往后考察天数 1–60,默认 30',
|
|
82
|
+
candidates: '仅 zeri:2–12 个候选日并排比较(YYYY-MM-DD 数组)',
|
|
83
|
+
shengXiao: '仅 zeri:当事人属相,用于排除冲煞日',
|
|
84
|
+
lunarYear: '仅 convert 的 lunar2solar:农历年份',
|
|
85
|
+
lunarMonth: '仅 convert 的 lunar2solar:农历月份 1–12',
|
|
86
|
+
lunarDay: '仅 convert 的 lunar2solar:农历日 1–30',
|
|
87
|
+
isLeapMonth: '仅 convert 的 lunar2solar:该农历月是否闰月',
|
|
88
|
+
num1: '仅 xiaoliuren 报数起课:第一个数 1–99',
|
|
89
|
+
num2: '仅 xiaoliuren 报数起课:第二个数 1–99',
|
|
90
|
+
num3: '仅 xiaoliuren 报数起课:第三个数 1–99',
|
|
91
|
+
juMethod: '仅 qimen:取局流派 chaibu / chaoshen / zhirun',
|
|
92
|
+
lateZiHourMode: '仅 qimen:晚子时归 same-day 或 next-day',
|
|
93
|
+
partner2: '仅 hepan:第三方出生信息,给出即多回 triple 三对分数',
|
|
94
|
+
chart: '仅 hepan:底层用 bazi 还是 ziwei 盘,默认 bazi',
|
|
95
|
+
rectifyHours: '仅 bazi rectify:候选时辰列表 0–23,省略用十二时辰',
|
|
96
|
+
upper: '仅 meihua 数字起卦:定上卦的数,与 lower 成对',
|
|
97
|
+
lower: '仅 meihua 数字起卦:定下卦的数',
|
|
98
|
+
changing: '仅 meihua 数字起卦:手动指定动爻 1–6',
|
|
99
|
+
count: '仅 tarot:牌数 1 / 3 / 5,默认 3',
|
|
100
|
+
leapMonth: '仅 ziwei:闰月口径 split / next / prev',
|
|
101
|
+
leapAsNext: '仅 ziwei:旧写法,true 等同 leapMonth=next',
|
|
102
|
+
brightnessSchool: '仅 ziwei:十四主星亮度流派 default/QS/XD1/ZZ/XD2',
|
|
103
|
+
gengYearSihua: '仅 ziwei:庚干四化变体,默认 GYWYT',
|
|
104
|
+
tianmaSchool: '仅 ziwei:天马安法 year-branch / month-branch',
|
|
105
|
+
tiankongSchool: '仅 ziwei:天空安法 off / normal / shun-jia-shi',
|
|
106
|
+
jiekongSchool: '仅 ziwei:截空旬空安法 off / double / single / zhanyan',
|
|
107
|
+
kuiyueSchool: '仅 ziwei:天魁天钺安法 qs1 / qs2 / zhong-yiming',
|
|
108
|
+
tianshiTianshang: '仅 ziwei:天伤天使安法 normal / zhongzhou',
|
|
109
|
+
changshengSchool: '仅 ziwei:长生十二神起点流派',
|
|
110
|
+
lateZishi: '仅 ziwei:晚子时口径 current-day / next-day',
|
|
111
|
+
shenzhuSchool: '仅 ziwei:身主安法 common / verse',
|
|
112
|
+
zuoyaoSchool: '仅 ziwei:杂曜取盘 minimal / full',
|
|
113
|
+
action: 'fengshui:natal(默认)/ advise / xuankong 玄空;name:assess / generate',
|
|
114
|
+
toAge: '仅 trend:算到几岁 10–100,默认 100',
|
|
115
|
+
sitting: '仅 fengshui advise/xuankong 玄空:坐山(24 山之一)',
|
|
116
|
+
facing: '仅 fengshui advise/xuankong 玄空:向山(24 山之一)',
|
|
117
|
+
fortuneYear: '仅 fengshui:natal 定流年飞星年份;xuankong 定三元九运',
|
|
118
|
+
period: '仅 fengshui xuankong:三元九运 1–9,优先于 fortuneYear',
|
|
119
|
+
sittingOffset: '仅 fengshui xuankong:坐山偏离本山中心度数 −7.5–7.5',
|
|
120
|
+
sittingDegree: '仅 fengshui xuankong:坐山罗盘度数 0–360',
|
|
121
|
+
facingOffset: '仅 fengshui xuankong:向山偏离本山中心度数 −7.5–7.5',
|
|
122
|
+
facingDegree: '仅 fengshui xuankong:向山罗盘度数 0–360',
|
|
123
|
+
fortuneDate: '仅 fengshui xuankong:飞星日期 YYYY-MM-DD,才排月日时紫白',
|
|
124
|
+
fortuneTime: '仅 fengshui xuankong:飞星时刻 HH:MM,默认 12:00',
|
|
125
|
+
surname: '仅 name:姓氏,复姓整体写(如 欧阳)',
|
|
126
|
+
givenName: '仅 name assess:名 1–2 个汉字',
|
|
127
|
+
limit: '仅 name generate:候选个数 1–20,默认 10',
|
|
128
|
+
charElements: '仅 name:未收录字的五行,如 {"琰":"火"}',
|
|
129
|
+
strokeOverrides: '仅 name:逐字覆盖康熙笔画,如 {"沐":8}',
|
|
130
|
+
surnameStrokes: '仅 name:按姓氏逐字覆盖康熙笔画',
|
|
131
|
+
year: '仅 name:公历出生年,配合 month/day/hour',
|
|
132
|
+
month: '仅 name:公历出生月 1–12',
|
|
133
|
+
day: '仅 name:公历出生日 1–31',
|
|
134
|
+
answers: '仅 mbti:按题目顺序的 24 个 1–5 分,跳过传 null',
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const BIRTH_KEYS = ['datetime', 'gender', 'address', 'geocode', 'longitude', 'trueSolar', 'lateZi', 'dst', 'asOf', 'shadow'];
|
|
138
|
+
const READ_KEYS = ['withInterpret', 'detail', 'ask', 'question', 'direction', 'shadow', 'asOf'];
|
|
139
|
+
|
|
140
|
+
/** 每个体系实际会读的参数:help 按这张表展开,避免「有这个键但该体系不看」 */
|
|
141
|
+
const SYSTEM_PARAMS = {
|
|
142
|
+
bazi: [...BIRTH_KEYS, 'hourUnknown', 'verifyPast', 'rectify', 'events', 'rectifyHours', 'cross', 'diff', 'trace', ...READ_KEYS],
|
|
143
|
+
ziwei: [...BIRTH_KEYS, 'leapMonth', 'leapAsNext', 'brightnessSchool', 'gengYearSihua', 'tianmaSchool', 'tiankongSchool', 'jiekongSchool', 'kuiyueSchool', 'tianshiTianshang', 'changshengSchool', 'lateZishi', 'shenzhuSchool', 'zuoyaoSchool', 'cross', 'diff', 'trace', ...READ_KEYS],
|
|
144
|
+
liuyao: ['datetime', 'lines', 'seed', 'trace', ...READ_KEYS],
|
|
145
|
+
tarot: ['count', 'seed', ...READ_KEYS],
|
|
146
|
+
meihua: ['datetime', 'mode', 'upper', 'lower', 'changing', ...READ_KEYS],
|
|
147
|
+
daliuren: ['datetime', 'trace', ...READ_KEYS],
|
|
148
|
+
hepan: [...BIRTH_KEYS, 'partner', 'partner2', 'chart', 'leapMonth', 'ask'],
|
|
149
|
+
qimen: ['datetime', 'juMethod', 'lateZiHourMode', 'trace', ...READ_KEYS],
|
|
150
|
+
almanac: ['datetime', 'shadow'],
|
|
151
|
+
zeri: ['task', 'startDate', 'days', 'candidates', 'shengXiao', 'datetime', 'shadow'],
|
|
152
|
+
chenggu: ['datetime', 'hour', 'lateZi', 'gender', 'shadow'],
|
|
153
|
+
xiaoliuren: ['datetime', 'date', 'hour', 'mode', 'num1', 'num2', 'num3', 'lateZi', 'question', 'shadow'],
|
|
154
|
+
star: ['datetime', 'shadow'],
|
|
155
|
+
numerology: ['datetime', 'shadow'],
|
|
156
|
+
mbti: ['answers', 'shadow'],
|
|
157
|
+
trend: ['datetime', 'gender', 'address', 'geocode', 'longitude', 'trueSolar', 'hourUnknown', 'toAge', 'asOf', 'shadow', 'ask'],
|
|
158
|
+
fengshui: ['action', 'datetime', 'gender', 'sitting', 'facing', 'fortuneYear', 'period', 'sittingOffset', 'sittingDegree', 'facingOffset', 'facingDegree', 'fortuneDate', 'fortuneTime', 'dst', 'shadow'],
|
|
159
|
+
name: ['action', 'datetime', 'surname', 'givenName', 'limit', 'charElements', 'strokeOverrides', 'surnameStrokes', 'year', 'month', 'day', 'hour', 'minute', 'gender', 'address', 'geocode', 'longitude', 'trueSolar', 'dst', 'shadow'],
|
|
160
|
+
convert: ['mode', 'datetime', 'lunarYear', 'lunarMonth', 'lunarDay', 'isLeapMonth', 'shadow'],
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const SYSTEM_REQUIRED = {
|
|
164
|
+
bazi: 'datetime、gender',
|
|
165
|
+
ziwei: 'datetime、gender',
|
|
166
|
+
liuyao: '无(不传 lines 则随机摇卦)',
|
|
167
|
+
tarot: '无(问题可选)',
|
|
168
|
+
meihua: 'time 模式 datetime;number 模式 upper + lower',
|
|
169
|
+
daliuren: 'datetime',
|
|
170
|
+
hepan: 'datetime、gender,以及 partner{datetime, gender}',
|
|
171
|
+
qimen: 'datetime',
|
|
172
|
+
almanac: 'datetime(省略补今天)',
|
|
173
|
+
zeri: 'task',
|
|
174
|
+
chenggu: 'datetime(可只给日期 + hour)',
|
|
175
|
+
xiaoliuren: 'time 模式 datetime/date+hour;number 模式 num1–num3',
|
|
176
|
+
star: '无(省略补今天)',
|
|
177
|
+
numerology: '无(省略补今天)',
|
|
178
|
+
mbti: '无(先不带 answers 拿题)',
|
|
179
|
+
trend: 'datetime、gender',
|
|
180
|
+
fengshui: 'natal 要 datetime + gender;advise/xuankong 要 sitting 或 facing',
|
|
181
|
+
name: 'assess 要 surname + givenName;generate 要生辰(datetime 或 year/month/day)',
|
|
182
|
+
convert: 'solar2lunar 要 datetime;lunar2solar 要 lunarYear/Month/Day',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const PARAM_KEY_SET = new Set([...CORE_KEYS, ...OPTION_KEYS]);
|
|
186
|
+
|
|
187
|
+
function pickParams(all, keys) {
|
|
188
|
+
const out = {};
|
|
189
|
+
for (const k of keys) if (all[k]) out[k] = all[k];
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function buildProperties(all) {
|
|
194
|
+
const core = pickParams(all, CORE_KEYS);
|
|
195
|
+
const options = {};
|
|
196
|
+
for (const k of OPTION_KEYS) {
|
|
197
|
+
const spec = all[k];
|
|
198
|
+
if (!spec) continue;
|
|
199
|
+
options[k] = { ...spec, description: OPTION_HINTS[k] || spec.description };
|
|
200
|
+
}
|
|
201
|
+
core.options = {
|
|
202
|
+
type: 'object',
|
|
203
|
+
description: '体系专属参数:只放当前 system 用得到的键(如 ziwei 的 brightnessSchool、fengshui 的 sitting、name 的 surname)。键名与取值用 metaphysics_help 查;与顶层同名时以顶层为准。',
|
|
204
|
+
properties: options,
|
|
205
|
+
};
|
|
206
|
+
return core;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** options 摊平到顶层:引擎与句柄只认扁平参数,旧调用(直接平铺)继续有效 */
|
|
210
|
+
function flattenOptions(raw, all, bad) {
|
|
211
|
+
const { options, ...rest } = raw;
|
|
212
|
+
// MCP 客户端常把「没有 options」写成 null;null 与 undefined 同义,都是没给。
|
|
213
|
+
if (options === undefined || options === null) return rest;
|
|
214
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
215
|
+
throw bad('options 必须是对象,例如 options: { brightnessSchool: "QS" }');
|
|
216
|
+
}
|
|
217
|
+
// hasOwn 而不是 in:'constructor' / 'toString' / '__proto__' 都在原型链上,
|
|
218
|
+
// 用 in 判断会让这些键混过「未知参数」检查,静默落进参数里。
|
|
219
|
+
const unknown = Object.keys(options).filter((k) => !hasOwn(all, k));
|
|
220
|
+
if (unknown.length) {
|
|
221
|
+
throw bad(`options 里有未知参数:${unknown.join('、')};可用键见 metaphysics_help`);
|
|
222
|
+
}
|
|
223
|
+
return { ...options, ...rest };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 参数整形:模型常把 true 写成 "true"、把 9 写成 "9"。
|
|
230
|
+
* 按 schema 声明的类型统一归一,非法值当场报 E_INPUT:
|
|
231
|
+
* 以前字符串布尔被 bool() 当 false(以为开了其实没开),字符串数字在引擎里
|
|
232
|
+
* 报「运必须是 1–9 的整数,收到 9」——数字明明合法,错得让人无从下手。
|
|
233
|
+
*/
|
|
234
|
+
function normalizeArgs(raw, all, bad) {
|
|
235
|
+
const normalize = (spec, value, key) => {
|
|
236
|
+
if (value === undefined || value === null) return value;
|
|
237
|
+
const types = Array.isArray(spec.type) ? spec.type : [spec.type];
|
|
238
|
+
if (types.includes('boolean')) {
|
|
239
|
+
if (typeof value === 'boolean') return value;
|
|
240
|
+
if (value === 1 || value === 0) return value === 1;
|
|
241
|
+
if (typeof value === 'string') {
|
|
242
|
+
const s = value.trim().toLowerCase();
|
|
243
|
+
if (!s) return undefined;
|
|
244
|
+
if (s === 'true' || s === '1') return true;
|
|
245
|
+
if (s === 'false' || s === '0') return false;
|
|
246
|
+
}
|
|
247
|
+
throw bad(`${key} 要是布尔值 true / false,收到「${String(value).slice(0, 24)}」`);
|
|
248
|
+
}
|
|
249
|
+
// schema 里的 enum 以前只写给模型看、不做校验:direction:"love"(少了 s)会被引擎
|
|
250
|
+
// 静默当成 general,gender:"女" 在六爻里静默按 male 出用神。枚举是契约的一部分,
|
|
251
|
+
// 写错就该在入口报出来,而不是换一张「看着正常」的盘。
|
|
252
|
+
if (types.includes('string') && Array.isArray(spec.enum) && typeof value === 'string') {
|
|
253
|
+
const s = value.trim();
|
|
254
|
+
if (!s) return undefined;
|
|
255
|
+
if (!spec.enum.includes(s)) {
|
|
256
|
+
throw bad(`${key} 只能是 ${spec.enum.join(' / ')},收到「${s.slice(0, 24)}」`);
|
|
257
|
+
}
|
|
258
|
+
return s;
|
|
259
|
+
}
|
|
260
|
+
if (types.includes('integer') || types.includes('number')) {
|
|
261
|
+
const integer = types.includes('integer');
|
|
262
|
+
if (typeof value === 'string') {
|
|
263
|
+
const s = value.trim();
|
|
264
|
+
if (!s) return undefined;
|
|
265
|
+
if (!/^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(s)) {
|
|
266
|
+
throw bad(`${key} 要是${integer ? '整数' : '数字'},收到「${s.slice(0, 24)}」`);
|
|
267
|
+
}
|
|
268
|
+
value = Number(s);
|
|
269
|
+
}
|
|
270
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
271
|
+
throw bad(`${key} 要是${integer ? '整数' : '有限数字'},收到「${String(value).slice(0, 24)}」`);
|
|
272
|
+
}
|
|
273
|
+
if (integer && !Number.isInteger(value)) throw bad(`${key} 要是整数,收到「${value}」`);
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
if (types.includes('array') && Array.isArray(value) && spec.items) {
|
|
277
|
+
return value.map((item) => normalize(spec.items, item, `${key}[]`));
|
|
278
|
+
}
|
|
279
|
+
if (types.includes('object') && value && typeof value === 'object' && !Array.isArray(value) && spec.properties) {
|
|
280
|
+
const out = { ...value };
|
|
281
|
+
for (const [k, child] of Object.entries(spec.properties)) {
|
|
282
|
+
if (out[k] !== undefined) out[k] = normalize(child, out[k], `${key}.${k}`);
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
return value;
|
|
287
|
+
};
|
|
288
|
+
const out = { ...raw };
|
|
289
|
+
for (const [key, spec] of Object.entries(all)) {
|
|
290
|
+
// 经度有专门的尺(normalizeLongitudes):十进制串它自己会转,非十进制报 E_RANGE。
|
|
291
|
+
// 这里先按「数字」拦下会把那条更准确的报错盖成泛泛的「要是数字」。
|
|
292
|
+
if (key === 'longitude') continue;
|
|
293
|
+
if (out[key] !== undefined) out[key] = normalize(spec, out[key], key);
|
|
294
|
+
}
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function paramDoc(key, spec) {
|
|
299
|
+
const type = Array.isArray(spec.type) ? spec.type.join('|') : spec.type;
|
|
300
|
+
const values = spec.enum ? `,取值 ${spec.enum.join(' / ')}` : '';
|
|
301
|
+
return `- ${key}(${type}${values}):${spec.description}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function helpText(all, args) {
|
|
305
|
+
const key = typeof args.key === 'string' ? args.key.trim() : '';
|
|
306
|
+
if (key) {
|
|
307
|
+
const spec = hasOwn(all, key) ? all[key] : null;
|
|
308
|
+
if (!spec) return `没有参数「${key}」。可用参数:${Object.keys(all).join('、')}`;
|
|
309
|
+
return paramDoc(key, spec);
|
|
310
|
+
}
|
|
311
|
+
const system = typeof args.system === 'string' ? args.system.trim() : '';
|
|
312
|
+
if (system) {
|
|
313
|
+
if (!SYSTEM_ENUM.includes(system)) return `未知体系「${system}」。可选:${SYSTEM_ENUM.join('、')}`;
|
|
314
|
+
const keys = [...new Set(SYSTEM_PARAMS[system])];
|
|
315
|
+
return [
|
|
316
|
+
`# ${system} 参数`,
|
|
317
|
+
`必填:${SYSTEM_REQUIRED[system]}`,
|
|
318
|
+
`可用键:${keys.join('、')}`,
|
|
319
|
+
'',
|
|
320
|
+
...keys.map((k) => paramDoc(k, all[k])),
|
|
321
|
+
].join('\n');
|
|
322
|
+
}
|
|
323
|
+
return [
|
|
324
|
+
'# metaphysics_chart 参数索引',
|
|
325
|
+
`顶层通用参数:${CORE_KEYS.join('、')}`,
|
|
326
|
+
'体系专属参数放进 options;各体系可用键:',
|
|
327
|
+
...SYSTEM_ENUM.map((s) => `- ${s}:${[...new Set(SYSTEM_PARAMS[s])].join('、')}`),
|
|
328
|
+
'',
|
|
329
|
+
'查单个参数全文:metaphysics_help({ key: "参数名" });查某体系:metaphysics_help({ system: "ziwei" })',
|
|
330
|
+
].join('\n');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function helpDefinition(all) {
|
|
334
|
+
return {
|
|
335
|
+
name: 'metaphysics_help',
|
|
336
|
+
description: '查 metaphysics_chart 的参数说明:省略参数给体系索引;system 给该体系的必填项与参数全解;key 给单个参数全文。只在不确定键名或取值时调用,排盘本身不需要它。',
|
|
337
|
+
parameters: {
|
|
338
|
+
type: 'object',
|
|
339
|
+
properties: {
|
|
340
|
+
system: { type: 'string', enum: SYSTEM_ENUM, description: '要查的体系,如 ziwei / fengshui / name;省略则返回全部体系的键索引。' },
|
|
341
|
+
key: { type: 'string', description: '要查的参数名,如 brightnessSchool、sitting、action;给出时优先于 system。' },
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
output: {
|
|
345
|
+
schema: { type: 'object' },
|
|
346
|
+
render: (_args, value) => [{ type: 'text', text: String((value && value.text) || '') }],
|
|
347
|
+
},
|
|
348
|
+
async execute(args) {
|
|
349
|
+
return { text: helpText(all, args || {}) };
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
44
354
|
export function apply(ctx) {
|
|
45
355
|
const bad = (message) => {
|
|
46
356
|
const e = new Error(message);
|
|
@@ -65,8 +375,10 @@ export function apply(ctx) {
|
|
|
65
375
|
}
|
|
66
376
|
|
|
67
377
|
/** 工具主体的实际流程:参数整形 → 地址解析(默认离线) → 排盘 */
|
|
68
|
-
async function run(
|
|
69
|
-
if (!
|
|
378
|
+
async function run(rawArgs) {
|
|
379
|
+
if (!rawArgs || typeof rawArgs !== 'object' || Array.isArray(rawArgs)) throw bad('参数必须是对象');
|
|
380
|
+
// 渐进披露写法先摊平:后面的句柄合并、地址解析与体系校验都只认扁平参数
|
|
381
|
+
const args = flattenOptions(rawArgs, ALL_PARAMS, bad);
|
|
70
382
|
if (args.geocode !== undefined && args.geocode !== 'offline' && args.geocode !== 'online') {
|
|
71
383
|
throw bad(`geocode 只能是 offline / online,收到「${args.geocode}」`);
|
|
72
384
|
}
|
|
@@ -99,6 +411,8 @@ export function apply(ctx) {
|
|
|
99
411
|
ignored: rev.ignored,
|
|
100
412
|
};
|
|
101
413
|
}
|
|
414
|
+
// 句柄与一句话改参都可能带来 "true"/"9" 这类字符串写法:归一后再做体系校验与排盘
|
|
415
|
+
next = normalizeArgs(next, ALL_PARAMS, bad);
|
|
102
416
|
if (next.shadow === true && !next.shadowVersion) next.shadowVersion = PKG_VERSION;
|
|
103
417
|
// hour / date / minute 只有会读它们的体系放行:其它体系收了也不看,用户以为改的是时刻,
|
|
104
418
|
// 实际仍按 datetime(或 00:00)排。宁可直接报错,也不静默排成另一张盘。
|
|
@@ -207,6 +521,11 @@ export function apply(ctx) {
|
|
|
207
521
|
&& next.fortuneYear === undefined) {
|
|
208
522
|
next.fortuneYear = now.getFullYear();
|
|
209
523
|
}
|
|
524
|
+
// 工具面默认给 brief:模型九成只要结论,全文里一半以上是长论述与「规则·出处」。
|
|
525
|
+
// 引擎/面板的缺省仍是 full(UI 要完整解析);模型追问依据时显式传 detail:"full"。
|
|
526
|
+
if (next.withInterpret === true && (next.detail === undefined || next.detail === null || next.detail === '')) {
|
|
527
|
+
next.detail = 'brief';
|
|
528
|
+
}
|
|
210
529
|
const out = compute(next);
|
|
211
530
|
if (revision) out.revision = revision;
|
|
212
531
|
// 显式要求时才做口径对照:每次要额外排 2–3 张盘,默认不付这笔开销
|
|
@@ -217,77 +536,74 @@ export function apply(ctx) {
|
|
|
217
536
|
return out;
|
|
218
537
|
}
|
|
219
538
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
parameters: {
|
|
224
|
-
type: 'object',
|
|
225
|
-
properties: {
|
|
539
|
+
// 88 个参数的单一来源:顶层/options 只是它的两种视图,help 也读这一份。
|
|
540
|
+
// 新增参数只改这里与 CORE_KEYS/OPTION_KEYS/OPTION_HINTS,别在两个地方各写一份。
|
|
541
|
+
const ALL_PARAMS = {
|
|
226
542
|
handle: {
|
|
227
543
|
type: 'string',
|
|
228
544
|
description:
|
|
229
|
-
'
|
|
545
|
+
'排盘句柄(mc1.xxx.yyy):带到下一次调用即可只给增量参数,不必重报出生信息;截断或改字报 E_HANDLE。',
|
|
230
546
|
},
|
|
231
547
|
revise: {
|
|
232
548
|
type: 'string',
|
|
233
549
|
description:
|
|
234
|
-
'
|
|
550
|
+
'一句话改参:把用户原话放这里(如「女命」「换成紫微看 2030 年事业」),引擎翻译成参数补丁并在 revision 回报;识别不到不猜。',
|
|
235
551
|
},
|
|
236
552
|
system: {
|
|
237
553
|
type: 'string',
|
|
238
|
-
enum:
|
|
554
|
+
enum: SYSTEM_ENUM,
|
|
239
555
|
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 农历转公历)。',
|
|
240
556
|
},
|
|
241
557
|
datetime: {
|
|
242
558
|
type: 'string',
|
|
243
559
|
description:
|
|
244
|
-
'公历本地时间,如 1990-05-12T14:30。bazi / ziwei 必填;
|
|
560
|
+
'公历本地时间,如 1990-05-12T14:30。bazi / ziwei 必填;almanac / star / numerology 只需日期;日期类体系省略时补今天。',
|
|
245
561
|
},
|
|
246
562
|
date: {
|
|
247
563
|
type: 'string',
|
|
248
|
-
description: '仅 xiaoliuren
|
|
564
|
+
description: '仅 xiaoliuren 时间起课:日期 YYYY-MM-DD(与 hour 成对)。',
|
|
249
565
|
},
|
|
250
566
|
hour: {
|
|
251
567
|
type: 'integer',
|
|
252
|
-
description: '
|
|
568
|
+
description: '时辰小时 0–23:chenggu 补时辰、xiaoliuren 时间起课、name 生辰。',
|
|
253
569
|
},
|
|
254
570
|
minute: {
|
|
255
571
|
type: 'integer',
|
|
256
|
-
description: '
|
|
572
|
+
description: '分钟 0–59,仅 name 配合 year/month/day 用。',
|
|
257
573
|
},
|
|
258
574
|
dst: {
|
|
259
575
|
type: 'boolean',
|
|
260
|
-
description: '
|
|
576
|
+
description: '是否校正 1986–1991 夏令时,默认 true。',
|
|
261
577
|
},
|
|
262
578
|
gender: {
|
|
263
579
|
type: 'string',
|
|
264
580
|
enum: ['male', 'female'],
|
|
265
|
-
description: '
|
|
581
|
+
description: '性别 male / female,bazi / ziwei / hepan 必填。',
|
|
266
582
|
},
|
|
267
583
|
address: {
|
|
268
584
|
type: 'string',
|
|
269
585
|
description:
|
|
270
|
-
'
|
|
586
|
+
'出生地(省/市即可),用于真太阳时;与 longitude 同时给出时以后者为准。',
|
|
271
587
|
},
|
|
272
588
|
geocode: {
|
|
273
589
|
type: 'string',
|
|
274
590
|
enum: ['offline', 'online'],
|
|
275
591
|
description:
|
|
276
|
-
'
|
|
592
|
+
'地址解析:offline(默认,内置表)/ online(发第三方 Nominatim,仅明确要求区县时)。',
|
|
277
593
|
},
|
|
278
594
|
longitude: {
|
|
279
595
|
type: 'number',
|
|
280
596
|
description:
|
|
281
|
-
'
|
|
597
|
+
'出生地经度(东经为正),如乌鲁木齐 87.6、北京 116.4。',
|
|
282
598
|
},
|
|
283
599
|
lateZi: {
|
|
284
600
|
type: 'boolean',
|
|
285
601
|
description:
|
|
286
|
-
'23:00
|
|
602
|
+
'23:00 后是否算次日;多数体系默认 true,qimen 默认 false。',
|
|
287
603
|
},
|
|
288
604
|
trueSolar: {
|
|
289
605
|
type: 'boolean',
|
|
290
|
-
description: '
|
|
606
|
+
description: '是否启用真太阳时,默认 true;无经度时退回平太阳时。',
|
|
291
607
|
},
|
|
292
608
|
lines: {
|
|
293
609
|
type: 'array',
|
|
@@ -297,7 +613,7 @@ export function apply(ctx) {
|
|
|
297
613
|
},
|
|
298
614
|
question: {
|
|
299
615
|
type: 'string',
|
|
300
|
-
description: '
|
|
616
|
+
description: '所问之事:liuyao / tarot / meihua 用于取象,withInterpret 时写进解析。',
|
|
301
617
|
},
|
|
302
618
|
task: {
|
|
303
619
|
type: 'string',
|
|
@@ -326,7 +642,7 @@ export function apply(ctx) {
|
|
|
326
642
|
type: 'string',
|
|
327
643
|
enum: ['solar2lunar', 'lunar2solar', 'time', 'number'],
|
|
328
644
|
description:
|
|
329
|
-
'
|
|
645
|
+
'取值按体系分三组。convert:solar2lunar / lunar2solar;meihua:time / number;xiaoliuren:time / number。',
|
|
330
646
|
},
|
|
331
647
|
lunarYear: { type: 'integer', description: '仅 convert 的 lunar2solar:农历年份,如 2026。' },
|
|
332
648
|
lunarMonth: { type: 'integer', description: '仅 convert 的 lunar2solar:农历月份 1–12。' },
|
|
@@ -338,39 +654,39 @@ export function apply(ctx) {
|
|
|
338
654
|
direction: {
|
|
339
655
|
type: 'string',
|
|
340
656
|
enum: ['general', 'wealth', 'career', 'love', 'health', 'study', 'travel'],
|
|
341
|
-
description: '
|
|
657
|
+
description: '询问方向(配合 withInterpret):general / wealth / career / love / health / study / travel。',
|
|
342
658
|
},
|
|
343
659
|
cross: {
|
|
344
660
|
type: 'boolean',
|
|
345
|
-
description: '
|
|
661
|
+
description: 'bazi / ziwei:为同一出生信息再排另一套盘,返回交叉验证,不合并数值。',
|
|
346
662
|
},
|
|
347
663
|
hourUnknown: {
|
|
348
664
|
type: 'boolean',
|
|
349
|
-
description: '
|
|
665
|
+
description: '出生时刻未知(bazi / trend):只排三柱并标注;ziwei / hepan 直接阻断。',
|
|
350
666
|
},
|
|
351
667
|
diff: {
|
|
352
668
|
type: 'boolean',
|
|
353
|
-
description: '口径对照(bazi / ziwei / qimen
|
|
669
|
+
description: '口径对照(bazi / ziwei / qimen):按另一档口径再排一次,只列差异不改盘。',
|
|
354
670
|
},
|
|
355
671
|
shadow: {
|
|
356
672
|
type: 'boolean',
|
|
357
|
-
description: '
|
|
673
|
+
description: '影子盘:结果附短 ID,同参数同 ID,用于回放/对拍。',
|
|
358
674
|
},
|
|
359
675
|
trace: {
|
|
360
676
|
type: 'boolean',
|
|
361
|
-
description: '
|
|
677
|
+
description: '推导链(liuyao / bazi / ziwei / qimen / daliuren):返回每层规则与中间值,另给 traceText。',
|
|
362
678
|
},
|
|
363
679
|
verifyPast: {
|
|
364
680
|
type: 'number',
|
|
365
|
-
description: '
|
|
681
|
+
description: '验前事(bazi):传年数,返回过去 N 年流年信号行。',
|
|
366
682
|
},
|
|
367
683
|
rectify: {
|
|
368
684
|
type: 'boolean',
|
|
369
|
-
description: '
|
|
685
|
+
description: '生时校正(bazi):配合 events 给候选时辰排序。',
|
|
370
686
|
},
|
|
371
687
|
events: {
|
|
372
688
|
type: 'array',
|
|
373
|
-
description: '生时校正用的过去事件:年份 +
|
|
689
|
+
description: '生时校正用的过去事件:年份 + 类别。',
|
|
374
690
|
items: {
|
|
375
691
|
type: 'object',
|
|
376
692
|
properties: {
|
|
@@ -382,24 +698,24 @@ export function apply(ctx) {
|
|
|
382
698
|
},
|
|
383
699
|
ask: {
|
|
384
700
|
type: 'string',
|
|
385
|
-
description: '
|
|
701
|
+
description: '追问:围绕已算好的盘回答某年/某方面,只取盘面字段。',
|
|
386
702
|
},
|
|
387
703
|
withInterpret: {
|
|
388
704
|
type: 'boolean',
|
|
389
|
-
description: '
|
|
705
|
+
description: '是否附带解析与详解,默认 false;追问依据时再开。',
|
|
390
706
|
},
|
|
391
707
|
detail: {
|
|
392
708
|
type: 'string',
|
|
393
|
-
enum: ['
|
|
394
|
-
description: '
|
|
709
|
+
enum: ['brief', 'lean', 'full'],
|
|
710
|
+
description: '解析档位(仅 withInterpret):缺省 brief(结论+所问方向+三张表,表最多 8 行,不按体积截断)/ lean 再给其余维度 / full 全论述与规则出处。',
|
|
395
711
|
},
|
|
396
712
|
asOf: {
|
|
397
713
|
type: 'string',
|
|
398
|
-
description: '
|
|
714
|
+
description: '基准时刻(如 2026-10-22T16:00):紫微定流月日时,八字定流年起点,塔罗记抽牌时刻。',
|
|
399
715
|
},
|
|
400
716
|
seed: {
|
|
401
717
|
type: 'number',
|
|
402
|
-
description: '
|
|
718
|
+
description: '随机种子(tarot / liuyao):同 seed 可复现。',
|
|
403
719
|
},
|
|
404
720
|
juMethod: {
|
|
405
721
|
type: 'string',
|
|
@@ -413,7 +729,7 @@ export function apply(ctx) {
|
|
|
413
729
|
},
|
|
414
730
|
partner: {
|
|
415
731
|
type: 'object',
|
|
416
|
-
description: '
|
|
732
|
+
description: '合盘对方:datetime + gender 必填,address 可选。仅 hepan 使用。',
|
|
417
733
|
properties: {
|
|
418
734
|
datetime: { type: 'string', description: '对方公历本地时间,如 1998-05-20T10:30' },
|
|
419
735
|
gender: { type: 'string', enum: ['male', 'female'], description: '对方性别(决定对方大运/大限顺逆)' },
|
|
@@ -584,9 +900,16 @@ export function apply(ctx) {
|
|
|
584
900
|
items: { type: ['integer', 'null'] },
|
|
585
901
|
description: '仅 mbti 使用:按题目顺序的 24 个分数,1(完全不像)–5(很像我);跳过某题传 null。不给则返回问卷题目,答满 12 题即可出类型。',
|
|
586
902
|
},
|
|
587
|
-
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
const definition = {
|
|
906
|
+
name: TOOL_NAME,
|
|
907
|
+
description: DESCRIPTION,
|
|
908
|
+
parameters: {
|
|
909
|
+
type: 'object',
|
|
588
910
|
// 不写 required:只带 handle 的追问是文档主推用法,schema 层拦住会让宿主先拒绝。
|
|
589
911
|
// 真正的必填校验放到 run():system 或 handle 至少给一个。
|
|
912
|
+
properties: buildProperties(ALL_PARAMS),
|
|
590
913
|
},
|
|
591
914
|
output: {
|
|
592
915
|
schema: { type: 'object' },
|
|
@@ -608,6 +931,8 @@ export function apply(ctx) {
|
|
|
608
931
|
},
|
|
609
932
|
};
|
|
610
933
|
|
|
934
|
+
// help 先注册、chart 后注册:工具列表里两者都在,而测试与宿主按最后一次注册取主工具。
|
|
935
|
+
ctx.effect(() => ctx.tools.register(helpDefinition(ALL_PARAMS)));
|
|
611
936
|
ctx.effect(() => ctx.tools.register(definition));
|
|
612
937
|
}
|
|
613
938
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-metaphysics",
|
|
3
|
-
"version": "0.62.
|
|
3
|
+
"version": "0.62.2",
|
|
4
4
|
"type": "module",
|
|
5
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, 1,062-char name library with per-character pinyin, Kangxi stroke counts, five-grid (五格/三才) analysis and mechanical phonetics checks, a 78-card tarot library on the Rider-Waite-Smith base (per-card English name, image description, core meaning and astrological correspondence, cross-checked against three published sources; court cards carry no zodiac by design and the chart says so), 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 (全卦逐爻旺衰) and response-time clues (应期线索:逢值/逢冲/逢合 + 月建五态快慢) 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 (闰月按日切分/整月归次月/整月归本月) eleven school switches (亮度/庚干四化/天马/天空/截空旬空/魁钺/天伤天使/长生/晚子时/身主/杂曜全表) for ziwei, the last one opting in to a 61-star minor-star (杂曜) table where each of the twelve extra stars carries its placement verse and the palace it landed in (verses accepted only when at least two independent published editions give the same table, homonymous stars never merged), caliber diffs for bazi/ziwei/qimen (same inputs, one switch, root-field changes), shadow-chart IDs (same normalized params → same ID; version bumps auto-diff chart fields), step-by-step deduction traces (liuyao najia→liuqin→shiying→fushen→strength; bazi strength score and pattern/yongshen; ziwei pattern conditions), and a bundled offline single-file page (命理排盘.html), a panel-to-chat handoff that asks the Harness about a finished chart, per-tab lazy engine chunks so the first-screen client bundle stays under 100KB gzip, and a public verification report (128 classical hexagram cases, 312 Zi Wei charts × 34 patterns, 12 dayun handover cases) generated from the same source that gates CI.",
|
|
6
6
|
"keywords": [
|