oh-my-knowledge 0.46.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 CHANGED
@@ -8,8 +8,8 @@
8
8
 
9
9
  **English** | [简体中文](./README.zh.md)
10
10
 
11
- **Did your prompt actually get better?**
12
- A/B test your prompts and skills with statistical rigor bootstrap CI and length-debias on by default, Krippendorff α the moment you add a gold set.
11
+ **Is this knowledge input (prompt / skill / RAG / agent) any good, and can you ship it with evidence?**
12
+ `doctor` checks whether this knowledge input is coherent enough to measure; `eval` fixes the model and samples, changes only the knowledge input, and tells you whether the new version is genuinely better. Bootstrap CI and length-debias are on by default; Krippendorff α appears the moment you add a gold set.
13
13
 
14
14
  📖 **Full documentation: [oh-my-knowledge.pages.dev](https://oh-my-knowledge.pages.dev)** (searchable, English / 简体中文)
15
15
 
@@ -35,6 +35,20 @@ Walkthrough: [5-minute quickstart guide](docs/quickstart-skill-eval.md) (recomme
35
35
 
36
36
  Deeper: [who omk is for](docs/explanation/who-omk-is-for.md) · [CLI reference](docs/reference/cli.md) · [how it works](docs/explanation/architecture.md) · [eval sample format](docs/reference/eval-sample-format.md) · [executors](docs/reference/executors.md) · [artifact layout](docs/reference/artifact-layout.md)
37
37
 
38
+ ## The first workflow
39
+
40
+ omk is primarily for authors and maintainers of LLM knowledge artifacts who need a release decision, not for passive end-users of a skill. The first workflow is deliberately small:
41
+
42
+ ```text
43
+ change a skill / prompt / agent artifact
44
+ → run omk doctor to catch structure, dependency, and measurability problems
45
+ → run omk eval to compare against a baseline on the same samples
46
+ → read the report / Studio view for the next concrete fix
47
+ → decide ship / don't ship
48
+ ```
49
+
50
+ `observe` is the later production-feedback loop: useful once real usage traces exist, but not required for omk's first value. The trunk is the pre-ship doctor → eval decision.
51
+
38
52
  ## Use inside AI Coding Agents
39
53
 
40
54
  Install the official omk Agent Skill to let your coding agent run omk workflows from natural language:
@@ -73,7 +87,7 @@ You can also describe the goal in natural language, such as "compare v1 vs v2" o
73
87
 
74
88
  ## Why this tool
75
89
 
76
- Teams doing knowledge engineering produce lots of knowledge artifacts (skills today, but also prompts, agents, workflows…). When someone asks "why is v2 better than v1", you need objective data instead of gut feeling. `oh-my-knowledge` solves this with controlled experiments: **same model, same test samples, only the knowledge artifact changes.**
90
+ Teams doing knowledge engineering produce lots of knowledge artifacts (skills today, but also prompts, agents, workflows…). When someone asks "can we ship v2, and why?", you need objective data instead of gut feeling. `oh-my-knowledge` solves this with controlled experiments: **same model, same test samples, only the knowledge artifact changes.**
77
91
 
78
92
  ## Why omk over alternatives
79
93
 
package/README.zh.md CHANGED
@@ -8,8 +8,8 @@
8
8
 
9
9
  [English](./README.md) | **简体中文**
10
10
 
11
- **你改完 prompt,真的变好了吗?**
12
- 用统计严谨性 A/B 测试你的 prompt 和 skill —— Bootstrap 置信区间、长度去偏默认开,配一份人工 gold 就自动算 Krippendorff α。
11
+ **一段知识输入(prompt / skill / RAG / agent)好不好,能不能有证据地发布?**
12
+ `doctor` 先检查这类知识输入是否清楚到值得测;`eval` 固定模型和用例,只改变知识输入,判断新版本是否真的更好。Bootstrap 置信区间、长度去偏默认开,配一份人工 gold 就自动算 Krippendorff α。
13
13
 
14
14
  📖 **完整文档:[oh-my-knowledge.pages.dev/zh](https://oh-my-knowledge.pages.dev/zh/)**(可搜索,可切换英文)
15
15
 
@@ -35,6 +35,20 @@ omk eval --control code-review-v1 --treatment code-review-v2
35
35
 
36
36
  深入:[为谁、解决什么](docs/zh/explanation/who-omk-is-for.md) · [CLI 参考](docs/zh/reference/cli.md) · [工作原理](docs/zh/explanation/architecture.md) · [评测用例格式](docs/zh/reference/eval-sample-format.md) · [执行器](docs/zh/reference/executors.md) · [artifact 布局](docs/zh/reference/artifact-layout.md)
37
37
 
38
+ ## 第一条工作流
39
+
40
+ omk 主要给 LLM 知识载体的作者 / 维护者用,帮他们做发布判断;它不是给被动安装 skill 的普通使用者用的。第一条工作流应该很短:
41
+
42
+ ```text
43
+ 改了一个 skill / prompt / agent artifact
44
+ → 跑 omk doctor,先抓结构、依赖和可测性问题
45
+ → 跑 omk eval,在同一批用例上和 baseline 对比
46
+ → 看报告 / Studio,知道下一步具体该改哪里
47
+ → 决定发布 / 不发布
48
+ ```
49
+
50
+ `observe` 是后续的生产反馈闭环:等真实使用 trace 存在后很有价值,但不是 omk 第一价值的前提。主干先是发布前的 doctor → eval 判断。
51
+
38
52
  ## 在 AI Coding Agent 中使用
39
53
 
40
54
  安装 omk 官方 Agent Skill 后,可以直接用自然语言让 coding agent 跑 omk 工作流:
@@ -73,7 +87,7 @@ omk sample skills/my-skill.md
73
87
 
74
88
  ## 为什么需要这个工具
75
89
 
76
- 做知识工程的团队会产出大量知识载体(当前常见是 skill,也包括 prompt、agent、workflow 等)。当被问到「v2 比 v1 好在哪」时,需要客观数据而非主观判断。`oh-my-knowledge` 通过控制变量实验解决这个问题:**相同模型、相同评测用例,只改变知识载体。**
90
+ 做知识工程的团队会产出大量知识载体(当前常见是 skill,也包括 prompt、agent、workflow 等)。当被问到「v2 能不能发、为什么」时,需要客观数据而非主观判断。`oh-my-knowledge` 通过控制变量实验解决这个问题:**相同模型、相同评测用例,只改变知识载体。**
77
91
 
78
92
  ## 为什么选 omk
79
93
 
@@ -178,7 +178,7 @@ export default class Doctor extends BaseCommand {
178
178
  }
179
179
  }
180
180
  const { runDoctor } = await import('../../doctor/index.js');
181
- const { renderDoctorReportText, renderDoctorReportJson } = await import('../../doctor/renderer.js');
181
+ const { renderDoctorReportText, renderDoctorReportJson, renderDoctorActionPlanText } = await import('../../doctor/renderer.js');
182
182
  const { getRegisteredRules } = await import('../../doctor/rules.js');
183
183
  const { isComposerRule } = await import('../../types/doctor.js');
184
184
  // 默认:静态规则 + 在线检查(LLM health composer + endpoint 自定义维度 external=true)。
@@ -236,6 +236,7 @@ export default class Doctor extends BaseCommand {
236
236
  ? `doctor failed: ${report.totals.fail} 个 skill 未通过 (${report.totals.warn} warn / ${report.totals.pass} pass)`
237
237
  : `doctor failed: ${report.totals.fail} skills did not pass (${report.totals.warn} warn / ${report.totals.pass} pass)`;
238
238
  console.error(summary);
239
+ process.stderr.write(renderDoctorActionPlanText(report, lang));
239
240
  }
240
241
  }
241
242
  else {
@@ -152,6 +152,14 @@ async function emitBatchVerdict(report, reportsDir, values, lang) {
152
152
  for (const result of results) {
153
153
  emitVerdictText(` ${result.verdict.level}: ${result.treatment} — ${result.verdict.headline}`);
154
154
  }
155
+ const next = lang === 'zh'
156
+ ? (failed === 0
157
+ ? '全部通过,可以进入发布流程:请留存这些报告作为发布证据;如果是受管 skill,再运行 `omk promote`。'
158
+ : '先处理未通过的 skill;逐个打开对应报告,看最差层和失败用例后再重跑。')
159
+ : (failed === 0
160
+ ? 'all skills passed and are ready for release: keep these reports as release evidence; for managed skills, run `omk promote`.'
161
+ : 'fix the failing skills first; open each report, inspect the weakest layer and failing samples, then re-run.');
162
+ emitVerdictText(tCli('cli.run.batch_verdict_next_step', lang, { next }));
155
163
  return failed === 0 ? 0 : 1;
156
164
  }
157
165
  async function announceSavedReport({ report, filePath, reportsDir, values, lang, }) {
@@ -1,3 +1,3 @@
1
1
  import type { CliMessage } from './types.js';
2
- export type RunMessageKey = 'cli.progress.preflight_starting' | 'cli.progress.sample_retry' | 'cli.progress.sample_error' | 'cli.progress.sample_executing' | 'cli.progress.sample_exec_done' | 'cli.progress.output_preview' | 'cli.progress.judging' | 'cli.progress.judged' | 'cli.progress.skipped' | 'cli.progress.sample_done' | 'cli.progress.sample_failed_done' | 'cli.run.invalid_repeat' | 'cli.run.invalid_holdout_ratio' | 'cli.run.invalid_judge_repeat' | 'cli.run.no_debias_length_active' | 'cli.run.invalid_bootstrap_samples' | 'cli.run.bootstrap_samples_too_large' | 'cli.run.dry_run_no_scores' | 'cli.run.skill_section' | 'cli.run.run_section' | 'cli.run.batch_complete' | 'cli.run.batch_verdict_header' | 'cli.run.batch_child_report_missing' | 'cli.run.eval_complete' | 'cli.run.tally' | 'cli.run.report_saved' | 'cli.run.evidence_recorded' | 'cli.run.evidence_recorded_unbound' | 'cli.run.report_only_gate_skipped' | 'cli.run.report_server_running' | 'cli.run.report_server_view' | 'cli.run.report_server_stop' | 'cli.run.no_serve_in_non_tty' | 'cli.run.no_serve_view_hint' | 'cli.run.gold_load_failed' | 'cli.run.gold_load_issue' | 'cli.run.contamination_warning' | 'cli.run.skip_connectivity_warning';
2
+ export type RunMessageKey = 'cli.progress.preflight_starting' | 'cli.progress.sample_retry' | 'cli.progress.sample_error' | 'cli.progress.sample_executing' | 'cli.progress.sample_exec_done' | 'cli.progress.output_preview' | 'cli.progress.judging' | 'cli.progress.judged' | 'cli.progress.skipped' | 'cli.progress.sample_done' | 'cli.progress.sample_failed_done' | 'cli.run.invalid_repeat' | 'cli.run.invalid_holdout_ratio' | 'cli.run.invalid_judge_repeat' | 'cli.run.no_debias_length_active' | 'cli.run.invalid_bootstrap_samples' | 'cli.run.bootstrap_samples_too_large' | 'cli.run.dry_run_no_scores' | 'cli.run.skill_section' | 'cli.run.run_section' | 'cli.run.batch_complete' | 'cli.run.batch_verdict_header' | 'cli.run.batch_verdict_next_step' | 'cli.run.batch_child_report_missing' | 'cli.run.eval_complete' | 'cli.run.tally' | 'cli.run.report_saved' | 'cli.run.evidence_recorded' | 'cli.run.evidence_recorded_unbound' | 'cli.run.report_only_gate_skipped' | 'cli.run.report_server_running' | 'cli.run.report_server_view' | 'cli.run.report_server_stop' | 'cli.run.no_serve_in_non_tty' | 'cli.run.no_serve_view_hint' | 'cli.run.gold_load_failed' | 'cli.run.gold_load_issue' | 'cli.run.contamination_warning' | 'cli.run.skip_connectivity_warning';
3
3
  export declare const runDict: Record<RunMessageKey, CliMessage>;
@@ -87,6 +87,10 @@ export const runDict = {
87
87
  zh: '批量评测结论:{status}({passed}/{total} 通过)',
88
88
  en: 'Batch verdict: {status} ({passed}/{total} passed)',
89
89
  },
90
+ 'cli.run.batch_verdict_next_step': {
91
+ zh: ' 下一步:{next}',
92
+ en: ' Next: {next}',
93
+ },
90
94
  'cli.run.batch_child_report_missing': {
91
95
  zh: '⚠ 子报告缺失:{id},将按不可 ship 处理。\n',
92
96
  en: '⚠ Child report missing: {id}; treating it as not shippable.\n',
@@ -12,5 +12,6 @@
12
12
  */
13
13
  import type { Lang } from '../types/shared.js';
14
14
  import type { DoctorReport } from '../types/index.js';
15
+ export declare function renderDoctorActionPlanText(report: DoctorReport, lang: Lang): string;
15
16
  export declare function renderDoctorReportText(report: DoctorReport, lang: Lang, write?: (s: string) => void): void;
16
17
  export declare function renderDoctorReportJson(report: DoctorReport): string;
@@ -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);
@@ -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 under 6 lines per the
170
- * spec — one verdict + four rationale bullets + one ship recommendation.
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 under 6 lines per the
588
- * spec — one verdict + four rationale bullets + one ship recommendation.
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: 'skill 健康检查未通过,评测已中止。doctor 是评测必经环节,无 skip 选项——请修复上述问题后重跑。',
16
- en: 'skill health check failed; evaluation aborted. doctor is mandatory and not skippable fix the issues above and re-run.',
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: ${tEvalWorkflowMessage('doctor_gate_blocked', lang)}`);
65
+ throw new Error(`doctor failed:\n${tEvalWorkflowMessage('doctor_gate_blocked', lang)}`);
66
66
  }
67
67
  }
68
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-knowledge",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "packageManager": "yarn@4.16.0",
5
5
  "description": "Evaluation framework for LLM knowledge inputs — prompts, RAG corpora, skills, agent workflows. Fix the model, vary the artifact. Built-in statistical rigor: bootstrap CI, Krippendorff α, length-debias, saturation curves.",
6
6
  "type": "module",
@@ -93,11 +93,11 @@
93
93
  "license": "MIT",
94
94
  "dependencies": {
95
95
  "@anthropic-ai/claude-agent-sdk": "^0.3.143",
96
- "@anthropic-ai/sdk": "^0.105.0",
96
+ "@anthropic-ai/sdk": "^0.107.0",
97
97
  "@inquirer/prompts": "^8.4.3",
98
98
  "@modelcontextprotocol/sdk": "^1.29.0",
99
99
  "@oclif/core": "^4",
100
- "@openai/codex-sdk": "0.141.0",
100
+ "@openai/codex-sdk": "0.142.4",
101
101
  "ajv": "^8.18.0",
102
102
  "chart.js": "^4.5.1",
103
103
  "js-yaml": "^4.1.1",
@@ -112,11 +112,11 @@
112
112
  "@types/node": "^25.5.0",
113
113
  "eslint": "^10.1.0",
114
114
  "husky": "^9.1.7",
115
- "lint-staged": "17.0.7",
115
+ "lint-staged": "17.0.8",
116
116
  "npm-run-all2": "^9.0.1",
117
117
  "typescript": "^6.0.2",
118
118
  "typescript-eslint": "^8.58.0",
119
119
  "vitepress": "^1.6.4",
120
- "vitest": "4.1.8"
120
+ "vitest": "4.1.9"
121
121
  }
122
122
  }