oh-my-knowledge 0.45.0 → 0.47.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/README.md +18 -4
- package/README.zh.md +18 -4
- package/dist/assets/agent-skills/omk/SKILL.md +3 -3
- package/dist/assets/agent-skills/omk/references/commands.md +13 -6
- package/dist/cli/commands/doctor.d.ts +3 -2
- package/dist/cli/commands/doctor.js +68 -64
- package/dist/cli/commands/eval/index.js +8 -0
- package/dist/cli/lib/cmd-flags.d.ts +3 -2
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +0 -4
- package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/run.js +4 -0
- package/dist/doctor/endpoint-rule.js +1 -1
- package/dist/doctor/health/composer.js +162 -50
- package/dist/doctor/health/consensus.d.ts +43 -0
- package/dist/doctor/health/consensus.js +224 -0
- package/dist/doctor/health/dimension-spec.d.ts +6 -0
- package/dist/doctor/health/register.d.ts +0 -1
- package/dist/doctor/health/register.js +0 -1
- package/dist/doctor/index.js +3 -0
- package/dist/doctor/messages.d.ts +1 -1
- package/dist/doctor/messages.js +8 -0
- package/dist/doctor/renderer.d.ts +1 -0
- package/dist/doctor/renderer.js +63 -0
- package/dist/doctor/rules.d.ts +4 -2
- package/dist/doctor/rules.js +4 -2
- package/dist/eval-core/verdict.d.ts +2 -2
- package/dist/eval-core/verdict.js +36 -2
- package/dist/eval-workflows/messages.js +2 -2
- package/dist/eval-workflows/run-evaluation.js +1 -1
- package/dist/executors/claude-cli.js +1 -1
- package/dist/observability/skill-chain-advisories.js +2 -2
- package/dist/renderer/doctor-detail-renderer.js +30 -2
- package/dist/renderer/report-shell.d.ts +1 -1
- package/dist/renderer/report-shell.js +5 -0
- package/dist/renderer/skill-detail-renderer.js +844 -100
- package/dist/server/skill-index.js +37 -1
- package/dist/shared/llm-prompts/registry.js +2 -1
- package/dist/shared/llm-prompts/skill-health-merge.d.ts +22 -0
- package/dist/shared/llm-prompts/skill-health-merge.js +79 -0
- package/dist/shared/llm-prompts/skill-health.js +1 -1
- package/dist/types/doctor.d.ts +32 -8
- package/dist/types/skill-index.d.ts +10 -0
- package/package.json +6 -5
package/dist/doctor/renderer.js
CHANGED
|
@@ -43,6 +43,68 @@ function renderRuleLine(result, ruleLabel, lang, indent = ' ') {
|
|
|
43
43
|
}
|
|
44
44
|
return line;
|
|
45
45
|
}
|
|
46
|
+
function compactActionText(text) {
|
|
47
|
+
return (text ?? '').replace(/\s+/g, ' ').trim();
|
|
48
|
+
}
|
|
49
|
+
function collectDoctorRepairItems(report, lang) {
|
|
50
|
+
const allItems = [];
|
|
51
|
+
for (const skill of report.skills) {
|
|
52
|
+
for (const result of skill.results) {
|
|
53
|
+
if (result.status !== 'fail' && result.status !== 'warn')
|
|
54
|
+
continue;
|
|
55
|
+
const action = compactActionText(result.hint || result.message);
|
|
56
|
+
allItems.push({
|
|
57
|
+
skillName: skill.skillName,
|
|
58
|
+
status: result.status,
|
|
59
|
+
label: renderRuleLabel(result, lang),
|
|
60
|
+
action,
|
|
61
|
+
isSummary: result.ruleId.endsWith(':_summary'),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const focusedItems = allItems.filter((item) => !item.isSummary);
|
|
66
|
+
const source = focusedItems.length > 0 ? focusedItems : allItems;
|
|
67
|
+
const sorted = [...source].sort((a, b) => STATUS_RANK[a.status] - STATUS_RANK[b.status]);
|
|
68
|
+
return { items: sorted.slice(0, 6), total: source.length };
|
|
69
|
+
}
|
|
70
|
+
export function renderDoctorActionPlanText(report, lang) {
|
|
71
|
+
const { items, total } = collectDoctorRepairItems(report, lang);
|
|
72
|
+
if (items.length === 0) {
|
|
73
|
+
return lang === 'zh'
|
|
74
|
+
? '\n下一步:doctor 已通过,可以继续运行 `omk eval`。\n'
|
|
75
|
+
: '\nNext: doctor passed; continue with `omk eval`.\n';
|
|
76
|
+
}
|
|
77
|
+
const hasFail = items.some((item) => item.status === 'fail');
|
|
78
|
+
const lines = [
|
|
79
|
+
'',
|
|
80
|
+
lang === 'zh'
|
|
81
|
+
? (hasFail ? '修复清单(先处理阻塞项):' : '修复清单(建议处理,不阻断 eval):')
|
|
82
|
+
: (hasFail ? 'Repair checklist (fix blocking items first):' : 'Repair checklist (recommended; does not block eval):'),
|
|
83
|
+
];
|
|
84
|
+
items.forEach((item, index) => {
|
|
85
|
+
const status = statusLabel(item.status, lang);
|
|
86
|
+
if (lang === 'zh') {
|
|
87
|
+
lines.push(` ${index + 1}. [${item.skillName}] ${status}:${item.label}。${item.action}`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
lines.push(` ${index + 1}. [${item.skillName}] ${status}: ${item.label}. ${item.action}`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const remaining = total - items.length;
|
|
94
|
+
if (remaining > 0) {
|
|
95
|
+
lines.push(lang === 'zh'
|
|
96
|
+
? ` ……还有 ${remaining} 项,运行 \`omk doctor\` 查看完整明细。`
|
|
97
|
+
: ` ...and ${remaining} more; run \`omk doctor\` for full detail.`);
|
|
98
|
+
}
|
|
99
|
+
lines.push(hasFail
|
|
100
|
+
? (lang === 'zh'
|
|
101
|
+
? '下一步:先修阻塞项,重跑 `omk doctor --gate`;通过后再跑 `omk eval`。'
|
|
102
|
+
: 'Next: fix the blocking items, re-run `omk doctor --gate`, then run `omk eval`.')
|
|
103
|
+
: (lang === 'zh'
|
|
104
|
+
? '下一步:可以先跑 `omk eval`,但发布前建议把这些 warning 处理掉。'
|
|
105
|
+
: 'Next: you can run `omk eval`, but clear these warnings before shipping.'));
|
|
106
|
+
return `${lines.join('\n')}\n`;
|
|
107
|
+
}
|
|
46
108
|
function segmentResults(results) {
|
|
47
109
|
const segs = [];
|
|
48
110
|
const groupSegMap = new Map();
|
|
@@ -115,6 +177,7 @@ export function renderDoctorReportText(report, lang, write = (s) => process.stde
|
|
|
115
177
|
? `\n总览: ${report.totals.pass} 通过 / ${report.totals.warn} 警告 / ${report.totals.fail} 失败\n`
|
|
116
178
|
: `\nSummary: ${report.totals.pass} pass / ${report.totals.warn} warn / ${report.totals.fail} fail\n`;
|
|
117
179
|
write(summary);
|
|
180
|
+
write(renderDoctorActionPlanText(report, lang));
|
|
118
181
|
}
|
|
119
182
|
export function renderDoctorReportJson(report) {
|
|
120
183
|
return JSON.stringify(report, null, 2);
|
package/dist/doctor/rules.d.ts
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* omk doctor 内置规则注册表。
|
|
3
3
|
*
|
|
4
4
|
* BUILTIN_RULES 是**纯静态/低成本检查**, 不碰 LLM 连通性 — executor / judge
|
|
5
|
-
* 连通性由 evaluation preflight 负责。用户入口 `omk doctor`
|
|
6
|
-
* skill_health composer 做 LLM 审计;`--static-only`
|
|
5
|
+
* 连通性由 evaluation preflight 负责。用户入口 `omk doctor` 默认会跑这些静态
|
|
6
|
+
* 规则,再跑 skill_health composer 做 LLM 审计;`--static-only` 只跑静态规则。
|
|
7
|
+
* samples_contract_aligned 需要 samples,仍只归 eval preflight
|
|
8
|
+
* (run-evaluation.ts 经 runDoctor 调,dependencies_present 守依赖完整性)。
|
|
7
9
|
*
|
|
8
10
|
* 每条 rule 回答一个独立的「skill 能不能被有意义评测」子问题:
|
|
9
11
|
* - skill_readable: 文件能读、内容非空且有最小长度
|
package/dist/doctor/rules.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* omk doctor 内置规则注册表。
|
|
3
3
|
*
|
|
4
4
|
* BUILTIN_RULES 是**纯静态/低成本检查**, 不碰 LLM 连通性 — executor / judge
|
|
5
|
-
* 连通性由 evaluation preflight 负责。用户入口 `omk doctor`
|
|
6
|
-
* skill_health composer 做 LLM 审计;`--static-only`
|
|
5
|
+
* 连通性由 evaluation preflight 负责。用户入口 `omk doctor` 默认会跑这些静态
|
|
6
|
+
* 规则,再跑 skill_health composer 做 LLM 审计;`--static-only` 只跑静态规则。
|
|
7
|
+
* samples_contract_aligned 需要 samples,仍只归 eval preflight
|
|
8
|
+
* (run-evaluation.ts 经 runDoctor 调,dependencies_present 守依赖完整性)。
|
|
7
9
|
*
|
|
8
10
|
* 每条 rule 回答一个独立的「skill 能不能被有意义评测」子问题:
|
|
9
11
|
* - skill_readable: 文件能读、内容非空且有最小长度
|
|
@@ -166,8 +166,8 @@ export declare function medianStabilityCV(report: Report): {
|
|
|
166
166
|
cv: number;
|
|
167
167
|
} | null;
|
|
168
168
|
/**
|
|
169
|
-
* Plain-text formatter for the `omk eval` verdict. Stays
|
|
170
|
-
* spec — one verdict
|
|
169
|
+
* Plain-text formatter for the `omk eval` verdict. Stays terse for the
|
|
170
|
+
* spec — one verdict, rationale bullets, one ship recommendation, and one next step.
|
|
171
171
|
*/
|
|
172
172
|
export declare function formatVerdictText(result: VerdictResult, options?: {
|
|
173
173
|
verbose?: boolean;
|
|
@@ -108,6 +108,7 @@ export function computeVerdict(report, options = {}) {
|
|
|
108
108
|
...(judgeInd.rationale ? { judgeAgreement: judgeInd.rationale } : {}),
|
|
109
109
|
...(overfit.rationale ? { overfitting: overfit.rationale } : {}),
|
|
110
110
|
...(gap.rationale ? { gapSignal: gap.rationale } : {}),
|
|
111
|
+
shipRecommendation: recommendation('SOLO', []),
|
|
111
112
|
},
|
|
112
113
|
...((overfit.data || gap.data) ? {
|
|
113
114
|
caveats: {
|
|
@@ -583,9 +584,41 @@ function recommendation(level, _perPair, lang = 'en') {
|
|
|
583
584
|
return 'ADD A CONTROL — single-variant report. Re-run with --control baseline --treatment <name>.';
|
|
584
585
|
}
|
|
585
586
|
}
|
|
587
|
+
function releaseNextStep(level, lang) {
|
|
588
|
+
if (lang === 'zh') {
|
|
589
|
+
switch (level) {
|
|
590
|
+
case 'PROGRESS':
|
|
591
|
+
return '可以进入发布流程:请留存本次报告作为发布证据;如果这是受管 skill,再运行 `omk promote`。';
|
|
592
|
+
case 'CAUTIOUS':
|
|
593
|
+
return '先看触发的告警(分层门控、评委分歧、稳定性或 holdout),修完再重跑。';
|
|
594
|
+
case 'REGRESS':
|
|
595
|
+
return '不要发布;定位最差层和失败用例,修复后重跑。';
|
|
596
|
+
case 'NOISE':
|
|
597
|
+
return '先别发布;增加样本数或提高用例区分度,再重跑。';
|
|
598
|
+
case 'UNDERPOWERED':
|
|
599
|
+
return '把样本数加到至少 20,或先按当前规模 2× 扩充后重跑。';
|
|
600
|
+
case 'SOLO':
|
|
601
|
+
return '补一个 baseline 对照,再跑 `omk eval --control baseline --treatment <名字>`。';
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
switch (level) {
|
|
605
|
+
case 'PROGRESS':
|
|
606
|
+
return 'ready for release: keep this report as release evidence; for a managed skill, run `omk promote`.';
|
|
607
|
+
case 'CAUTIOUS':
|
|
608
|
+
return 'inspect the warnings (layer gates, judge dissent, stability, or holdout), fix them, then re-run.';
|
|
609
|
+
case 'REGRESS':
|
|
610
|
+
return 'do not ship; inspect the weakest layer and failing samples, fix them, then re-run.';
|
|
611
|
+
case 'NOISE':
|
|
612
|
+
return 'do not ship yet; add samples or sharpen the test set, then re-run.';
|
|
613
|
+
case 'UNDERPOWERED':
|
|
614
|
+
return 'increase the sample set to at least 20, or roughly 2x the current size, then re-run.';
|
|
615
|
+
case 'SOLO':
|
|
616
|
+
return 'add a baseline control and re-run `omk eval --control baseline --treatment <name>`.';
|
|
617
|
+
}
|
|
618
|
+
}
|
|
586
619
|
/**
|
|
587
|
-
* Plain-text formatter for the `omk eval` verdict. Stays
|
|
588
|
-
* spec — one verdict
|
|
620
|
+
* Plain-text formatter for the `omk eval` verdict. Stays terse for the
|
|
621
|
+
* spec — one verdict, rationale bullets, one ship recommendation, and one next step.
|
|
589
622
|
*/
|
|
590
623
|
export function formatVerdictText(result, options = {}) {
|
|
591
624
|
// lang 默认 'en':保留既有英文输出逐字节不变(verdict.test 与历史 CLI 行为)。zh 只本地化
|
|
@@ -609,6 +642,7 @@ export function formatVerdictText(result, options = {}) {
|
|
|
609
642
|
lines.push(zh ? ` 知识缺口:${result.rationale.gapSignal}` : ` Gap signal: ${result.rationale.gapSignal}`);
|
|
610
643
|
if (result.rationale.shipRecommendation) {
|
|
611
644
|
lines.push(` ${zh ? recommendation(result.level, [], 'zh') : result.rationale.shipRecommendation}`);
|
|
645
|
+
lines.push(zh ? ` 下一步:${releaseNextStep(result.level, 'zh')}` : ` Next: ${releaseNextStep(result.level, 'en')}`);
|
|
612
646
|
}
|
|
613
647
|
if (options.verbose && result.perPair && result.perPair.length > 1) {
|
|
614
648
|
lines.push('');
|
|
@@ -12,8 +12,8 @@ const MESSAGES = {
|
|
|
12
12
|
en: '⚠ --repeat=1: single-run cannot measure stability (CV will be marked "not measured"). Use --repeat 3+ to detect within-variant variance.',
|
|
13
13
|
},
|
|
14
14
|
doctor_gate_blocked: {
|
|
15
|
-
zh: '
|
|
16
|
-
en: '
|
|
15
|
+
zh: '发布前 doctor 门禁未通过,评测已中止。\n下一步:先修复上面的阻塞项,再重跑 `omk eval`。\n原因:这段输入还不值得测,继续比较分数会是 garbage-in。\n如果依赖确实由 mock / stub 提供、doctor 误报,可用 `--skip-doctor` 绕过,但这次结果由你承担不可比风险。',
|
|
16
|
+
en: 'pre-ship doctor gate failed; evaluation aborted.\nNext: fix the blocking findings above, then re-run `omk eval`.\nWhy: this input is not measurable enough yet, and comparing scores now would be garbage-in.\nIf deps are truly supplied by mocks/stubs and doctor is a false positive, use `--skip-doctor`, but you own the comparability risk.',
|
|
17
17
|
},
|
|
18
18
|
};
|
|
19
19
|
const DEFAULT_LANG = 'zh';
|
|
@@ -62,7 +62,7 @@ export async function runEvaluation({ samplesPath, skillDir, variantSpecs = [],
|
|
|
62
62
|
});
|
|
63
63
|
if (doctorReport.outcome === 'failed') {
|
|
64
64
|
renderDoctorReportText(doctorReport, lang);
|
|
65
|
-
throw new Error(`doctor failed
|
|
65
|
+
throw new Error(`doctor failed:\n${tEvalWorkflowMessage('doctor_gate_blocked', lang)}`);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -40,7 +40,7 @@ export async function claudeCliExecutor({ model, system, prompt, cwd, skillDir,
|
|
|
40
40
|
if (system)
|
|
41
41
|
args.push('--system-prompt', system);
|
|
42
42
|
// effort 决策:
|
|
43
|
-
// - lean=true(纯文本生成):默认 'low',但允许调用方显式覆盖(如 doctor
|
|
43
|
+
// - lean=true(纯文本生成):默认 'low',但允许调用方显式覆盖(如 doctor 用 --effort 调高)。
|
|
44
44
|
// - 否则用调用方传入的 effort,或 sonnet 默认(不传 flag = claude CLI 自己定)。
|
|
45
45
|
const effectiveEffort = lean ? (effort ?? 'low') : effort;
|
|
46
46
|
if (lean) {
|
|
@@ -49,8 +49,8 @@ const ADVISORIES = {
|
|
|
49
49
|
skill_md_not_found: {
|
|
50
50
|
code: 'skill_md_not_found',
|
|
51
51
|
shortLabel: '本地未找到 SKILL.md',
|
|
52
|
-
message: '本地常见 skill 目录里没有这个 skill 的 SKILL.md,inbox
|
|
53
|
-
commandTemplate: 'omk doctor ~/.claude/skills/${skillName}/SKILL.md
|
|
52
|
+
message: '本地常见 skill 目录里没有这个 skill 的 SKILL.md,inbox 跑不了静态/运行时审计。如果你想看 LLM 健康度审计(触发边界 / 文档 / 依赖 / 安全等 7 维),可以试试 omk doctor;inbox 本身不依赖它。',
|
|
53
|
+
commandTemplate: 'omk doctor ~/.claude/skills/${skillName}/SKILL.md',
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
export function getSkillChainAdvisory(code) {
|
|
@@ -46,8 +46,18 @@ function renderRuleCard(r, lang, forceOpen = false) {
|
|
|
46
46
|
const mIcon = isErr
|
|
47
47
|
? svgIcon('x', { size: 14, style: 'color:#dc2626;vertical-align:-2px;margin-right:5px' })
|
|
48
48
|
: svgIcon('warn', { size: 14, style: `color:${f.level === '警告' ? '#d97706' : '#637083'};vertical-align:-2px;margin-right:5px` });
|
|
49
|
+
// 多采样支持度 k/n:仅 n>1 时展示(单次采样无意义)。k<n 用偏灰提示"非每次都报"。
|
|
50
|
+
const sup = f.support;
|
|
51
|
+
const supportTitle = sup && typeof sup.n === 'number' && typeof sup.k === 'number'
|
|
52
|
+
? (lang === 'zh'
|
|
53
|
+
? `${sup.n} 次采样里有 ${sup.k} 次报了这条`
|
|
54
|
+
: `Reported by ${sup.k} of ${sup.n} samples`)
|
|
55
|
+
: '';
|
|
56
|
+
const supBadge = sup && typeof sup.n === 'number' && sup.n > 1 && typeof sup.k === 'number'
|
|
57
|
+
? `<span class="rs-badge rs-badge--${sup.k >= sup.n ? 'pass' : 'warn'}" title="${e(supportTitle)}" style="margin-left:6px">${sup.k}/${sup.n}</span>`
|
|
58
|
+
: '';
|
|
49
59
|
return `<div class="rs-finding rs-finding--${isErr ? 'err' : 'warn'}">
|
|
50
|
-
<div class="rs-finding-desc">${mIcon}${e(f.description ?? '')}</div>
|
|
60
|
+
<div class="rs-finding-desc">${mIcon}${e(f.description ?? '')}${supBadge}</div>
|
|
51
61
|
${f.suggestion ? `<div class="rs-finding-sug">${svgIcon('bulb', { size: 13, style: 'vertical-align:-2px;margin-right:5px' })}${e(f.suggestion)}</div>` : ''}
|
|
52
62
|
</div>`;
|
|
53
63
|
}).join('');
|
|
@@ -56,7 +66,25 @@ function renderRuleCard(r, lang, forceOpen = false) {
|
|
|
56
66
|
<div class="rs-card-body">${findingHtml}</div>
|
|
57
67
|
</details>`;
|
|
58
68
|
}
|
|
69
|
+
function renderSampleDegradedAlert(summary, lang) {
|
|
70
|
+
const samples = summary?.detail?.samples;
|
|
71
|
+
if (!samples || samples.degraded !== true)
|
|
72
|
+
return '';
|
|
73
|
+
const requested = typeof samples.requested === 'number' ? samples.requested : 0;
|
|
74
|
+
const succeeded = typeof samples.succeeded === 'number' ? samples.succeeded : 0;
|
|
75
|
+
if (requested <= 1 || succeeded >= requested)
|
|
76
|
+
return '';
|
|
77
|
+
const title = lang === 'zh' ? '共识置信降级' : 'Consensus confidence degraded';
|
|
78
|
+
const body = lang === 'zh'
|
|
79
|
+
? `本次只成功解析 ${succeeded}/${requested} 次采样,finding 支持度仅基于成功样本。建议重跑,或在限流场景下降低并发。`
|
|
80
|
+
: `Only ${succeeded}/${requested} samples parsed successfully. Finding support is based on successful samples only. Re-run or lower concurrency if rate-limited.`;
|
|
81
|
+
return `<div class="rs-alert rs-alert--warn">
|
|
82
|
+
<div class="rs-alert-title">${e(title)}</div>
|
|
83
|
+
<div class="rs-alert-body">${e(body)}</div>
|
|
84
|
+
</div>`;
|
|
85
|
+
}
|
|
59
86
|
function renderSkillBlock(sk, lang) {
|
|
87
|
+
const summary = sk.results.find((r) => r.ruleId.endsWith(':_summary'));
|
|
60
88
|
const rules = sk.results.filter((r) => !r.ruleId.endsWith(':_summary'));
|
|
61
89
|
const pass = rules.filter((r) => r.status === 'pass').length;
|
|
62
90
|
const warn = rules.filter((r) => r.status === 'warn').length;
|
|
@@ -78,7 +106,7 @@ function renderSkillBlock(sk, lang) {
|
|
|
78
106
|
const oksHtml = oks.length
|
|
79
107
|
? `<div class="rs-panel"><details class="rs-fold"><summary>${zh ? `展开 ${oks.length} 条通过的规则` : `Show ${oks.length} passed rules`}</summary>${oks.map((r) => renderRuleCard(r, lang)).join('')}</details></div>`
|
|
80
108
|
: '';
|
|
81
|
-
return stats + issuesHtml + oksHtml;
|
|
109
|
+
return renderSampleDegradedAlert(summary, lang) + stats + issuesHtml + oksHtml;
|
|
82
110
|
}
|
|
83
111
|
export function renderDoctorDetail(report, skillName, langQ, lang = DEFAULT_LANG, skillContext) {
|
|
84
112
|
const zh = lang === 'zh';
|
|
@@ -74,4 +74,4 @@ export interface ReportShellOpts {
|
|
|
74
74
|
extraCss?: string;
|
|
75
75
|
}
|
|
76
76
|
export declare function reportShell(opts: ReportShellOpts, lang?: Lang): string;
|
|
77
|
-
export declare const REPORT_SHELL_CSS = "\n/* \u2500\u2500 \u9762\u5305\u5C51(\u66FF\u6389\u88F8\u8FD4\u56DE\u94FE\u63A5) \u2500\u2500 */\n.rs-crumb{display:flex;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;color:#637083}\n.rs-crumb-link{display:inline-flex;align-items:center;gap:6px;color:#637083;text-decoration:none}\n.rs-crumb-link:hover{color:#4f46e5;text-decoration:none}\n.rs-crumb-link svg{color:#9ca3af}\n.rs-crumb-link:hover svg{color:#4f46e5}\n.rs-crumb-sep{color:#c0c6d2}\n.rs-crumb-dim{color:#637083}\n.rs-crumb-cur{color:#182033;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-crumb-spacer{flex:1}\n.rs-crumb-back{display:inline-flex;align-items:center;gap:5px;color:#9ca3af;text-decoration:none;flex-shrink:0}\n.rs-crumb-back:hover{color:#4f46e5;text-decoration:none}\n\n/* \u2500\u2500 skill \u7EF4\u5EA6\u5207\u6362\u5668(doctor/eval/observe \u4E92\u94FE)\u2014 \u5206\u6BB5\u63A7\u4EF6\u98CE \u2500\u2500 */\n.rs-dims{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0 0 16px}\n.rs-dims-seg{display:inline-flex;align-items:stretch;background:#fff;border:1px solid #e4e8f1;border-radius:10px;padding:3px;gap:2px;box-shadow:0 1px 2px rgba(31,41,55,3%)}\n.rs-dim{display:inline-flex;align-items:center;gap:7px;font-size:13px;padding:7px 14px;border-radius:7px;color:#637083;text-decoration:none;white-space:nowrap;transition:background .12s,color .12s}\na.rs-dim:hover{background:#f1f4f9;color:#182033}\n.rs-dim-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}\n.rs-dim-lbl{font-weight:500}\n/* \u5F53\u524D\u7EF4\u5EA6:\u6D45\u586B\u5145 + \u72B6\u6001\u70B9\u5DF2\u8868\u660E\u8EAB\u4EFD,\u6587\u5B57\u52A0\u91CD */\n.rs-dim--active{background:#f1f4f9;color:#182033;cursor:default}\n.rs-dim--active .rs-dim-lbl{font-weight:700}\n.rs-dim--empty{color:#b6bcc8;cursor:default}\n/* \u5168\u90E8\u5386\u53F2\u6309\u94AE */\n.rs-hist-btn{display:inline-flex;align-items:center;gap:6px;font-size:13px;padding:7px 13px;border-radius:9px;border:1px solid #e4e8f1;background:#fff;color:#637083;cursor:pointer;white-space:nowrap;font-family:inherit;transition:border-color .15s,color .15s}\n.rs-hist-btn:hover{border-color:#c7cedb;color:#182033}\n.rs-hist-ct{font-variant-numeric:tabular-nums;font-weight:700;color:#9ca3af;font-size:12px}\n/* \u5168\u90E8\u5386\u53F2\u5F39\u6846 */\n.rs-hist-overlay{display:none;position:fixed;inset:0;background:rgba(24,32,51,.32);z-index:50;align-items:flex-start;justify-content:center;padding:8vh 16px}\n.rs-hist-modal{background:#fff;border-radius:14px;box-shadow:0 24px 60px rgba(31,41,55,.22);width:100%;max-width:520px;max-height:76vh;display:flex;flex-direction:column;overflow:hidden}\n.rs-hist-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eef1f6;font-size:14px;color:#182033}\n.rs-hist-x{background:transparent;border:none;color:#9ca3af;font-size:16px;cursor:pointer;line-height:1;padding:4px}\n.rs-hist-x:hover{color:#182033}\n.rs-hist-list{overflow-y:auto;padding:6px 10px 12px}\n.rs-hist-group{margin-top:8px}\n.rs-hist-group-h{display:flex;align-items:center;gap:7px;font-size:12px;color:#637083;font-weight:600;padding:8px 8px 6px}\n.rs-hist-group-dot{width:7px;height:7px;border-radius:50%}\n.rs-hist-group-h small{font-weight:500;color:#b6bcc8}\n.rs-hist-row{display:flex;align-items:baseline;gap:14px;padding:8px;border-radius:8px;text-decoration:none;color:#182033;font-size:13px}\n.rs-hist-row:hover{background:#f1f4f9}\n.rs-hist-row--cur{background:#f1f4f9;cursor:default}\n.rs-hist-date{font-variant-numeric:tabular-nums;color:#637083;min-width:88px;flex-shrink:0}\n.rs-hist-score{font-weight:700;font-variant-numeric:tabular-nums;font-family:\"SF Mono\",Menlo,monospace;min-width:72px;flex-shrink:0}\n.rs-hist-score--green{color:#1f9d63}.rs-hist-score--yellow{color:#d97706}.rs-hist-score--red{color:#dc2626}.rs-hist-score--gray{color:#9ca3af}\n.rs-hist-meta{flex:1;min-width:0;color:#9ca3af;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-hist-go{color:#c0c6d2;font-size:13px;flex-shrink:0}\n.rs-hist-row:hover .rs-hist-go{color:#637083}\n.rs-hist-row--cur .rs-hist-go{color:#4f46e5;font-weight:600;font-size:12px}\n\n/* \u2500\u2500 Hero \u2500\u2500 */\n.rs-hero{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:20px 24px;background:#fff;border:1px solid #e4e8f1;border-radius:12px;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}\n@media(max-width:640px){.rs-hero{flex-direction:column;align-items:flex-start}}\n.rs-hero-left{display:flex;flex-direction:column;gap:4px;min-width:0}\n.rs-hero-kind{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:#637083}\n.rs-hero-icon{display:inline-flex;align-items:center}\n.rs-hero-name{margin:0;font-size:20px;font-weight:700;color:#182033;letter-spacing:-.2px}\n.rs-hero-meta{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:4px;color:#9ca3af;font-size:12px;font-variant-numeric:tabular-nums}\n.rs-hero-meta span{display:inline-flex;align-items:center;gap:4px}\n.rs-hero-score{display:flex;flex-direction:column;align-items:flex-end;gap:2px;flex-shrink:0;line-height:1}\n.rs-hero-num{font-size:40px;font-weight:800;letter-spacing:-1.5px;font-variant-numeric:tabular-nums;line-height:1}\n.rs-hero-grade{font-size:12px;font-weight:600;color:#637083}\n\n/* \u2500\u2500 \u7EDF\u8BA1\u6761 \u2500\u2500 */\n.rs-stats{display:flex;flex-wrap:wrap;gap:20px;padding:14px 20px;background:#fff;border:1px solid #e4e8f1;border-radius:10px;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}\n.rs-stat{display:flex;align-items:baseline;gap:6px}\n.rs-stat-num{font-size:20px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1}\n.rs-stat-label{font-size:12px;color:#637083}\n.rs-stat--pass .rs-stat-num{color:#1f9d63}\n.rs-stat--warn .rs-stat-num{color:#d97706}\n.rs-stat--fail .rs-stat-num{color:#dc2626}\n.rs-stat--total .rs-stat-num{color:#182033}\n\n/* \u2500\u2500 Panel\uFF08\u5185\u5BB9\u533A\u5757\uFF09 \u2500\u2500 */\n.rs-panel{background:#fff;border:1px solid #e4e8f1;border-radius:12px;box-shadow:0 8px 24px rgba(31,41,55,4%);padding:18px 20px;margin-bottom:16px}\n.rs-panel-title{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:700;color:#182033;margin-bottom:14px}\n.rs-panel-title small{font-size:12px;font-weight:400;color:#9ca3af;margin-left:4px}\n\n/* \u2500\u2500 \u89C4\u5219 / \u7528\u4F8B:\u9762\u677F\u5185\u7684 hairline \u5217\u8868\u9879(\u65E0 tint \u76D2\u5B50,\u72B6\u6001\u9760\u5DE6\u4FA7\u8272\u70B9) \u2500\u2500 */\n.rs-card{border-bottom:1px solid #eef1f6;background:transparent;border-radius:8px;transition:background .12s}\n.rs-card:last-child{border-bottom:none}\ndetails.rs-card:not([open]):hover{background:#f9fafc}\ndetails.rs-card[open]{background:#fafbfd}\n.rs-card-head{display:flex;align-items:center;gap:10px;padding:12px 6px;cursor:pointer;list-style:none}\n.rs-card-head::-webkit-details-marker{display:none}\ndetails.rs-card>.rs-card-head::after{content:'\u203A';margin-left:4px;color:#c0c6d2;font-size:16px;transition:transform .15s;flex-shrink:0}\ndetails.rs-card[open]>.rs-card-head::after{transform:rotate(90deg)}\n.rs-card-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.rs-card-dot--fail{background:#dc2626}\n.rs-card-dot--warn{background:#d97706}\n.rs-card-dot--pass{background:#1f9d63}\n.rs-card-dot--skip{background:#cbd2dd}\n.rs-card-name{flex:1;min-width:0;font-size:14px;font-weight:600;color:#182033;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-card-badges{display:flex;gap:6px;flex-shrink:0}\n.rs-badge{font-size:11px;font-weight:600;padding:1px 8px;border-radius:10px}\n.rs-badge--fail{color:#dc2626;background:rgba(220,38,38,10%)}\n.rs-badge--warn{color:#d97706;background:rgba(217,119,6,10%)}\n.rs-badge--pass{color:#1f9d63;background:rgba(31,157,99,10%)}\n.rs-card-body{padding:0 6px 14px 24px}\n.rs-finding{margin-top:10px;font-size:13.5px;line-height:1.65}\n.rs-finding-desc{color:#374151;word-break:break-word}\n.rs-finding-sug{margin-top:6px;padding:8px 12px;background:rgba(79,70,229,.05);border-radius:6px;color:#4f46e5;font-size:12.5px;line-height:1.6;word-break:break-word}\n\n/* \u2500\u2500 \u6298\u53E0\u901A\u8FC7\u9879 \u2500\u2500 */\n.rs-fold{margin-top:10px}\n.rs-fold>summary{cursor:pointer;list-style:none;font-size:12.5px;color:#637083;padding:6px 0}\n.rs-fold>summary::-webkit-details-marker{display:none}\n.rs-fold>summary:hover{color:#182033}\n.rs-fold[open]>summary{margin-bottom:8px}\n\n/* \u2500\u2500 \u7A7A\u6001 \u2500\u2500 */\n.rs-empty{padding:32px 20px;text-align:center;color:#9ca3af;font-size:13px}\n";
|
|
77
|
+
export declare const REPORT_SHELL_CSS = "\n/* \u2500\u2500 \u9762\u5305\u5C51(\u66FF\u6389\u88F8\u8FD4\u56DE\u94FE\u63A5) \u2500\u2500 */\n.rs-crumb{display:flex;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;color:#637083}\n.rs-crumb-link{display:inline-flex;align-items:center;gap:6px;color:#637083;text-decoration:none}\n.rs-crumb-link:hover{color:#4f46e5;text-decoration:none}\n.rs-crumb-link svg{color:#9ca3af}\n.rs-crumb-link:hover svg{color:#4f46e5}\n.rs-crumb-sep{color:#c0c6d2}\n.rs-crumb-dim{color:#637083}\n.rs-crumb-cur{color:#182033;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-crumb-spacer{flex:1}\n.rs-crumb-back{display:inline-flex;align-items:center;gap:5px;color:#9ca3af;text-decoration:none;flex-shrink:0}\n.rs-crumb-back:hover{color:#4f46e5;text-decoration:none}\n\n/* \u2500\u2500 skill \u7EF4\u5EA6\u5207\u6362\u5668(doctor/eval/observe \u4E92\u94FE)\u2014 \u5206\u6BB5\u63A7\u4EF6\u98CE \u2500\u2500 */\n.rs-dims{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0 0 16px}\n.rs-dims-seg{display:inline-flex;align-items:stretch;background:#fff;border:1px solid #e4e8f1;border-radius:10px;padding:3px;gap:2px;box-shadow:0 1px 2px rgba(31,41,55,3%)}\n.rs-dim{display:inline-flex;align-items:center;gap:7px;font-size:13px;padding:7px 14px;border-radius:7px;color:#637083;text-decoration:none;white-space:nowrap;transition:background .12s,color .12s}\na.rs-dim:hover{background:#f1f4f9;color:#182033}\n.rs-dim-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}\n.rs-dim-lbl{font-weight:500}\n/* \u5F53\u524D\u7EF4\u5EA6:\u6D45\u586B\u5145 + \u72B6\u6001\u70B9\u5DF2\u8868\u660E\u8EAB\u4EFD,\u6587\u5B57\u52A0\u91CD */\n.rs-dim--active{background:#f1f4f9;color:#182033;cursor:default}\n.rs-dim--active .rs-dim-lbl{font-weight:700}\n.rs-dim--empty{color:#b6bcc8;cursor:default}\n/* \u5168\u90E8\u5386\u53F2\u6309\u94AE */\n.rs-hist-btn{display:inline-flex;align-items:center;gap:6px;font-size:13px;padding:7px 13px;border-radius:9px;border:1px solid #e4e8f1;background:#fff;color:#637083;cursor:pointer;white-space:nowrap;font-family:inherit;transition:border-color .15s,color .15s}\n.rs-hist-btn:hover{border-color:#c7cedb;color:#182033}\n.rs-hist-ct{font-variant-numeric:tabular-nums;font-weight:700;color:#9ca3af;font-size:12px}\n/* \u5168\u90E8\u5386\u53F2\u5F39\u6846 */\n.rs-hist-overlay{display:none;position:fixed;inset:0;background:rgba(24,32,51,.32);z-index:50;align-items:flex-start;justify-content:center;padding:8vh 16px}\n.rs-hist-modal{background:#fff;border-radius:14px;box-shadow:0 24px 60px rgba(31,41,55,.22);width:100%;max-width:520px;max-height:76vh;display:flex;flex-direction:column;overflow:hidden}\n.rs-hist-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eef1f6;font-size:14px;color:#182033}\n.rs-hist-x{background:transparent;border:none;color:#9ca3af;font-size:16px;cursor:pointer;line-height:1;padding:4px}\n.rs-hist-x:hover{color:#182033}\n.rs-hist-list{overflow-y:auto;padding:6px 10px 12px}\n.rs-hist-group{margin-top:8px}\n.rs-hist-group-h{display:flex;align-items:center;gap:7px;font-size:12px;color:#637083;font-weight:600;padding:8px 8px 6px}\n.rs-hist-group-dot{width:7px;height:7px;border-radius:50%}\n.rs-hist-group-h small{font-weight:500;color:#b6bcc8}\n.rs-hist-row{display:flex;align-items:baseline;gap:14px;padding:8px;border-radius:8px;text-decoration:none;color:#182033;font-size:13px}\n.rs-hist-row:hover{background:#f1f4f9}\n.rs-hist-row--cur{background:#f1f4f9;cursor:default}\n.rs-hist-date{font-variant-numeric:tabular-nums;color:#637083;min-width:88px;flex-shrink:0}\n.rs-hist-score{font-weight:700;font-variant-numeric:tabular-nums;font-family:\"SF Mono\",Menlo,monospace;min-width:72px;flex-shrink:0}\n.rs-hist-score--green{color:#1f9d63}.rs-hist-score--yellow{color:#d97706}.rs-hist-score--red{color:#dc2626}.rs-hist-score--gray{color:#9ca3af}\n.rs-hist-meta{flex:1;min-width:0;color:#9ca3af;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-hist-go{color:#c0c6d2;font-size:13px;flex-shrink:0}\n.rs-hist-row:hover .rs-hist-go{color:#637083}\n.rs-hist-row--cur .rs-hist-go{color:#4f46e5;font-weight:600;font-size:12px}\n\n/* \u2500\u2500 Hero \u2500\u2500 */\n.rs-hero{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:20px 24px;background:#fff;border:1px solid #e4e8f1;border-radius:12px;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}\n@media(max-width:640px){.rs-hero{flex-direction:column;align-items:flex-start}}\n.rs-hero-left{display:flex;flex-direction:column;gap:4px;min-width:0}\n.rs-hero-kind{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:#637083}\n.rs-hero-icon{display:inline-flex;align-items:center}\n.rs-hero-name{margin:0;font-size:20px;font-weight:700;color:#182033;letter-spacing:-.2px}\n.rs-hero-meta{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:4px;color:#9ca3af;font-size:12px;font-variant-numeric:tabular-nums}\n.rs-hero-meta span{display:inline-flex;align-items:center;gap:4px}\n.rs-hero-score{display:flex;flex-direction:column;align-items:flex-end;gap:2px;flex-shrink:0;line-height:1}\n.rs-hero-num{font-size:40px;font-weight:800;letter-spacing:-1.5px;font-variant-numeric:tabular-nums;line-height:1}\n.rs-hero-grade{font-size:12px;font-weight:600;color:#637083}\n\n/* \u2500\u2500 \u7EDF\u8BA1\u6761 \u2500\u2500 */\n.rs-stats{display:flex;flex-wrap:wrap;gap:20px;padding:14px 20px;background:#fff;border:1px solid #e4e8f1;border-radius:10px;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}\n.rs-stat{display:flex;align-items:baseline;gap:6px}\n.rs-stat-num{font-size:20px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1}\n.rs-stat-label{font-size:12px;color:#637083}\n.rs-stat--pass .rs-stat-num{color:#1f9d63}\n.rs-stat--warn .rs-stat-num{color:#d97706}\n.rs-stat--fail .rs-stat-num{color:#dc2626}\n.rs-stat--total .rs-stat-num{color:#182033}\n\n/* \u2500\u2500 \u63D0\u793A\u6761 \u2500\u2500 */\n.rs-alert{display:flex;flex-direction:column;gap:3px;padding:12px 16px;border:1px solid #f5c26b;border-radius:10px;background:#fff8ed;color:#92400e;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}\n.rs-alert-title{font-size:13px;font-weight:800}\n.rs-alert-body{font-size:12.5px;line-height:1.65}\n\n/* \u2500\u2500 Panel\uFF08\u5185\u5BB9\u533A\u5757\uFF09 \u2500\u2500 */\n.rs-panel{background:#fff;border:1px solid #e4e8f1;border-radius:12px;box-shadow:0 8px 24px rgba(31,41,55,4%);padding:18px 20px;margin-bottom:16px}\n.rs-panel-title{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:700;color:#182033;margin-bottom:14px}\n.rs-panel-title small{font-size:12px;font-weight:400;color:#9ca3af;margin-left:4px}\n\n/* \u2500\u2500 \u89C4\u5219 / \u7528\u4F8B:\u9762\u677F\u5185\u7684 hairline \u5217\u8868\u9879(\u65E0 tint \u76D2\u5B50,\u72B6\u6001\u9760\u5DE6\u4FA7\u8272\u70B9) \u2500\u2500 */\n.rs-card{border-bottom:1px solid #eef1f6;background:transparent;border-radius:8px;transition:background .12s}\n.rs-card:last-child{border-bottom:none}\ndetails.rs-card:not([open]):hover{background:#f9fafc}\ndetails.rs-card[open]{background:#fafbfd}\n.rs-card-head{display:flex;align-items:center;gap:10px;padding:12px 6px;cursor:pointer;list-style:none}\n.rs-card-head::-webkit-details-marker{display:none}\ndetails.rs-card>.rs-card-head::after{content:'\u203A';margin-left:4px;color:#c0c6d2;font-size:16px;transition:transform .15s;flex-shrink:0}\ndetails.rs-card[open]>.rs-card-head::after{transform:rotate(90deg)}\n.rs-card-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.rs-card-dot--fail{background:#dc2626}\n.rs-card-dot--warn{background:#d97706}\n.rs-card-dot--pass{background:#1f9d63}\n.rs-card-dot--skip{background:#cbd2dd}\n.rs-card-name{flex:1;min-width:0;font-size:14px;font-weight:600;color:#182033;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rs-card-badges{display:flex;gap:6px;flex-shrink:0}\n.rs-badge{font-size:11px;font-weight:600;padding:1px 8px;border-radius:10px}\n.rs-badge--fail{color:#dc2626;background:rgba(220,38,38,10%)}\n.rs-badge--warn{color:#d97706;background:rgba(217,119,6,10%)}\n.rs-badge--pass{color:#1f9d63;background:rgba(31,157,99,10%)}\n.rs-card-body{padding:0 6px 14px 24px}\n.rs-finding{margin-top:10px;font-size:13.5px;line-height:1.65}\n.rs-finding-desc{color:#374151;word-break:break-word}\n.rs-finding-sug{margin-top:6px;padding:8px 12px;background:rgba(79,70,229,.05);border-radius:6px;color:#4f46e5;font-size:12.5px;line-height:1.6;word-break:break-word}\n\n/* \u2500\u2500 \u6298\u53E0\u901A\u8FC7\u9879 \u2500\u2500 */\n.rs-fold{margin-top:10px}\n.rs-fold>summary{cursor:pointer;list-style:none;font-size:12.5px;color:#637083;padding:6px 0}\n.rs-fold>summary::-webkit-details-marker{display:none}\n.rs-fold>summary:hover{color:#182033}\n.rs-fold[open]>summary{margin-bottom:8px}\n\n/* \u2500\u2500 \u7A7A\u6001 \u2500\u2500 */\n.rs-empty{padding:32px 20px;text-align:center;color:#9ca3af;font-size:13px}\n";
|
|
@@ -181,6 +181,11 @@ a.rs-dim:hover{background:#f1f4f9;color:#182033}
|
|
|
181
181
|
.rs-stat--fail .rs-stat-num{color:#dc2626}
|
|
182
182
|
.rs-stat--total .rs-stat-num{color:#182033}
|
|
183
183
|
|
|
184
|
+
/* ── 提示条 ── */
|
|
185
|
+
.rs-alert{display:flex;flex-direction:column;gap:3px;padding:12px 16px;border:1px solid #f5c26b;border-radius:10px;background:#fff8ed;color:#92400e;box-shadow:0 8px 24px rgba(31,41,55,4%);margin-bottom:16px}
|
|
186
|
+
.rs-alert-title{font-size:13px;font-weight:800}
|
|
187
|
+
.rs-alert-body{font-size:12.5px;line-height:1.65}
|
|
188
|
+
|
|
184
189
|
/* ── Panel(内容区块) ── */
|
|
185
190
|
.rs-panel{background:#fff;border:1px solid #e4e8f1;border-radius:12px;box-shadow:0 8px 24px rgba(31,41,55,4%);padding:18px 20px;margin-bottom:16px}
|
|
186
191
|
.rs-panel-title{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:700;color:#182033;margin-bottom:14px}
|