dsh-metaphysics 0.56.0 → 0.58.0
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 +103 -1
- package/README.md +26 -9
- package/client.almanac.js +66 -61
- package/client.bazi.js +98 -91
- package/client.chenggu.js +68 -63
- package/client.convert.js +68 -63
- package/client.daliuren.js +104 -97
- package/client.fengshui.js +65 -60
- package/client.hepan.js +99 -92
- package/client.js +392 -267
- package/client.liuyao.js +101 -94
- package/client.mbti.js +66 -61
- package/client.name.js +68 -63
- package/client.numerology.js +66 -61
- package/client.qimen.js +104 -97
- package/client.star.js +66 -61
- package/client.tarot.js +99 -94
- package/client.trend.js +104 -97
- package/client.xiaoliuren.js +66 -61
- package/client.zeri.js +66 -61
- package/client.ziwei.js +98 -91
- package/engine/bridge.js +211 -0
- package/engine/citations.js +603 -320
- package/engine/diff.js +59 -0
- package/engine/fengshui.js +718 -718
- package/engine/geju.js +16 -0
- package/engine/handle.js +192 -0
- package/engine/hepan.js +43 -0
- package/engine/index.js +168 -51
- package/engine/interpret-knowledge.js +3 -2
- package/engine/interpret-plain.js +4 -1
- package/engine/interpret.js +7 -3
- package/engine/qimen-ganying.js +134 -134
- package/engine/qimen-interpret.js +233 -233
- package/engine/revise.js +162 -0
- package/engine/trace.js +493 -456
- package/engine/zeri.js +89 -13
- package/index.js +59 -6
- package/package.json +6 -3
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +3548 -1943
package/engine/zeri.js
CHANGED
|
@@ -81,23 +81,68 @@ const VERDICT = (score, ctx) => {
|
|
|
81
81
|
* @param {{task:string, startDate:string, days?:number, shengXiao?:string}} input
|
|
82
82
|
* startDate 必填(公历 YYYY-MM-DD),避免引擎在无时钟环境下静默取「今天」。
|
|
83
83
|
*/
|
|
84
|
+
/** 严格解析公历日期:2 月 30 日这类不存在的日子直接判非法,不滚到下个月 */
|
|
85
|
+
function parseYmd(raw) {
|
|
86
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(raw || '').trim());
|
|
87
|
+
if (!m) return null;
|
|
88
|
+
const year = Number(m[1]);
|
|
89
|
+
const month = Number(m[2]);
|
|
90
|
+
const day = Number(m[3]);
|
|
91
|
+
if (month < 1 || month > 12 || day < 1) return null;
|
|
92
|
+
const back = fromJDN(toJDN(year, month, day));
|
|
93
|
+
return back.year === year && back.month === month && back.day === day ? { year, month, day } : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const ymd = (p) => `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 择日主函数。
|
|
100
|
+
* @param {{task:string, startDate:string, days?:number, shengXiao?:string,
|
|
101
|
+
* candidates?:string[]}} input
|
|
102
|
+
* startDate 必填(公历 YYYY-MM-DD),避免引擎在无时钟环境下静默取「今天」。
|
|
103
|
+
* candidates 给了就只比这几个候选日(2–12 个,去重、保持输入顺序),startDate 与 days 不参与。
|
|
104
|
+
*/
|
|
84
105
|
export function buildZeri(input) {
|
|
85
106
|
const task = input.task;
|
|
86
107
|
if (!TASKS[task]) throw new Error(`不支持的事项「${task}」。支持:${Object.keys(TASKS).join('、')}`);
|
|
87
|
-
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.startDate || '').trim());
|
|
88
|
-
if (!m) throw new Error('择日需要 startDate(公历 YYYY-MM-DD)——请向用户询问起算日,不要代填');
|
|
89
|
-
const start = { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
90
|
-
const days = Math.max(1, Math.min(60, Number.isInteger(input.days) ? input.days : 30));
|
|
91
108
|
const shengXiaoRaw = typeof input.shengXiao === 'string' ? input.shengXiao.trim() : '';
|
|
92
109
|
const shengXiao = shengXiaoRaw || null;
|
|
93
110
|
if (shengXiao && !SHENGXIAO.includes(shengXiao)) throw new Error(`shengXiao 须为生肖名:${SHENGXIAO.join('、')}`);
|
|
94
111
|
|
|
112
|
+
// 候选日模式:只比用户给的这几天,不再按区间扫
|
|
113
|
+
let candidates = null;
|
|
114
|
+
if (input.candidates !== undefined && input.candidates !== null) {
|
|
115
|
+
if (!Array.isArray(input.candidates)) {
|
|
116
|
+
throw new Error('candidates 须为日期数组,例如 ["2026-12-01","2026-12-08","2026-12-15"]');
|
|
117
|
+
}
|
|
118
|
+
if (input.candidates.length < 2) throw new Error('candidates 至少给 2 个候选日;只想看一天请用 startDate + days: 1');
|
|
119
|
+
if (input.candidates.length > 12) throw new Error('candidates 最多 12 个候选日;更多请用 startDate + days 扫区间');
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
candidates = [];
|
|
122
|
+
for (const raw of input.candidates) {
|
|
123
|
+
const p = parseYmd(raw);
|
|
124
|
+
if (!p) throw new Error(`候选日不是真实存在的公历日期:「${raw}」,请用 2026-12-01 这种格式`);
|
|
125
|
+
const key = ymd(p);
|
|
126
|
+
if (seen.has(key)) continue;
|
|
127
|
+
seen.add(key);
|
|
128
|
+
candidates.push(p);
|
|
129
|
+
}
|
|
130
|
+
if (candidates.length < 2) throw new Error('候选日去重后不足 2 天;至少要两个不同的日子才谈得上比较');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let start = null;
|
|
134
|
+
let days = 0;
|
|
135
|
+
if (!candidates) {
|
|
136
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.startDate || '').trim());
|
|
137
|
+
if (!m) throw new Error('择日需要 startDate(公历 YYYY-MM-DD)——请向用户询问起算日,不要代填');
|
|
138
|
+
start = { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
139
|
+
days = Math.max(1, Math.min(60, Number.isInteger(input.days) ? input.days : 30));
|
|
140
|
+
}
|
|
141
|
+
|
|
95
142
|
const spec = TASKS[task];
|
|
96
143
|
const zhixingSet = ZHIXING_FAVOR[spec.category];
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
for (let i = 0; i < days; i++) {
|
|
100
|
-
const p = fromJDN(startJdn + i);
|
|
144
|
+
|
|
145
|
+
const makeRow = (p) => {
|
|
101
146
|
const a = buildAlmanac(p);
|
|
102
147
|
let score = 0;
|
|
103
148
|
const reasons = [];
|
|
@@ -114,7 +159,7 @@ export function buildZeri(input) {
|
|
|
114
159
|
if (a.zhiXing === '危' && spec.category !== 'travel' && spec.category !== 'worship') { score -= 1; reasons.push('危日(-1)'); }
|
|
115
160
|
|
|
116
161
|
const v = VERDICT(score, { chongSelf, jiHit });
|
|
117
|
-
|
|
162
|
+
return {
|
|
118
163
|
date: a.meta.date,
|
|
119
164
|
weekday: a.meta.weekday,
|
|
120
165
|
lunar: a.lunar.text,
|
|
@@ -128,17 +173,47 @@ export function buildZeri(input) {
|
|
|
128
173
|
verdict: v.verdict,
|
|
129
174
|
verdictText: v.text,
|
|
130
175
|
...(v.excluded ? { excluded: true } : {}),
|
|
131
|
-
}
|
|
132
|
-
}
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const list = candidates
|
|
180
|
+
? candidates.map(makeRow)
|
|
181
|
+
: Array.from({ length: days }, (_, i) => makeRow(fromJDN(toJDN(start.year, start.month, start.day) + i)));
|
|
133
182
|
|
|
134
183
|
const ranked = list.slice().sort((x, y) => y.score - x.score || (x.date < y.date ? -1 : 1));
|
|
184
|
+
const startDate = candidates
|
|
185
|
+
? candidates.slice().sort((x, y) => (ymd(x) < ymd(y) ? -1 : 1)).map(ymd)[0]
|
|
186
|
+
: ymd(start);
|
|
187
|
+
|
|
188
|
+
// 候选日比较:只在这几天里比,给出最高分和同分者;不换算成「好多少」
|
|
189
|
+
let compare = null;
|
|
190
|
+
if (candidates) {
|
|
191
|
+
const pool = ranked.filter((d) => !d.excluded);
|
|
192
|
+
const top = pool[0] || null;
|
|
193
|
+
const scores = list.map((d) => d.score);
|
|
194
|
+
compare = {
|
|
195
|
+
mode: 'candidates',
|
|
196
|
+
count: list.length,
|
|
197
|
+
best: top ? {
|
|
198
|
+
date: top.date,
|
|
199
|
+
score: top.score,
|
|
200
|
+
verdict: top.verdict,
|
|
201
|
+
ties: pool.filter((d) => d.score === top.score).map((d) => d.date),
|
|
202
|
+
} : null,
|
|
203
|
+
spread: Math.max(...scores) - Math.min(...scores),
|
|
204
|
+
note: '候选日只在这几天里比:分数来自建除宜忌与冲煞,胜出只表示更合所问事项,不代表「好多少」。',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
135
208
|
return {
|
|
136
209
|
system: 'zeri',
|
|
137
210
|
meta: {
|
|
138
211
|
task,
|
|
139
|
-
|
|
140
|
-
|
|
212
|
+
mode: candidates ? 'candidates' : 'range',
|
|
213
|
+
startDate,
|
|
214
|
+
days: list.length,
|
|
141
215
|
shengXiao,
|
|
216
|
+
...(candidates ? { candidates: candidates.map(ymd) } : {}),
|
|
142
217
|
scope: shengXiao
|
|
143
218
|
? `宜忌按建除简表;已给当事人属相(${shengXiao}),凡当日冲该属相径直排除。评分仅用于排序引导。`
|
|
144
219
|
: '宜忌按建除简表;未给当事人属相,未做冲属相排除。评分仅用于排序引导。',
|
|
@@ -147,6 +222,7 @@ export function buildZeri(input) {
|
|
|
147
222
|
usable: ranked.filter((d) => d.verdict === '可用' && !d.excluded).slice(0, 5).map((d) => d.date),
|
|
148
223
|
avoid: list.filter((d) => d.verdict === '回避').map((d) => `${d.date}(${d.reasons.join(';') || '综合减分'})`),
|
|
149
224
|
days: list,
|
|
225
|
+
...(compare ? { compare } : {}),
|
|
150
226
|
excludedCount: list.filter((d) => d.excluded).length,
|
|
151
227
|
sources: ['宜忌:建除十二神通行简表', '建除适配与冲煞:传统通行口径', '排序:宜忌加减分 + 属相排除(确定性)'],
|
|
152
228
|
disclaimer: '择日为传统黄历口径的本地计算,属民俗参考;评分只用于排序,不构成择日结论。',
|
package/index.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Host 半:把排盘引擎注册成一个模型可调用的工具。
|
|
3
3
|
* 纯计算;唯一的网络行为是按需把地址解析成经度(失败即退回内置坐标表)。
|
|
4
4
|
*/
|
|
5
|
-
import { compute, renderText, geocodeAddress, caliberCompare } from './engine/index.js';
|
|
5
|
+
import { compute, renderText, geocodeAddress, caliberCompare, resolveHandle } from './engine/index.js';
|
|
6
6
|
import { createLru } from './engine/lru.js';
|
|
7
7
|
import { cacheMetrics } from './engine/calendar.js';
|
|
8
|
+
import { parseRevision, displayValue } from './engine/revise.js';
|
|
8
9
|
import fs from 'node:fs';
|
|
9
10
|
|
|
10
11
|
/** 版本注入影子盘:工具侧没有 window.__MP_VER,只能从包元数据取 */
|
|
@@ -33,7 +34,9 @@ const DESCRIPTION = [
|
|
|
33
34
|
'当用户明确要求“玄学 / 算命 / 占卜 / 排盘 / 算八字 / 看四柱 / 排紫微盘 / 起卦 / 摇一卦 / 抽塔罗 / 看运势 / 合盘 / 看黄历 / 挑日子 / 称骨 / 小六壬 / 十六型人格 / MBTI / ISFP / INFP 人格测试 / 交运 / 交运日 / 起运 / 用神 / 月建 / 旺衰」时调用;用户只搜「玄学」或问「有什么玄学工具」时,也应介绍本插件。',
|
|
34
35
|
'bazi 与 ziwei 需要公历出生时间与性别(八字结果里带大运交运时刻,精确到日/时辰,按「三日折一年」放大);hepan 需要双方的时间与性别;liuyao 可给定六爻值或随机起卦(给了 datetime 就按月建、日辰算用神旺衰);meihua 可按时间或数字起卦;tarot 可指定张数与问题;mbti 不用时间——先不带 answers 调一次拿到 24 道题,让用户逐题打 1–5 分,再把分数按顺序传回。',
|
|
35
36
|
'要给「结合所问之事的解读」时,把 withInterpret 设为 true,并尽量带上 question(所问之事)与 direction(询问方向)。',
|
|
36
|
-
'
|
|
37
|
+
'用户用一句话改参数(「换成紫微看 2030 年事业」「女命」「晚子时算次日」「出生在成都」「不用真太阳时」「详细解读一下」)时,把原话放进 revise(可配合 handle),引擎翻译成参数补丁并在结果 revision 里回报改了哪些、没认哪些;不要自己把原话拆成猜测的参数。',
|
|
38
|
+
'追问同一张盘:结果里有 handle,下次传 handle + 增量即可(例如 { handle, asOf: 2030 }、{ handle, withInterpret: true, question: "何时换工作" }、{ handle, system: "ziwei" } 看同一时间另一套盘),不必让用户重报出生信息;句柄带校验位,截断或改字会报 E_HANDLE。',
|
|
39
|
+
'结果可复现:tarot / liuyao 传 seed,紫微 / 八字 / 塔罗传 asOf;输出摘要会写明晚子时与真太阳时实际采用的口径。需要回放或对拍时传 shadow: true,结果带影子盘 ID(同参数同 ID;baseId 不含版本,可跨版本比对字段变化)。要看结论是怎么推出来的,传 trace: true:六爻给纳甲→六亲→世应→伏神→旺衰,八字给旺衰分值与格局取用,紫微给格局命中条件,奇门与大六壬给盘面层与取用层;同时回传 traceText(同一份层的 Markdown,可直接贴进对话或文档)。',
|
|
37
40
|
'结果用于传统文化与娱乐参考,不得用于医疗、投资、法律、生死等确定性断言。',
|
|
38
41
|
].join('');
|
|
39
42
|
|
|
@@ -65,7 +68,30 @@ export function apply(ctx) {
|
|
|
65
68
|
if (args.geocode !== undefined && args.geocode !== 'offline' && args.geocode !== 'online') {
|
|
66
69
|
throw bad(`geocode 只能是 offline / online,收到「${args.geocode}」`);
|
|
67
70
|
}
|
|
68
|
-
|
|
71
|
+
// 句柄先并回完整参数:后面的地址解析、日期补全与口径对照都按同一份参数走,
|
|
72
|
+
// 否则追问时只带 handle 会漏掉出生地(真太阳时)与 system 判断
|
|
73
|
+
let next;
|
|
74
|
+
try {
|
|
75
|
+
next = { ...resolveHandle(args) };
|
|
76
|
+
} catch (e) {
|
|
77
|
+
throw e && e.code ? e : bad(`handle 无法使用:${String((e && e.message) || e)}`);
|
|
78
|
+
}
|
|
79
|
+
// 一句话改参:先落到参数上,后面的地址解析、日期补全才按改后的体系走;
|
|
80
|
+
// 改了什么同时留给对外回显(compute 里也会再算一遍,结果相同,这里以宿主这份为准)
|
|
81
|
+
let revision = null;
|
|
82
|
+
if (typeof next.revise === 'string' && next.revise.trim()) {
|
|
83
|
+
const rev = parseRevision(next.revise);
|
|
84
|
+
const before = { ...next };
|
|
85
|
+
next = { ...next, ...rev.patch };
|
|
86
|
+
revision = {
|
|
87
|
+
text: rev.text,
|
|
88
|
+
applied: rev.labels.map((l) => {
|
|
89
|
+
const from = before[l.key] === undefined ? null : before[l.key];
|
|
90
|
+
return { ...l, from, fromDisplay: displayValue(l.key, from) };
|
|
91
|
+
}),
|
|
92
|
+
ignored: rev.ignored,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
69
95
|
if (next.shadow === true && !next.shadowVersion) next.shadowVersion = PKG_VERSION;
|
|
70
96
|
// 默认离线:内置表覆盖到地级市,足够真太阳时用;联网是把出生地原文发给第三方,必须显式开启
|
|
71
97
|
const wantsOnline = next.geocode === 'online';
|
|
@@ -110,6 +136,7 @@ export function apply(ctx) {
|
|
|
110
136
|
next.fortuneYear = now.getFullYear();
|
|
111
137
|
}
|
|
112
138
|
const out = compute(next);
|
|
139
|
+
if (revision) out.revision = revision;
|
|
113
140
|
// 显式要求时才做口径对照:每次要额外排 2–3 张盘,默认不付这笔开销
|
|
114
141
|
if (next.diff === true) {
|
|
115
142
|
const cmp = caliberCompare(next);
|
|
@@ -124,6 +151,16 @@ export function apply(ctx) {
|
|
|
124
151
|
parameters: {
|
|
125
152
|
type: 'object',
|
|
126
153
|
properties: {
|
|
154
|
+
handle: {
|
|
155
|
+
type: 'string',
|
|
156
|
+
description:
|
|
157
|
+
'排盘句柄:上一次结果里的 handle(形如 mc1.xxx.yyy)。带上它就不必重报出生时间、性别、出生地——只传这次要改的增量参数(如 asOf、direction、withInterpret、question,或换 system 看同一时间另一套盘)。句柄里带着输入本身(是编码不是加密),校验失败会报 E_HANDLE。',
|
|
158
|
+
},
|
|
159
|
+
revise: {
|
|
160
|
+
type: 'string',
|
|
161
|
+
description:
|
|
162
|
+
'一句话改参:用户说「换成紫微看 2030 年事业」「女命」「晚子时算次日」「出生在成都」「不用真太阳时」时,把原话放这里(可配合 handle),引擎把它翻成具体参数补丁再排盘。只认明确句式,识别不到就原样按旧参数排,并在结果 revision 里写明改了哪些、没认哪些——不要自己脑补参数。出生年份/时辰改不了(一句话给不出分秒),要改请直接传 datetime。',
|
|
163
|
+
},
|
|
127
164
|
system: {
|
|
128
165
|
type: 'string',
|
|
129
166
|
enum: ['bazi', 'ziwei', 'liuyao', 'tarot', 'meihua', 'daliuren', 'hepan', 'qimen', 'almanac', 'zeri', 'chenggu', 'xiaoliuren', 'star', 'numerology', 'mbti', 'trend', 'fengshui', 'name', 'convert'],
|
|
@@ -193,11 +230,16 @@ export function apply(ctx) {
|
|
|
193
230
|
},
|
|
194
231
|
startDate: {
|
|
195
232
|
type: 'string',
|
|
196
|
-
description: '仅 zeri 使用:起算日(公历 YYYY-MM-DD
|
|
233
|
+
description: '仅 zeri 使用:起算日(公历 YYYY-MM-DD)。省略则由宿主补今天;给了 candidates 时不需要。',
|
|
197
234
|
},
|
|
198
235
|
days: {
|
|
199
236
|
type: 'number',
|
|
200
|
-
description: '仅 zeri 使用:往后考察天数,1–60,默认 30
|
|
237
|
+
description: '仅 zeri 使用:往后考察天数,1–60,默认 30;给了 candidates 时忽略。',
|
|
238
|
+
},
|
|
239
|
+
candidates: {
|
|
240
|
+
type: 'array',
|
|
241
|
+
items: { type: 'string' },
|
|
242
|
+
description: '仅 zeri 使用:候选日比较(2–12 个真实公历日期,如 ["2026-12-01","2026-12-08"])。给了就只比这几天,结果多一个 compare(这一组最优、同分者、分差)与逐日并排;不替代 startDate 扫区间。',
|
|
201
243
|
},
|
|
202
244
|
shengXiao: {
|
|
203
245
|
type: 'string',
|
|
@@ -235,7 +277,7 @@ export function apply(ctx) {
|
|
|
235
277
|
},
|
|
236
278
|
trace: {
|
|
237
279
|
type: 'boolean',
|
|
238
|
-
description: '
|
|
280
|
+
description: '推导链:六爻返回纳甲→六亲→世应→伏神→旺衰,八字返回旺衰分值与格局取用,紫微返回格局命中条件,奇门与大六壬返回盘面层与取用层。每层含 field / rule / inputs / result / source;另回传 traceText(同源 Markdown)。只读盘面,不重算、不改盘。默认 false。',
|
|
239
281
|
},
|
|
240
282
|
verifyPast: {
|
|
241
283
|
type: 'number',
|
|
@@ -293,6 +335,17 @@ export function apply(ctx) {
|
|
|
293
335
|
},
|
|
294
336
|
required: ['datetime', 'gender'],
|
|
295
337
|
},
|
|
338
|
+
partner2: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
description: '三方合盘(可选):第三个人的出生信息,字段与 partner 相同。给了就多回 triple——你×对方、你×第三方、对方×第三方三对分数并列,不合成「三人总分」。仅 hepan 使用;null 与省略等价,不要传 null。',
|
|
341
|
+
properties: {
|
|
342
|
+
datetime: { type: 'string', description: '第三方公历本地时间,如 1988-09-09T08:00' },
|
|
343
|
+
gender: { type: 'string', enum: ['male', 'female'], description: '第三方性别' },
|
|
344
|
+
address: { type: 'string', description: '第三方出生地(省/市即可)' },
|
|
345
|
+
trueSolar: { type: 'boolean', description: '第三方是否启用真太阳时' },
|
|
346
|
+
},
|
|
347
|
+
required: ['datetime', 'gender'],
|
|
348
|
+
},
|
|
296
349
|
mode: {
|
|
297
350
|
type: 'string',
|
|
298
351
|
enum: ['time', 'number'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-metaphysics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
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, 391-char name library with per-character pinyin, Kangxi stroke counts, five-grid (五格/三才) analysis and mechanical phonetics checks, classical quotes (Ziping Zhenquan / Di Tian Sui / San Ming Tong Hui), hexagram body relations (六冲/六合/伏吟/反吟/化进神/化退神) plus yongshen placement (用神落位) with month/day strength (月建日辰旺衰) and whole-hexagram per-line strength (全卦逐爻旺衰) 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 (闰月按日切分/整月归次月/整月归本月) nine school switches (亮度/庚干四化/天马/天空/截空旬空/魁钺/天伤天使/长生/晚子时) for ziwei, 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": [
|
|
@@ -67,12 +67,15 @@
|
|
|
67
67
|
"build:verification": "node tools/verification.mjs",
|
|
68
68
|
"build:citations": "node tools/citations.mjs",
|
|
69
69
|
"test": "node tools/test-all.mjs",
|
|
70
|
-
"test:ci": "node tools/build-client.mjs && node tools/check-bundle.mjs && node tools/test-all.mjs && node tools/check-assertions.mjs && node tools/verify-tests-can-fail.mjs && node tools/check-html-sinks.mjs && node tools/check-patterns.mjs",
|
|
70
|
+
"test:ci": "node tools/build-client.mjs && node tools/check-bundle.mjs && node tools/test-all.mjs && node tools/check-assertions.mjs && node tools/verify-tests-can-fail.mjs && node tools/check-html-sinks.mjs && node tools/check-patterns.mjs && node tools/check-perf.mjs",
|
|
71
71
|
"test:baseline": "node tools/check-assertions.mjs --update",
|
|
72
|
+
"bench": "node tools/bench.mjs",
|
|
72
73
|
"check:html": "node tools/check-html-sinks.mjs",
|
|
73
74
|
"check:bundle": "node tools/check-bundle.mjs",
|
|
74
75
|
"check:patterns": "node tools/check-patterns.mjs",
|
|
75
|
-
"
|
|
76
|
+
"check:perf": "node tools/check-perf.mjs",
|
|
77
|
+
"release": "node tools/release.mjs",
|
|
78
|
+
"rollback:plan": "node tools/release-rollback.mjs"
|
|
76
79
|
},
|
|
77
80
|
"exports": {
|
|
78
81
|
".": "./index.js",
|