dsh-rule-engine 0.6.3 → 0.6.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.
@@ -1,66 +0,0 @@
1
- // health-audit.mjs - 健康审计(找茬,不是证明;2026-08-26)。
2
- // 输出"问题清单"(近 24h),无问题也要列出"查了什么":
3
- // ① 失败/降级类统计:intent-llm(成功/失败降级)、judge-pass/false/unavailable、
4
- // verify-gap、inject-skip、source-skip——失败可见化("LLM 意图 31 次全降级"从此自浮现);
5
- // ② 接口接线交叉:关键导出符号在 lib 下引用计数 ≤1(仅定义,无引用)→ 疑似未接线;
6
- // ③ 与 verify-all 的真实判例口径一致(供人工核对)。
7
- import { readdirSync, statSync, readFileSync } from "node:fs";
8
- import { dirname, join } from "node:path";
9
- import { fileURLToPath } from "node:url";
10
-
11
- const root = join(dirname(fileURLToPath(import.meta.url)), "..");
12
- const auditPath = join(process.env.DSH_HOME || join(process.env.USERPROFILE || "", ".dsh"), "rule-engine.log.jsonl");
13
- const now = Date.now();
14
- const cuts = { intentFail: 0, intentOk: 0, judgePass: 0, judgeFalse: 0, judgeUnavail: 0, verifyGap: 0, injectSkip: 0, sourceSkip: 0, denyTotal: 0, labelIncorrect: 0, labelCorrect: 0, errorHint: 0 };
15
-
16
- try {
17
- for (const line of readFileSync(auditPath, "utf8").split("\n")) {
18
- const m = line.match(/"ts":"([^"]+)"/);
19
- if (!m || now - new Date(m[1]).getTime() > 24 * 3600 * 1000) continue;
20
- if (line.includes('"kind":"intent-llm"')) (line.includes("LLM 失败降级") ? cuts.intentFail++ : cuts.intentOk++);
21
- if (line.includes('"kind":"judge-pass"')) cuts.judgePass++;
22
- if (line.includes('"kind":"judge-false"')) cuts.judgeFalse++;
23
- if (line.includes('"kind":"judge-unavailable"')) cuts.judgeUnavail++;
24
- if (line.includes('"kind":"verify-gap"')) cuts.verifyGap++;
25
- if (line.includes('"kind":"inject-skip"')) cuts.injectSkip++;
26
- if (line.includes('"kind":"source-skip"')) cuts.sourceSkip++;
27
- if (line.includes('"kind":"deny"')) cuts.denyTotal++;
28
- if (line.includes('"kind":"task-label"') && line.includes("incorrect")) cuts.labelIncorrect++;
29
- if (line.includes('"kind":"task-label"') && line.includes("correct")) cuts.labelCorrect++;
30
- if (line.includes('"kind":"error-hint"')) cuts.errorHint++;
31
- }
32
- } catch {
33
- // 无日志文件:一切 0
34
- }
35
-
36
- // ② 接线交叉:导出符号引用计数(>1 = 定义+引用;=1 = 疑似仅定义未接线)
37
- const EXPORTS = [
38
- "setSessionWorkspaceRoot", "setWorkspaceRoots", "isVerificationCommand",
39
- "isNegatingSuggestion", "isPromiseQuoteContext", "judgeViolation",
40
- "shouldDetectTurn", "shouldDeliver", "isReadOnlyCommand"
41
- ];
42
- let libText = "";
43
- (function walk(d) {
44
- for (const f of readdirSync(d)) {
45
- const p = join(d, f);
46
- if (statSync(p).isDirectory()) walk(p);
47
- else if (/\.js$/.test(f)) libText += readFileSync(p, "utf8") + "\n";
48
- }
49
- })(join(root, "lib"));
50
- const orphans = EXPORTS.filter((n) => ((libText.match(new RegExp("\\b" + n + "\\b", "g")) || []).length) <= 1);
51
-
52
- console.log("== 健康审计(近 24h)==");
53
- console.log(`intent-llm 失败降级 ${cuts.intentFail} 条 / 成功 ${cuts.intentOk} 条`);
54
- console.log(`judge-pass ${cuts.judgePass} 条 / judge-false ${cuts.judgeFalse} 条 / judge-unavailable ${cuts.judgeUnavail} 条`);
55
- console.log(`verify-gap ${cuts.verifyGap} 条 / inject-skip ${cuts.injectSkip} 条 / source-skip ${cuts.sourceSkip} 条`);
56
- console.log(`误判打标(建议4):deny 总数 ${cuts.denyTotal} 条 → label incorrect ${cuts.labelIncorrect} 条 / correct ${cuts.labelCorrect} 条${cuts.denyTotal > 0 && cuts.labelIncorrect > 0 ? `(incorrect 占比 ${(cuts.labelIncorrect / cuts.denyTotal * 100).toFixed(1)}%)` : ""}——词表迭代量化依据`);
57
- console.log(`已知坑召回(建议5):error-hint ${cuts.errorHint} 条`);
58
- console.log(`接线交叉:${EXPORTS.length} 个关键导出 → 疑似未接线 ${orphans.length ? orphans.join("、") : "0 个"}`);
59
- if (cuts.judgePass + cuts.judgeFalse === 0) {
60
- console.log("⚠️ 问题:近 24h 无真实判例——裁决器无运行证据(需实弹)");
61
- }
62
- if (cuts.intentFail > 0) {
63
- console.log(`⚠️ 问题:intent-llm 失败降级 ${cuts.intentFail} 条——LLM 意图判定不可用(需查模型路由/密钥)`);
64
- }
65
- console.log(orphans.length ? `❌ 问题:疑似未接线:${orphans.join("、")}` : `✅ 接线:全部导出有引用`);
66
- console.log("(已查:失败统计 8 类 + 接线交叉 9 个导出;无问题也列出如上)");
@@ -1,56 +0,0 @@
1
- // pnpm-exempt.mjs — pnpm-workspace minimumReleaseAgeExclude 豁免判定(verify-all ⑬ 与 release-plugin 预插共享单源)
2
- // 约定(exempt 语义):行含包名(`pkg@`)且版本段任一匹配——yaml 支持 `- pkg@1.5.3 || 1.5.4` 形态(段可带/不带包名前缀)
3
- import { readFileSync, writeFileSync } from "node:fs";
4
-
5
- export function exemptPkg(wsYaml, pkg, ver) {
6
- if (typeof wsYaml !== "string" || wsYaml.length === 0) return false;
7
- return wsYaml.split(/\r?\n/).some((l) => l.includes(`${pkg}@`) && l.split(/\s*\|\|\s*/).some((tok) => {
8
- const t = tok.trim().replace(/^-\s*/, "");
9
- return t === ver || t === `${pkg}@${ver}`;
10
- }));
11
- }
12
-
13
- /** 追加豁免行(已存在=幂等跳过;返回 {yaml, added}) */
14
- export function addExemptLine(wsYaml, pkg, ver) {
15
- if (exemptPkg(wsYaml, pkg, ver)) return { yaml: wsYaml, added: false };
16
- const sep = wsYaml.endsWith("\n") ? "" : "\n";
17
- return { yaml: `${wsYaml}${sep} - ${pkg}@${ver}\n`, added: true };
18
- }
19
-
20
- /** 读 yaml(fail-closed:读取失败抛错,调用方中止发布) */
21
- export function readYamlOrThrow(path) {
22
- try {
23
- const text = readFileSync(path, "utf8");
24
- if (!text.includes("minimumReleaseAgeExclude")) throw new Error(`yaml 缺 minimumReleaseAgeExclude 块:${path}`);
25
- return text;
26
- } catch (e) {
27
- if (e.code === "ENOENT") throw new Error(`yaml 不存在(fail-closed):${path}`);
28
- throw e;
29
- }
30
- }
31
-
32
- /**
33
- * 预插核心(release-plugin bump 后 publish 前调用;单测可注入 dryRun/write/log):
34
- * 已存在=幂等跳过;yaml 缺失/坏=抛错(fail-closed);dryRun=只打印不写。
35
- */
36
- export function ensureExempt(yamlPath, pkgName, ver, opts = {}) {
37
- const { dryRun = false, write = (p, c) => writeFileSync(p, c, "utf8"), log = console.log } = opts;
38
- let yaml;
39
- try {
40
- yaml = readYamlOrThrow(yamlPath);
41
- } catch (e) {
42
- throw new Error(`豁免预插失败(fail-closed 中止):${e.message}`);
43
- }
44
- const { yaml: nextYaml, added } = addExemptLine(yaml, pkgName, ver);
45
- if (dryRun) {
46
- log(`[DRY-RUN] 豁免预插:${added ? `将追加 ${pkgName}@${ver}` : `已存在(幂等跳过)`}`);
47
- return { added, dryRun: true };
48
- }
49
- if (added) {
50
- write(yamlPath, nextYaml);
51
- log(`豁免预插:+ ${pkgName}@${ver}`);
52
- } else {
53
- log(`豁免预插:${pkgName}@${ver} 已存在(幂等跳过)`);
54
- }
55
- return { added };
56
- }
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- // local-residue-scan.mjs — 发布物本机痕迹扫描(仅扫 lib/;无 --pack 模式)
3
- // 用法:node scripts/local-residue-scan.mjs (在包根目录运行;exit 0 = 干净,exit 1 = 有命中)
4
-
5
- import { readdirSync, statSync, readFileSync } from "node:fs";
6
- import { join, extname } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { dirname } from "node:path";
9
-
10
- const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
-
12
- // 词表唯一源(2026-09-09 起配置化):本机 rule-engine.json 的 dualtrack.markers 优先,
13
- // 其次 DUALTRACK_MARKERS 环境变量,最后回退包内示例文件——见 lib/core/dualtrack-markers.js。
14
- // 理由:词表是**发布者私有数据**,不应随公开仓库/包分发。
15
- import { loadMarkers } from "../lib/core/dualtrack-markers.js";
16
- const { markers: MARKERS, source: MARKERS_SOURCE } = loadMarkers({ root });
17
- if (MARKERS.length === 0) {
18
- console.error("REFUSED: 本机标识词表为空——请在 rule-engine.json 配置 dualtrack.markers,或设置 DUALTRACK_MARKERS 环境变量");
19
- process.exit(1);
20
- }
21
- // 注意:不扫 package.json(作者署名/仓库地址为合法项,A6 口径见其节);tool-catalog.js 的
22
- // dev_/esr_/engram_ 前缀规则按阶段 A「关键裁决 3」保留,静态枚举列 0.6.x 跟进。
23
-
24
- const TEXT_EXT = new Set([".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
25
- function* walk(dir) {
26
- for (const e of readdirSync(dir)) {
27
- const p = join(dir, e);
28
- const s = statSync(p);
29
- if (s.isDirectory()) { if (e !== "node_modules" && e !== ".git") yield* walk(p); }
30
- else if (TEXT_EXT.has(extname(e))) yield p;
31
- }
32
- }
33
-
34
- let hits = 0;
35
- for (const file of walk(join(root, "lib"))) {
36
- const text = readFileSync(file, "utf8");
37
- for (const m of MARKERS) {
38
- const lines = text.split("\n");
39
- lines.forEach((line, i) => {
40
- if (line.includes(m)) { console.log(`HIT ${file}:${i + 1} [${m}] ${line.trim()}`); hits++; }
41
- });
42
- }
43
- }
44
-
45
- if (hits) { console.error(`\nRESIDUE SCAN FAILED(${hits} 处本机痕迹)`); process.exit(1); }
46
- console.log("RESIDUE SCAN OK(发布面零本机痕迹)");
@@ -1,10 +0,0 @@
1
- {
2
- "profile": "web",
3
- "plugins": [
4
- {
5
- "name": "dsh-rule-engine",
6
- "dir": ".",
7
- "repo": "jilian-dsh/dsh-rule-engine"
8
- }
9
- ]
10
- }
@@ -1,3 +0,0 @@
1
- // probe-home.mjs - 实测 resolveDshHome 解析结果(同 host 用的 @deepseek-ai/dsh-home-paths)
2
- import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
3
- console.log("resolveDshHome():", resolveDshHome());
@@ -1,14 +0,0 @@
1
- // probe-intent.mjs - 测"落"字为何被判问句(用户明确执行却拦)
2
- import { parseUserIntents, shouldDenyMutation } from "../lib/core/intent.js";
3
- const texts = [
4
- "2、连同上面这条一起落",
5
- "连上面这条一起落",
6
- "落",
7
- "全部一起落",
8
- "认可全部link不装npm形态",
9
- "1、认可形态 2、连上面这条一起落"
10
- ];
11
- for (const t of texts) {
12
- const r = parseUserIntents(t);
13
- console.log(`"${t}" → hasExecute=${r.hasExecute} hasQuestion=${r.hasQuestion} types=${r.clauses.map((c) => c.type).join(",")}`);
14
- }
@@ -1,102 +0,0 @@
1
- // publish-aptitude-check.mjs — 发布适用性门禁(T6,2026-08-31)
2
- // 目的:兑现 README「通用化」承诺的"陌生人视角"检查——发布物必须先证明:
3
- // ① 无 AGENTS.md(陌生环境冷启动):引擎零错 / 零死映射 / 零意外拦截面
4
- // ② 空白规则文件:静默(0 规则、无错、无 dead)
5
- // ③ 任意编号规则(非本机编号):声明式绑定与"纯自证"分流行为正确
6
- // ④ 四要素 + 执行等级格式的最小规则:actions/handler 解析正确(README 格式契约冒烟)
7
- // ⑤ 发布物个人标识扫描:files 白名单内文件不得含本机个人标识(本地用户名/真实路径/邮箱/私人注释)
8
- // 默认模式 = 单进程纯函数链(不 spawn、不 import index.js,沙箱可直接跑);
9
- // --deep 模式 = 额外子进程全链冷启动(临时 DSH_HOME + 完整引擎加载 + 真实裁决链,需完整权限/CI)。
10
- // 用法:node scripts/publish-aptitude-check.mjs [--deep] [--tgz <路径>]
11
- import { mkdtempSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, existsSync } from "node:fs";
12
- import { tmpdir } from "node:os";
13
- import { join, dirname, resolve } from "node:path";
14
- import { fileURLToPath } from "node:url";
15
-
16
- const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
- const args = process.argv.slice(2);
18
- const deep = args.includes("--deep");
19
- const tgzArg = args.includes("--tgz") ? args[args.indexOf("--tgz") + 1] : null;
20
-
21
- const FAILS = [];
22
- function check(ok, msg) {
23
- console.log(`${ok ? "✅" : "❌"} ${msg}`);
24
- if (!ok) FAILS.push(msg);
25
- }
26
-
27
- // ①-④ 使用的纯函数链(与引擎 lib 同源,动态 import 保证读到本次代码)
28
- const { loadRules } = await import("../lib/core/parser.js");
29
- const { understandAll, understandRule, analyzeCoverage, defaultHandlerMapSize } = await import("../lib/core/understander.js");
30
-
31
- // ── ① 陌生环境冷启动(临时 DSH_HOME,无 AGENTS.md)──
32
- const sceneDir = mkdtempSync(join(tmpdir(), "aptitude-check-"));
33
- process.env.DSH_HOME = sceneDir;
34
- process.env.DSH_WORKSPACE = root;
35
- {
36
- const parsed = loadRules();
37
- check(parsed.ok === false && parsed.missing === true, `① 无 AGENTS.md:处理器返回 missing(ok=${parsed.ok}, missing=${parsed.missing})`);
38
- const configs = understandAll([]);
39
- check(configs.length === 0, "① 无 AGENTS.md:理解产物为空(不产生任何规则)");
40
- const { dead, uncovered } = analyzeCoverage(configs);
41
- // 残余1 剥离后:代码层无默认表(通用部署空表)→ dead=0;关键断言 = 无规则被声称机器执行(uncovered=0)且冷启动零报错
42
- check(dead.size === 0, `① 无 AGENTS.md:dead=0(代码层无默认偏好表)`);
43
- check(uncovered.length === 0, `① 无 AGENTS.md:零未覆盖(uncovered=0)`);
44
- }
45
-
46
- // ── ② 空白规则文件静默 ──
47
- {
48
- writeFileSync(join(sceneDir, "AGENTS.md"), "", "utf8");
49
- const parsed = loadRules();
50
- check(parsed.ok === true && parsed.rules.length === 0, `② 空白 AGENTS.md:解析成功且 0 规则(rules=${parsed.rules.length})`);
51
- const configs = understandAll(parsed.rules);
52
- check(configs.length === 0, "② 空白 AGENTS.md:理解产物为空");
53
- }
54
-
55
- // ── ③ 任意编号规则(非本机编号):声明式绑定 + 纯自证分流 ──
56
- {
57
- const arbitrary = {
58
- index: "X-77",
59
- title: "陌生用户规则(执行等级:A+D)",
60
- section: "自定义分区",
61
- level: "A+D",
62
- body: "- **触发**:使用内联命令。\n- **检查**:拦 node -e / pwsh -c。\n- **动作**:拒绝。\n- **豁免**:无。"
63
- };
64
- const plain = understandRule(arbitrary);
65
- check(plain.handler === "", "③ 任意编号 + 无声明:handler 为空(纯自证分流,不误绑定本机执行器)");
66
- check(plain.actions.includes("deny") && plain.actions.includes("self-certify"), "③ 任意编号:A+D 动作解析正确(deny+self-certify)");
67
- const declared = understandRule({ ...arbitrary, body: arbitrary.body + "\n<!-- handler: rule12a-approval -->" });
68
- check(declared.handler === "rule12a-approval", "③ 任意编号 + 声明:绑定指定执行器");
69
- const covered = analyzeCoverage(understandAll([arbitrary]));
70
- check(covered.uncovered.some((u) => u.ruleId === "X-77"), "③ 任意编号 A 级无 handler:被标记未覆盖(提示正确)");
71
- // 通用化闭环:defaultMap 清空 → 陌生规则无兜底绑定(纯声明式世界,不借道本机偏好表);
72
- // 注:analyzeCoverage 自省对象恒为本机默认表(体检对象),不受 defaultMap 影响。
73
- const bare = understandRule(arbitrary, { defaultMap: {} });
74
- check(bare.handler === "", "③ defaultMap 清空:陌生规则无兜底绑定(纯声明式世界)");
75
- }
76
-
77
- // ── ④ 四要素 + 执行等级格式契约冒烟(README 格式)──
78
- {
79
- const elem = understandRule({
80
- index: "E-1",
81
- title: "格式样例(执行等级:B + D)",
82
- section: "样例分区",
83
- level: "B + D",
84
- body: "- **触发**:触发条件。\n- **检查**:检查项。\n- **动作**:动作说明。\n- **豁免**:豁免说明。"
85
- });
86
- check(elem.elements.trigger.length > 0 && elem.elements.check.length > 0 && elem.elements.action.length > 0 && elem.elements.exemption.length > 0, "④ 四要素提取完整");
87
- check(elem.actions.includes("correct") && elem.actions.includes("self-certify"), "④ 组合等级 B + D 动作解析正确");
88
- check(elem.confidence === "high", "④ 四要素齐全 → 置信 high");
89
- }
90
-
91
- // ── ⑤(2026-09-04 本机化:存在性判据 + 个人词表扫描移入本机发行工具
92
- // scripts/scan-real-paths.mjs(判据库 scripts/lib/realpath-guard.js),
93
- // 本文件只保留 ①-④ 通用发布适用性检查;发布前由本机 release-gate 统一执行门禁)──
94
-
95
- // ── --deep:完整引擎链冷启动(子进程,需完整权限/CI;沙箱不可用)──
96
- if (deep) {
97
- console.log("\n(--deep 模式:请确认在完整权限/CI 环境运行——子进程捕获受沙箱限制)");
98
- }
99
-
100
- rmSync(sceneDir, { recursive: true, force: true });
101
- console.log(`\n${FAILS.length === 0 ? "ADEPTITUDE CHECK PASS" : `ADEPTITUDE CHECK FAIL(${FAILS.length} 项)`}`);
102
- process.exit(FAILS.length === 0 ? 0 : 1);
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env node
2
- // readme-version-check.mjs — README 版本四性一致性门禁
3
- // 检查:package.json version == README 徽章 == README 正文"当前版本 X" == 版本历史表含当前版本 == 发行固定源锚定当前版本
4
- // 用法:node scripts/readme-version-check.mjs (在包根目录运行;exit 0 = 一致,exit 1 = 不一致)
5
-
6
- import { readFileSync } from "node:fs";
7
- import { fileURLToPath } from "node:url";
8
- import { dirname, join } from "node:path";
9
-
10
- const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
- const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
12
- const readme = readFileSync(join(root, "README.md"), "utf8");
13
- const ver = pkg.version;
14
-
15
- let failures = [];
16
- const ok = (cond, label, detail) => {
17
- if (cond) console.log(`PASS ${label}`);
18
- else { failures.push(label); console.log(`FAIL ${label} — ${detail}`); }
19
- };
20
-
21
- // 1. 徽章:badge/version-X.Y.Z
22
- const badge = readme.match(/badge\/version-(\d+\.\d+\.\d+)/);
23
- ok(badge?.[1] === ver, "徽章=package.json", `徽章=${badge?.[1] ?? "未找到"} vs package=${ver}`);
24
-
25
- // 2. 正文"当前版本 X"(允许"当前版本 **X**")
26
- const cur = readme.match(/当前版本[^\d]{0,10}(\d+\.\d+\.\d+)/);
27
- ok(cur?.[1] === ver, "正文当前版本=package.json", `正文=${cur?.[1] ?? "未找到"} vs package=${ver}`);
28
-
29
- // 3. 版本历史表包含当前版本行:| **X.Y.Z** | 或 | X.Y.Z |
30
- const tableHit = new RegExp(`^\\|\\s*\\*{0,2}${ver.replace(/\./g, "\\.")}\\*{0,2}\\s*\\|`, "m").test(readme);
31
- ok(tableHit, "版本历史表含当前版本行", `未找到 | ${ver} | 行`);
32
-
33
- // 4. 发行固定源锚定当前版本:"**X(当前)**" 或 "X.Y.Z(当前)"
34
- const pin = readme.match(/\*\*(\d+\.\d+\.\d+)(当前)\*\*/);
35
- ok(pin?.[1] === ver, "发行固定源=package.json", `固定源=${pin?.[1] ?? "未找到"} vs package=${ver}`);
36
-
37
- if (failures.length) {
38
- console.error(`\nVERSION CHECK FAILED(${failures.length} 项不一致)`);
39
- process.exit(1);
40
- }
41
- console.log(`\nVERSION CHECK OK(${ver} 四处一致)`);