oh-my-knowledge 0.37.0 → 0.38.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 +1 -1
- package/README.zh.md +1 -1
- package/dist/assets/agent-skills/omk/SKILL.md +1 -1
- package/dist/assets/agent-skills/omk/references/commands.md +21 -12
- package/dist/authoring/evolver.d.ts +4 -1
- package/dist/authoring/evolver.js +13 -8
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +26 -6
- package/dist/cli/commands/eval/gold/compare.js +7 -4
- package/dist/cli/commands/eval/index.d.ts +1 -0
- package/dist/cli/commands/eval/index.js +14 -3
- package/dist/cli/commands/evolve.d.ts +1 -0
- package/dist/cli/commands/evolve.js +45 -0
- package/dist/cli/commands/init.js +18 -6
- package/dist/cli/commands/list.d.ts +0 -2
- package/dist/cli/commands/list.js +2 -7
- package/dist/cli/commands/observe/inbox.d.ts +1 -0
- package/dist/cli/commands/observe/inbox.js +14 -4
- package/dist/cli/commands/observe/index.d.ts +11 -0
- package/dist/cli/commands/observe/index.js +35 -12
- package/dist/cli/commands/observe/ingest.d.ts +1 -0
- package/dist/cli/commands/observe/ingest.js +13 -4
- package/dist/cli/commands/observe/show.d.ts +1 -0
- package/dist/cli/commands/observe/show.js +12 -2
- package/dist/cli/commands/promote.js +1 -2
- package/dist/cli/commands/sample.js +15 -9
- package/dist/cli/commands/studio.d.ts +2 -0
- package/dist/cli/commands/studio.js +62 -12
- package/dist/cli/lib/cmd-flags.d.ts +8 -0
- package/dist/cli/lib/i18n-dict/common.js +1 -1
- package/dist/cli/lib/i18n-dict/evolve.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/evolve.js +8 -0
- package/dist/cli/lib/i18n-dict/help.js +27 -45
- package/dist/cli/lib/i18n-dict/init.js +2 -2
- package/dist/cli/lib/parse-run-config.d.ts +0 -1
- package/dist/cli/lib/parse-run-config.js +6 -4
- package/dist/cli/lib/record-evolve-outcome.d.ts +31 -0
- package/dist/cli/lib/record-evolve-outcome.js +110 -0
- package/dist/cli/lib/resolve-skill-input.d.ts +6 -0
- package/dist/cli/lib/resolve-skill-input.js +4 -2
- package/dist/diagnosis/types.js +1 -1
- package/dist/doctor/index.js +4 -1
- package/dist/eval-core/artifact-index.d.ts +106 -0
- package/dist/eval-core/artifact-index.js +277 -0
- package/dist/eval-core/default-dirs.d.ts +34 -0
- package/dist/eval-core/default-dirs.js +36 -0
- package/dist/eval-core/evaluation-execution.js +3 -3
- package/dist/eval-core/evaluation-reporting.d.ts +7 -0
- package/dist/eval-core/evaluation-reporting.js +20 -5
- package/dist/eval-core/execution-strategy.js +3 -3
- package/dist/eval-core/measurement-dirs.d.ts +18 -0
- package/dist/eval-core/measurement-dirs.js +76 -0
- package/dist/eval-workflows/batch-evaluation-workflow.js +2 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +2 -1
- package/dist/eval-workflows/run-evaluation.js +5 -2
- package/dist/executors/codex-cli.js +2 -2
- package/dist/executors/script.js +43 -4
- package/dist/inputs/materialize-copy.d.ts +1 -1
- package/dist/inputs/materialize-copy.js +3 -3
- package/dist/inputs/skill-loader.d.ts +1 -1
- package/dist/inputs/skill-loader.js +1 -1
- package/dist/managed/index.d.ts +2 -0
- package/dist/managed/index.js +2 -0
- package/dist/managed/list-query.d.ts +7 -0
- package/dist/managed/list-query.js +11 -0
- package/dist/managed/source-probe.d.ts +15 -0
- package/dist/managed/source-probe.js +129 -0
- package/dist/managed/store.d.ts +10 -0
- package/dist/managed/store.js +25 -2
- package/dist/observability/inbox.js +6 -3
- package/dist/observability/skill-health-analyzer.d.ts +3 -0
- package/dist/observability/skill-health-analyzer.js +2 -0
- package/dist/renderer/html-renderer.js +4 -2
- package/dist/renderer/managed-history-renderer.d.ts +5 -0
- package/dist/renderer/managed-history-renderer.js +255 -0
- package/dist/renderer/observation-inbox-renderer.js +19 -19
- package/dist/renderer/skill-detail-renderer.js +17 -8
- package/dist/renderer/skill-list-renderer.js +1 -0
- package/dist/server/indexed-report-store.d.ts +8 -0
- package/dist/server/indexed-report-store.js +130 -0
- package/dist/server/job-store.d.ts +0 -1
- package/dist/server/job-store.js +0 -2
- package/dist/server/report-server.d.ts +17 -3
- package/dist/server/report-server.js +231 -85
- package/dist/server/report-store.d.ts +14 -8
- package/dist/server/report-store.js +46 -0
- package/dist/server/skill-index.d.ts +7 -1
- package/dist/server/skill-index.js +48 -12
- package/dist/types/executor.d.ts +4 -2
- package/dist/types/report.d.ts +15 -0
- package/dist/types/skill-index.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ That's it — no editing required. `omk init` scaffolds two skill variants and t
|
|
|
29
29
|
|
|
30
30
|
Walkthrough: [5-minute quickstart guide](docs/quickstart-skill-eval.md) (recommended for first-time users).
|
|
31
31
|
|
|
32
|
-
Deeper: [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)
|
|
32
|
+
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)
|
|
33
33
|
|
|
34
34
|
## Use inside AI Coding Agents
|
|
35
35
|
|
package/README.zh.md
CHANGED
|
@@ -29,7 +29,7 @@ omk eval --control code-review-v1 --treatment code-review-v2
|
|
|
29
29
|
|
|
30
30
|
手把手教程:[5 分钟快速上手](docs/zh/quickstart-skill-eval.md)(推荐第一次跑评测的用户)。
|
|
31
31
|
|
|
32
|
-
深入:[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)
|
|
32
|
+
深入:[为谁、解决什么](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)
|
|
33
33
|
|
|
34
34
|
## 在 AI Coding Agent 中使用
|
|
35
35
|
|
|
@@ -142,7 +142,7 @@ omk studio --host 0.0.0.0 # 局域网访问(默认 127.0.0.1
|
|
|
142
142
|
omk studio --no-open # 不自动开浏览器
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
Studio 是 skill-centric:列表页(`/`)按 skill 卡片展示健康等级 / 0-100 参考分 / 待优化数 / 趋势;详情页(`/skills/<name>`)左栏列关键问题清单,右栏画健康趋势 + 三档阶段卡(doctor / eval / observe)。访问 `/
|
|
145
|
+
Studio 是 skill-centric:列表页(`/`)按 skill 卡片展示健康等级 / 0-100 参考分 / 待优化数 / 趋势;详情页(`/skills/<name>`)左栏列关键问题清单,右栏画健康趋势 + 三档阶段卡(doctor / eval / observe)。访问 `/observe-inbox` 查看 observe inbox 看板。
|
|
146
146
|
|
|
147
147
|
## 第五步:解读结果
|
|
148
148
|
|
|
@@ -25,10 +25,11 @@ omk doctor [target] [flags]
|
|
|
25
25
|
- `--executor` `option`:执行器名,默认 claude。指定为测试 fixture 路径可在测试里跑(同 omk doctor)。
|
|
26
26
|
- `--fix` `boolean`:交互式修复:根据 doctor 报告问题,用 LLM agent 修复 skill。
|
|
27
27
|
- `--gate` `boolean`:静默模式,只在 fail 时输出 stderr 摘要,exit code 标识结果。
|
|
28
|
+
- `--global` `boolean`:写全局 ~/.oh-my-knowledge/doctors,而非项目 .omk/doctors
|
|
28
29
|
- `--json` `boolean`:JSON 输出到 stdout,适合 CI / 外部脚本消费。
|
|
29
30
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
30
31
|
- `--model` `option`:LLM model 名,默认 sonnet。
|
|
31
|
-
- `--output-dir` `option
|
|
32
|
+
- `--output-dir` `option`:报告输出目录,默认项目级 .omk/doctors(--global 写全局)。
|
|
32
33
|
- `--samples` `option`:用例文件路径(.json/.yaml)。不传则按 target / cwd 顺序自动发现。
|
|
33
34
|
- `--static-only` `boolean`:离线静态模式,只跑 4 条静态 rule(skill_readable / skill_metadata / dependencies_present / samples_contract_aligned),不调 LLM。
|
|
34
35
|
- `--timeout` `option`:单次 LLM 会话超时秒数,默认 600(10 分钟)。
|
|
@@ -79,6 +80,7 @@ omk eval [flags]
|
|
|
79
80
|
- `--dry-run` `boolean`:只 plan 不实跑
|
|
80
81
|
- `--effort` `option`:被测 LLM 扩展思考预算 low/medium/high/xhigh/max(默认 low;跨 effort 报告不严格可比)。
|
|
81
82
|
- `--executor` `option`:执行器:claude / claude-sdk / codex / codex-sdk / openai-api / gemini / 自定义命令(默认 claude)。
|
|
83
|
+
- `--global` `boolean`:报告写全局 ~/.oh-my-knowledge/reports,而非项目 .omk/
|
|
82
84
|
- `--gold-dir` `option`:gold dataset 目录
|
|
83
85
|
- `--judge-models` `option`:评委配置,格式 executor:model[,...],例 claude:haiku 或 claude:opus,openai:gpt-4o(≥ 2 个 = ensemble)。默认 <executor>:haiku。
|
|
84
86
|
- `--judge-repeat` `option`:每个 dim 评 N 次
|
|
@@ -94,7 +96,7 @@ omk eval [flags]
|
|
|
94
96
|
- `--no-judge` `boolean`:跳过 LLM judge
|
|
95
97
|
- `--no-serve` `boolean`:不启 report server
|
|
96
98
|
- `--no-strict-baseline` `boolean`:关闭 baseline 隔离
|
|
97
|
-
- `--output-dir` `option
|
|
99
|
+
- `--output-dir` `option`:报告输出目录(默认项目级 .omk/reports)
|
|
98
100
|
- `--repeat` `option`:每个 sample 重复跑 N 次
|
|
99
101
|
- `--report-only` `boolean`:生成报告并打印 verdict,但始终 exit 0(不参与 CI gate)。
|
|
100
102
|
- `--resume` `option`:从某次失败 run 续跑
|
|
@@ -234,6 +236,7 @@ omk evolve <skillPath> [flags]
|
|
|
234
236
|
- `--significance-alpha` `option` (默认 `0.05`):显著性门的 diff CI 显著性水平(默认 0.05 = 95% CI)
|
|
235
237
|
- `--skip-connectivity` `boolean`:跳过 LLM 连通性预检
|
|
236
238
|
- `--skip-doctor` `boolean`:跳过 doctor 门禁(escape hatch,自负 garbage-in 风险)
|
|
239
|
+
- `--snapshot-only` `boolean`:只产候选、不写回 source:胜出版本留在 evolve/<skillName>.r{N}.md 供你挑选,再 omk promote 接受。受管 skill 默认会写回 source 并记证据(measurable)。
|
|
237
240
|
- `--stop-on-assertions-pass` `boolean`:普通用例断言全过时提前停止
|
|
238
241
|
- `--target` `option`:目标 composite 分数,达到即停。不传则跑满 rounds
|
|
239
242
|
- `--test-ratio` `option` (默认 `0`):锁定 test 集比例(0..1,默认 0=关),需配 --holdout-ratio。全程不参与选择,收尾读一次给无偏泛化分
|
|
@@ -255,7 +258,7 @@ omk evolve skills/my-skill/SKILL.md --target 4.5 --model opus --improve-model op
|
|
|
255
258
|
|
|
256
259
|
## omk init
|
|
257
260
|
|
|
258
|
-
|
|
261
|
+
初始化一个 omk 项目:在目标目录铺好待测知识载体(skills/)与评测用例(eval-samples.json),供 omk eval / doctor / evolve / observe / list 操作。默认是两版 code-review skill 的 A/B 起步模板。
|
|
259
262
|
|
|
260
263
|
**用法:**
|
|
261
264
|
|
|
@@ -273,13 +276,13 @@ omk init [targetDir] [flags]
|
|
|
273
276
|
|
|
274
277
|
**示例:**
|
|
275
278
|
|
|
276
|
-
>
|
|
279
|
+
> 在当前目录初始化一个 omk 项目
|
|
277
280
|
|
|
278
281
|
```bash
|
|
279
282
|
omk init
|
|
280
283
|
```
|
|
281
284
|
|
|
282
|
-
>
|
|
285
|
+
> 在指定目录初始化一个 omk 项目
|
|
283
286
|
|
|
284
287
|
```bash
|
|
285
288
|
omk init my-project
|
|
@@ -401,10 +404,11 @@ omk observe [sessionsDir] [flags]
|
|
|
401
404
|
**Flags:**
|
|
402
405
|
|
|
403
406
|
- `--from` `option`:起始时间 ISO,优先级高于 --last
|
|
407
|
+
- `--global` `boolean`:写全局 ~/.oh-my-knowledge/observe-health,而非项目 .omk/observe-health
|
|
404
408
|
- `--kb` `option`:知识库 root,启用 KB-aware 分析
|
|
405
409
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
406
410
|
- `--last` `option`:时间窗(7d / 24h / 30m)
|
|
407
|
-
- `--output-dir` `option
|
|
411
|
+
- `--output-dir` `option`:健康报告输出目录,默认项目级 .omk/observe-health(--global 写全局)
|
|
408
412
|
- `--skills` `option`:只看指定 skill,逗号分隔
|
|
409
413
|
- `--to` `option`:结束时间 ISO
|
|
410
414
|
|
|
@@ -431,8 +435,9 @@ omk observe inbox [flags]
|
|
|
431
435
|
- `--by-skill` `boolean`:按 skill 聚合输出
|
|
432
436
|
- `--executor` `option`:LLM 增强复盘使用的执行器
|
|
433
437
|
- `--explore` `option`:抽样 N 条 medium/low 长尾(replaces limit)
|
|
438
|
+
- `--global` `boolean`:直接读取全局 ~/.oh-my-knowledge/observe-inbox(跳过项目级与兜底)。
|
|
434
439
|
- `--include-noise` `boolean`:explore 时也包含 noise 桶
|
|
435
|
-
- `--input-dir` `option`:inbox 数据目录,默认 .omk/
|
|
440
|
+
- `--input-dir` `option`:inbox 数据目录,默认 .omk/observe-inbox(项目级,相对于 cwd);目录不存在时兜底读 ~/.oh-my-knowledge/observe-inbox。
|
|
436
441
|
- `--json` `boolean`:JSON 格式输出
|
|
437
442
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
438
443
|
- `--limit` `option`:限制条数,默认 20
|
|
@@ -457,8 +462,9 @@ omk observe ingest <traceDir> [flags]
|
|
|
457
462
|
|
|
458
463
|
**Flags:**
|
|
459
464
|
|
|
465
|
+
- `--global` `boolean`:写入全局 ~/.oh-my-knowledge/observe-inbox,而非项目 .omk/observe-inbox。
|
|
460
466
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
461
|
-
- `--output-dir` `option`:输出目录,默认 .omk/
|
|
467
|
+
- `--output-dir` `option`:输出目录,默认 .omk/observe-inbox(项目级,相对于 cwd;--global 写全局)。
|
|
462
468
|
|
|
463
469
|
## omk observe show
|
|
464
470
|
|
|
@@ -476,6 +482,7 @@ omk observe show <inboxId> [flags]
|
|
|
476
482
|
|
|
477
483
|
**Flags:**
|
|
478
484
|
|
|
485
|
+
- `--global` `boolean`:直接读取全局 ~/.oh-my-knowledge/observe-inbox(跳过项目级与兜底)。
|
|
479
486
|
- `--input-dir` `option`:inbox 数据目录
|
|
480
487
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
481
488
|
|
|
@@ -586,7 +593,7 @@ omk sample [skillPath] [flags]
|
|
|
586
593
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
587
594
|
- `--model` `option` (默认 `sonnet`):生成 LLM model 名,默认 sonnet。
|
|
588
595
|
- `--no-mock` `boolean`:不生成 mocks,eval 时所有工具调用真实执行。
|
|
589
|
-
- `--observations-dir` `option`:observe inbox 目录(from-traces 模式用),默认项目 .omk/
|
|
596
|
+
- `--observations-dir` `option`:observe inbox 目录(from-traces 模式用),默认项目 .omk/observe-inbox。
|
|
590
597
|
- `--reports-dir` `option`:报告目录(fix 模式用),默认 ~/.oh-my-knowledge/reports。
|
|
591
598
|
- `--skill-dir` `option` (默认 `skills`):skill 根目录,默认 skills。batch 模式扫此目录。
|
|
592
599
|
- `--treatment` `option`:指定 treatment 名(fix 模式用),默认推断自 skill 路径。
|
|
@@ -629,14 +636,16 @@ omk studio [flags]
|
|
|
629
636
|
|
|
630
637
|
**Flags:**
|
|
631
638
|
|
|
632
|
-
- `--analyses-dir` `option
|
|
639
|
+
- `--analyses-dir` `option`:观测健康报告目录(可选,默认项目级 .omk/observe-health,空则全局兜底)
|
|
633
640
|
- `--dev` `boolean`:dev 模式:子进程启动 + 热更新
|
|
641
|
+
- `--doctors-dir` `option`:体检报告目录(可选,默认项目级 .omk/doctors,空则全局兜底)
|
|
642
|
+
- `--global` `boolean`:只看全局 reports / observe-health / doctors / observe-inbox 目录(~/.oh-my-knowledge/*),而非机器级聚合 / 项目优先;managed 不受影响
|
|
634
643
|
- `--host` `option`:监听 host,默认 localhost。改为 0.0.0.0 暴露给局域网
|
|
635
644
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
636
645
|
- `--no-open` `boolean`:不自动打开浏览器
|
|
637
|
-
- `--observations-dir` `option
|
|
646
|
+
- `--observations-dir` `option`:观测收件箱数据目录(可选,默认 .omk/observe-inbox)
|
|
638
647
|
- `--port` `option` (默认 `7799`):监听端口,默认 7799。传 0 让 OS 分配
|
|
639
|
-
- `--reports-dir` `option
|
|
648
|
+
- `--reports-dir` `option`:只看指定报告目录(可选;默认机器级聚合:当前项目 + 全局 + 别项目索引)
|
|
640
649
|
|
|
641
650
|
**示例:**
|
|
642
651
|
|
|
@@ -182,6 +182,9 @@ interface EvolveOptions {
|
|
|
182
182
|
/** Feed rejected candidate edits back into the next round's improvement prompt
|
|
183
183
|
* ("these were tried and did not help — don't repeat them"). Default true. */
|
|
184
184
|
rejectMemory?: boolean;
|
|
185
|
+
/** 是否把胜出版本写回原 source 文件。默认 true(保「一键化」:evolve 跑完源即更新)。
|
|
186
|
+
* `--snapshot-only` 置 false → 不写 source,候选仍落在 `evolve/<skillName>.r{N}.md` 供人工挑选 / promote。 */
|
|
187
|
+
writeBackToSource?: boolean;
|
|
185
188
|
onProgress?: ProgressCallback | null;
|
|
186
189
|
onRoundProgress?: ((progress: EvolveRoundProgressInfo) => void) | null;
|
|
187
190
|
}
|
|
@@ -263,5 +266,5 @@ export interface RoundReport {
|
|
|
263
266
|
report: Report;
|
|
264
267
|
}
|
|
265
268
|
export declare function mergeEvolveReports(roundReports: RoundReport[], skillName: string, totalCostUSD: number, samples?: Sample[], skillPath?: string): Report;
|
|
266
|
-
export declare function evolveSkill({ skillPath, samplesPath, rounds, target, stopOnAssertionsPass, autoFixSamples, sampleFixMaxAttempts, reuseLatestEval, model, judgeModels, improveModel, improveMode, executorName, concurrency, timeoutMs, skipConnectivity, effort, noDiagnostic, skipDoctor, holdoutRatio, significanceGate, significanceAlpha, testRatio, editBudget, rejectMemory, onProgress, onRoundProgress, }: EvolveOptions): Promise<EvolveResult>;
|
|
269
|
+
export declare function evolveSkill({ skillPath, samplesPath, rounds, target, stopOnAssertionsPass, autoFixSamples, sampleFixMaxAttempts, reuseLatestEval, model, judgeModels, improveModel, improveMode, executorName, concurrency, timeoutMs, skipConnectivity, effort, noDiagnostic, skipDoctor, holdoutRatio, significanceGate, significanceAlpha, testRatio, editBudget, rejectMemory, writeBackToSource, onProgress, onRoundProgress, }: EvolveOptions): Promise<EvolveResult>;
|
|
267
270
|
export {};
|
|
@@ -2,8 +2,9 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
|
2
2
|
import { resolve, join, dirname, basename } from 'node:path';
|
|
3
3
|
import { runEvaluation } from '../eval-workflows/run-evaluation.js';
|
|
4
4
|
import { createExecutor, DEFAULT_MODEL, JUDGE_MODEL } from '../executors/index.js';
|
|
5
|
-
import { persistReport, DEFAULT_OUTPUT_DIR,
|
|
6
|
-
import {
|
|
5
|
+
import { persistReport, DEFAULT_OUTPUT_DIR, runIdSuffix, hashString } from '../eval-core/evaluation-reporting.js';
|
|
6
|
+
import { createOverlayReportStore } from '../server/report-store.js';
|
|
7
|
+
import { projectReportsDir, globalReportsDir } from '../eval-core/measurement-dirs.js';
|
|
7
8
|
import { analyzeResults } from '../analysis/report-diagnostics.js';
|
|
8
9
|
import { loadSamples } from '../inputs/load-samples.js';
|
|
9
10
|
import { hashArtifactSource } from '../inputs/content-hash.js';
|
|
@@ -87,7 +88,9 @@ function singleVariantReport(report, variantKey) {
|
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
90
|
async function findReusableBaselineReport(opts) {
|
|
90
|
-
|
|
91
|
+
// baseline 复用读 overlay(项目 .omk/reports ∪ 全局):eval 写默认翻项目后,复用既能命中 eval 新写的项目
|
|
92
|
+
// baseline,又继续覆盖全局(含 evolve 自身写到全局的合并报告),复用命中率与报告数字不降。
|
|
93
|
+
const store = createOverlayReportStore(projectReportsDir(), globalReportsDir());
|
|
91
94
|
const { samples } = loadSamples(opts.samplesPath);
|
|
92
95
|
const artifactHash = opts.artifactHash;
|
|
93
96
|
const reports = await store.findByArtifactHash(artifactHash);
|
|
@@ -544,7 +547,7 @@ export function mergeEvolveReports(roundReports, skillName, totalCostUSD, sample
|
|
|
544
547
|
if (originalKey)
|
|
545
548
|
artifactHashes[variantLabels[i]] = hashes[originalKey];
|
|
546
549
|
}
|
|
547
|
-
const runId = `evolve-${skillName}-${
|
|
550
|
+
const runId = `evolve-${skillName}-${runIdSuffix()}`;
|
|
548
551
|
const report = {
|
|
549
552
|
kind: 'evaluation',
|
|
550
553
|
id: runId,
|
|
@@ -570,7 +573,7 @@ export function mergeEvolveReports(roundReports, skillName, totalCostUSD, sample
|
|
|
570
573
|
report.analysis = analyzeResults(report, { samples });
|
|
571
574
|
return report;
|
|
572
575
|
}
|
|
573
|
-
export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target = null, stopOnAssertionsPass = false, autoFixSamples = false, sampleFixMaxAttempts = 2, reuseLatestEval = false, model = DEFAULT_MODEL, judgeModels, improveModel = DEFAULT_MODEL, improveMode = 'agent', executorName = 'claude', concurrency = 1, timeoutMs, skipConnectivity = false, effort, noDiagnostic, skipDoctor, holdoutRatio = 0, significanceGate = true, significanceAlpha = DEFAULT_BOOTSTRAP_ALPHA, testRatio = 0, editBudget = 0.2, rejectMemory = true, onProgress = null, onRoundProgress = null, }) {
|
|
576
|
+
export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target = null, stopOnAssertionsPass = false, autoFixSamples = false, sampleFixMaxAttempts = 2, reuseLatestEval = false, model = DEFAULT_MODEL, judgeModels, improveModel = DEFAULT_MODEL, improveMode = 'agent', executorName = 'claude', concurrency = 1, timeoutMs, skipConnectivity = false, effort, noDiagnostic, skipDoctor, holdoutRatio = 0, significanceGate = true, significanceAlpha = DEFAULT_BOOTSTRAP_ALPHA, testRatio = 0, editBudget = 0.2, rejectMemory = true, writeBackToSource = true, onProgress = null, onRoundProgress = null, }) {
|
|
574
577
|
if (judgeModels && judgeModels.length > 1) {
|
|
575
578
|
throw new Error('evolveSkill does not support multi-judge ensemble (received '
|
|
576
579
|
+ `${judgeModels.length} judges). Pass a single-judge array, e.g. `
|
|
@@ -712,7 +715,8 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
|
|
|
712
715
|
onRoundProgress({ round: 0, totalRounds: rounds, phase: 'baseline', score: bestScore, costUSD: baselineCost, costReported: baselineCostReported, reused: baselineReused });
|
|
713
716
|
if (stopOnAssertionsPass && allNonTripwireAssertionsPass(baselineReport, baselineVariantKey)) {
|
|
714
717
|
stopReason = 'assertions-pass';
|
|
715
|
-
|
|
718
|
+
if (writeBackToSource)
|
|
719
|
+
writeFileSync(absSkillPath, currentBest);
|
|
716
720
|
const { samples } = loadSamples(absSamplesPath);
|
|
717
721
|
const mergedReport = mergeEvolveReports(roundReports, skillName, totalCostUSD, samples, absSkillPath);
|
|
718
722
|
persistReport(mergedReport, DEFAULT_OUTPUT_DIR);
|
|
@@ -908,8 +912,9 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
|
|
|
908
912
|
break;
|
|
909
913
|
}
|
|
910
914
|
}
|
|
911
|
-
// Write best version back to original file only if an improvement was accepted
|
|
912
|
-
|
|
915
|
+
// Write best version back to original file only if an improvement was accepted.
|
|
916
|
+
// `--snapshot-only`(writeBackToSource=false)跳过写回:候选仍在 evolve/<skillName>.r{N}.md,源不动。
|
|
917
|
+
if (bestRound > 0 && writeBackToSource) {
|
|
913
918
|
writeFileSync(absSkillPath, currentBest);
|
|
914
919
|
}
|
|
915
920
|
// Merge all round reports into one and persist
|
|
@@ -17,6 +17,7 @@ export default class Doctor extends BaseCommand {
|
|
|
17
17
|
samples: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
timeout: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'output-dir': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
dimensions: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
22
|
'static-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
23
|
fix: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
1
|
import { existsSync, statSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
2
|
import { dirname, join, resolve } from 'node:path';
|
|
4
3
|
import { Args, Flags } from '@oclif/core';
|
|
@@ -8,6 +7,9 @@ import { numberStringParser } from '../oclif/parsers.js';
|
|
|
8
7
|
import { CliExit } from '../lib/cli-exit.js';
|
|
9
8
|
import { tCli } from '../lib/i18n.js';
|
|
10
9
|
import { makeDoctorProgress } from '../lib/progress.js';
|
|
10
|
+
import { DEFAULT_DOCTORS_DIR } from '../../eval-core/default-dirs.js';
|
|
11
|
+
import { indexDoctorWrite, removeDoctorCard } from '../../eval-core/artifact-index.js';
|
|
12
|
+
import { projectDoctorsDir, globalDoctorsDir } from '../../eval-core/measurement-dirs.js';
|
|
11
13
|
const DEFAULT_SAMPLE_FILENAMES = ['eval-samples.json', 'eval-samples.yaml', 'eval-samples.yml'];
|
|
12
14
|
function findSamplesInDir(dir) {
|
|
13
15
|
for (const name of DEFAULT_SAMPLE_FILENAMES) {
|
|
@@ -131,8 +133,14 @@ export default class Doctor extends BaseCommand {
|
|
|
131
133
|
}),
|
|
132
134
|
'output-dir': Flags.string({
|
|
133
135
|
description: bilingual({
|
|
134
|
-
zh: '
|
|
135
|
-
en: 'Report output dir, default
|
|
136
|
+
zh: '报告输出目录,默认项目级 .omk/doctors(--global 写全局)。',
|
|
137
|
+
en: 'Report output dir, default project-level .omk/doctors (--global for global).',
|
|
138
|
+
}),
|
|
139
|
+
}),
|
|
140
|
+
global: Flags.boolean({
|
|
141
|
+
description: bilingual({
|
|
142
|
+
zh: '写全局 ~/.oh-my-knowledge/doctors,而非项目 .omk/doctors',
|
|
143
|
+
en: 'Write to global ~/.oh-my-knowledge/doctors instead of project .omk/doctors',
|
|
136
144
|
}),
|
|
137
145
|
}),
|
|
138
146
|
dimensions: Flags.string({
|
|
@@ -260,7 +268,9 @@ export default class Doctor extends BaseCommand {
|
|
|
260
268
|
}
|
|
261
269
|
renderDoctorReportText(report, lang);
|
|
262
270
|
}
|
|
263
|
-
persistDoctorReport(report, flags['output-dir']
|
|
271
|
+
persistDoctorReport(report, flags['output-dir']
|
|
272
|
+
? resolve(flags['output-dir'])
|
|
273
|
+
: (flags.global ? globalDoctorsDir() : projectDoctorsDir()));
|
|
264
274
|
if (flags.fix) {
|
|
265
275
|
const existing = report;
|
|
266
276
|
if (existing.outcome !== 'failed') {
|
|
@@ -279,7 +289,7 @@ export default class Doctor extends BaseCommand {
|
|
|
279
289
|
// scanDoctorReports 扫盘成本)。50 = ~每天 1 跑撑 1.5 个月 sparkline,够用。
|
|
280
290
|
const DOCTOR_HISTORY_MAX_PER_SKILL = 50;
|
|
281
291
|
function persistDoctorReport(report, outputDir) {
|
|
282
|
-
const dir = outputDir ??
|
|
292
|
+
const dir = outputDir ?? DEFAULT_DOCTORS_DIR;
|
|
283
293
|
mkdirSync(dir, { recursive: true });
|
|
284
294
|
const safeId = report.id.replace(/[/\\:*?"<>|]/g, '_');
|
|
285
295
|
for (const skill of report.skills) {
|
|
@@ -301,7 +311,14 @@ function persistDoctorReport(report, outputDir) {
|
|
|
301
311
|
outcome: skill.status === 'fail' ? 'failed' : skill.status === 'warn' ? 'warnings_only' : 'passed',
|
|
302
312
|
};
|
|
303
313
|
const safeName = skill.skillName.replace(/[/\\:*?"<>|]/g, '_');
|
|
304
|
-
|
|
314
|
+
const cardId = `${safeName}-${safeId}`;
|
|
315
|
+
const filePath = join(dir, `${cardId}.json`);
|
|
316
|
+
writeFileSync(filePath, JSON.stringify(perSkill, null, 2), 'utf8');
|
|
317
|
+
// 产物发现索引:per-skill 报告落项目本地后,best-effort 追加全局轻卡片,让 studio 跨项目聚合。
|
|
318
|
+
indexDoctorWrite({
|
|
319
|
+
id: cardId, path: filePath, skillName: skill.skillName, reportId: report.id, timestamp: report.timestamp,
|
|
320
|
+
status: skill.status, passCount: counts.pass, warnCount: counts.warn, failCount: counts.fail,
|
|
321
|
+
}, dir);
|
|
305
322
|
pruneDoctorHistory(dir, skill.skillName, DOCTOR_HISTORY_MAX_PER_SKILL);
|
|
306
323
|
}
|
|
307
324
|
}
|
|
@@ -332,5 +349,8 @@ export function pruneDoctorHistory(dir, skillName, maxKeep) {
|
|
|
332
349
|
unlinkSync(join(dir, file));
|
|
333
350
|
}
|
|
334
351
|
catch { /* ignore */ }
|
|
352
|
+
// 连带删卡片:否则被 prune 掉的报告会经 listDoctorCards 合并在本项目 studio「复活」(正文已删、卡片还在)。
|
|
353
|
+
// 卡片 id = 文件 stem(`{name}-{id}`),与 indexDoctorWrite 写入口径一致。
|
|
354
|
+
removeDoctorCard(file.replace(/\.json$/, ''));
|
|
335
355
|
}
|
|
336
356
|
}
|
|
@@ -4,7 +4,7 @@ import { BaseCommand } from '../../../oclif/base-command.js';
|
|
|
4
4
|
import { LANG_FLAG, bilingual } from '../../../oclif/i18n.js';
|
|
5
5
|
import { integerStringParser } from '../../../oclif/parsers.js';
|
|
6
6
|
import { CliExit } from '../../../lib/cli-exit.js';
|
|
7
|
-
import {
|
|
7
|
+
import { projectReportsDir, globalReportsDir } from '../../../../eval-core/measurement-dirs.js';
|
|
8
8
|
import { requireEvaluationReport } from '../../../lib/shared.js';
|
|
9
9
|
export default class EvalGoldCompare extends BaseCommand {
|
|
10
10
|
static description = bilingual({
|
|
@@ -62,7 +62,7 @@ export default class EvalGoldCompare extends BaseCommand {
|
|
|
62
62
|
}
|
|
63
63
|
const { loadGoldDataset } = await import('../../../../grading/gold-dataset.js');
|
|
64
64
|
const { compareGoldToReport, formatGoldCompare } = await import('../../../../grading/gold-cli.js');
|
|
65
|
-
const { createFileStore } = await import('../../../../server/report-store.js');
|
|
65
|
+
const { createFileStore, createOverlayReportStore } = await import('../../../../server/report-store.js');
|
|
66
66
|
const { dataset, issues } = loadGoldDataset(goldDir);
|
|
67
67
|
if (!dataset) {
|
|
68
68
|
console.error('Cannot load gold dataset:');
|
|
@@ -72,8 +72,11 @@ export default class EvalGoldCompare extends BaseCommand {
|
|
|
72
72
|
}
|
|
73
73
|
for (const i of issues)
|
|
74
74
|
console.error(`warn: ${i.message}`);
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// 显式 --reports-dir 固定该目录;默认 overlay(项目 .omk/reports 盖全局),get(reportId) 项目→全局兜底,
|
|
76
|
+
// 不因 eval 写默认翻项目而对比落空。
|
|
77
|
+
const store = flags['reports-dir']
|
|
78
|
+
? createFileStore(resolve(flags['reports-dir']))
|
|
79
|
+
: createOverlayReportStore(projectReportsDir(), globalReportsDir());
|
|
77
80
|
const report = requireEvaluationReport(await store.get(reportId), reportId, lang);
|
|
78
81
|
const samples = Math.max(100, Number(flags['bootstrap-samples'] ?? 1000) || 1000);
|
|
79
82
|
const seedVal = flags.seed != null ? Number(flags.seed) : undefined;
|
|
@@ -18,6 +18,7 @@ export default class Eval extends BaseCommand {
|
|
|
18
18
|
executor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'judge-models': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
20
|
'output-dir': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
21
22
|
'no-judge': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
23
|
'no-cache': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
24
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -84,8 +84,11 @@ function batchItemFallbackReport(batch, item) {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
async function loadBatchChildReports(batch, reportsDir, lang) {
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// 读侧走 overlay(本次 reportsDir 优先 → 全局兜底):写默认翻项目后,batch.json 引用的存量 / 全局子报告
|
|
88
|
+
// 仍能 get 命中,不致降级到 batchItemFallbackReport(来自 batch metadata 的过时数据)。
|
|
89
|
+
const { createOverlayReportStore } = await import('../../../server/report-store.js');
|
|
90
|
+
const { globalReportsDir } = await import('../../../eval-core/measurement-dirs.js');
|
|
91
|
+
const store = createOverlayReportStore(reportsDir, globalReportsDir());
|
|
89
92
|
const reports = [];
|
|
90
93
|
for (const item of batch.items) {
|
|
91
94
|
const loaded = await store.get(item.reportId);
|
|
@@ -134,6 +137,8 @@ async function announceSavedReport({ report, filePath, reportsDir, values, lang,
|
|
|
134
137
|
process.stderr.write(tCli('cli.run.report_saved', lang, { path: filePath }));
|
|
135
138
|
if (!values['no-serve'] && process.stdout.isTTY) {
|
|
136
139
|
const { createReportServer } = await import('../../../server/report-server.js');
|
|
140
|
+
// eval 结束的即时 serve 刻意钉本次 outputDir(--global 时即全局),不走 overlay:它的语义是「看我刚跑的这份」,
|
|
141
|
+
// 单目录保证刚写的报告(含 --global 写到全局的)一定可见;浏览全机器历史是 omk studio 的事(默认 overlay 项目优先)。
|
|
137
142
|
const server = createReportServer({ reportsDir });
|
|
138
143
|
const serverUrl = await server.start();
|
|
139
144
|
const reportUrl = `${serverUrl}/reports/${report.id}`;
|
|
@@ -368,7 +373,13 @@ export default class Eval extends BaseCommand {
|
|
|
368
373
|
}),
|
|
369
374
|
}),
|
|
370
375
|
'output-dir': Flags.string({
|
|
371
|
-
description: bilingual({ zh: '
|
|
376
|
+
description: bilingual({ zh: '报告输出目录(默认项目级 .omk/reports)', en: 'Report output dir (default project .omk/reports)' }),
|
|
377
|
+
}),
|
|
378
|
+
global: Flags.boolean({
|
|
379
|
+
description: bilingual({
|
|
380
|
+
zh: '报告写全局 ~/.oh-my-knowledge/reports,而非项目 .omk/',
|
|
381
|
+
en: 'Write report to global ~/.oh-my-knowledge/reports instead of project .omk/',
|
|
382
|
+
}),
|
|
372
383
|
}),
|
|
373
384
|
// ── 评测 toggle ──
|
|
374
385
|
'no-judge': Flags.boolean({
|
|
@@ -41,6 +41,7 @@ export default class Evolve extends BaseCommand {
|
|
|
41
41
|
'auto-fix-samples': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
42
42
|
'sample-fix-max-attempts': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
43
43
|
'reuse-latest-eval': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
44
|
+
'snapshot-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
44
45
|
'improve-mode': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
45
46
|
'holdout-ratio': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
46
47
|
'no-significance-gate': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -7,6 +7,17 @@ import { enumStringParser, integerStringParser, numberStringParser } from '../oc
|
|
|
7
7
|
import { CliExit } from '../lib/cli-exit.js';
|
|
8
8
|
import { tCli } from '../lib/i18n.js';
|
|
9
9
|
import { makeOnProgress } from '../lib/progress.js';
|
|
10
|
+
/** 受管联动旁路:evolve 写回 source 后记证据 + re-baseline。任何异常都不该让 evolve 失败,
|
|
11
|
+
* 故 try/catch 吞掉、返回 null(同 eval 的 recordEvidenceSafely 口径)。 */
|
|
12
|
+
async function recordEvolveOutcomeSafely(input) {
|
|
13
|
+
try {
|
|
14
|
+
const { recordEvolveOutcome } = await import('../lib/record-evolve-outcome.js');
|
|
15
|
+
return await recordEvolveOutcome(input);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
10
21
|
const VALID_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
11
22
|
function validateEvolveEffort(raw, lang) {
|
|
12
23
|
if (!VALID_EFFORTS.has(raw)) {
|
|
@@ -77,6 +88,9 @@ export async function runEvolve(args, flags, lang) {
|
|
|
77
88
|
throw new CliExit(1);
|
|
78
89
|
}
|
|
79
90
|
const skillPath = resolvedInput.skillPath;
|
|
91
|
+
// evolve 目标的形态(目录-skill / 文件-skill),供受管联动按形态精确匹配记录。**取解析后形态**而非「入参是不是
|
|
92
|
+
// 目录」:帮助文档鼓励传 `skills/foo/SKILL.md`,若按入参判会得 false、匹配不到 install 落的目录记录(漂移永不消)。
|
|
93
|
+
const skillIsDir = resolvedInput.isDirectorySkill;
|
|
80
94
|
let samplesFile = flags.samples;
|
|
81
95
|
if (samplesFile === 'eval-samples.json' && !existsSync(resolve(samplesFile))) {
|
|
82
96
|
samplesFile = resolvedInput.samplesPath;
|
|
@@ -183,6 +197,8 @@ export async function runEvolve(args, flags, lang) {
|
|
|
183
197
|
testRatio: Number(flags['test-ratio']),
|
|
184
198
|
editBudget: flags['no-edit-budget'] ? 0 : Number(flags['edit-budget']),
|
|
185
199
|
rejectMemory: !flags['no-reject-memory'],
|
|
200
|
+
// --snapshot-only:不写回 source,候选只留在 evolve/<skillName>.r{N}.md(供人工挑选 / promote)。
|
|
201
|
+
writeBackToSource: !flags['snapshot-only'],
|
|
186
202
|
improveMode: flags['improve-mode'] === 'rewrite' ? 'rewrite' : 'agent',
|
|
187
203
|
onProgress: makeOnProgress(lang),
|
|
188
204
|
onRoundProgress({ round, totalRounds: _totalRounds, phase, score, delta, accepted, costUSD, costReported, error, significant }) {
|
|
@@ -246,6 +262,28 @@ export async function runEvolve(args, flags, lang) {
|
|
|
246
262
|
if (result.reportId) {
|
|
247
263
|
process.stderr.write(tCli('cli.evolve.report_link', lang, { id: result.reportId }));
|
|
248
264
|
}
|
|
265
|
+
if (flags['snapshot-only']) {
|
|
266
|
+
// 不写回 source —— 候选留在 evolve/ 供人工挑选;受管记录不动。
|
|
267
|
+
process.stderr.write(tCli('cli.evolve.snapshot_only_hint', lang, {
|
|
268
|
+
dir: join(resolve(skillPath, '..'), 'evolve'),
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// 受管 skill:把胜出版本记成带 verdict 的证据 + re-baseline → omk list 显 measurable。
|
|
273
|
+
// 升 promoted 仍由人 omk promote 决定(统计门 ≠ 人的接受)。未纳管 / 无改进 → 静默 no-op。
|
|
274
|
+
const recorded = await recordEvolveOutcomeSafely({
|
|
275
|
+
reportId: result.reportId,
|
|
276
|
+
bestRound: result.bestRound,
|
|
277
|
+
skillPath: resolvedInput.skillPath,
|
|
278
|
+
skillDir: resolvedInput.skillDir,
|
|
279
|
+
isDirectorySkill: skillIsDir,
|
|
280
|
+
});
|
|
281
|
+
if (recorded) {
|
|
282
|
+
process.stderr.write(tCli('cli.evolve.evidence_recorded_managed', lang, {
|
|
283
|
+
name: recorded.name, verdict: recorded.verdict,
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
249
287
|
console.log(JSON.stringify(result, null, 2));
|
|
250
288
|
}
|
|
251
289
|
catch (err) {
|
|
@@ -398,6 +436,13 @@ export default class Evolve extends BaseCommand {
|
|
|
398
436
|
}),
|
|
399
437
|
default: false,
|
|
400
438
|
}),
|
|
439
|
+
'snapshot-only': Flags.boolean({
|
|
440
|
+
description: bilingual({
|
|
441
|
+
zh: '只产候选、不写回 source:胜出版本留在 evolve/<skillName>.r{N}.md 供你挑选,再 omk promote 接受。受管 skill 默认会写回 source 并记证据(measurable)。',
|
|
442
|
+
en: 'Produce candidates only, do not write back to source: the winner stays in evolve/<skillName>.r{N}.md for you to pick and then omk promote. By default a managed skill is written back and evidence is recorded (measurable).',
|
|
443
|
+
}),
|
|
444
|
+
default: false,
|
|
445
|
+
}),
|
|
401
446
|
'improve-mode': Flags.string({
|
|
402
447
|
description: bilingual({
|
|
403
448
|
zh: '改写策略(默认:agent)',
|
|
@@ -3,6 +3,15 @@ import { Args } from '@oclif/core';
|
|
|
3
3
|
import { LANG_FLAG, bilingual, resolveLang } from '../oclif/i18n.js';
|
|
4
4
|
import { BaseCommand } from '../oclif/base-command.js';
|
|
5
5
|
import { tCli } from '../lib/i18n.js';
|
|
6
|
+
// 预置 .omk/.gitignore:测量 bulk + doctor --fix 备份(项目本地、不该入库)默认不入库;
|
|
7
|
+
// managed/ 治理档案 + 配置不在此列,默认 track。
|
|
8
|
+
const INIT_OMK_GITIGNORE = `# omk 测量 bulk + doctor --fix 备份(项目本地)——不入库;前导 / 锚定 .omk/ 顶层,不误伤嵌套同名目录。
|
|
9
|
+
/observe-health/
|
|
10
|
+
/doctors/
|
|
11
|
+
/observe-inbox/
|
|
12
|
+
/reports/
|
|
13
|
+
/backups/
|
|
14
|
+
`;
|
|
6
15
|
const INIT_SAMPLES = `[
|
|
7
16
|
{
|
|
8
17
|
"sample_id": "s001",
|
|
@@ -83,21 +92,21 @@ description: 多维度代码审查,覆盖安全 / 健壮 / 可维护 / 性能,
|
|
|
83
92
|
`;
|
|
84
93
|
export default class Init extends BaseCommand {
|
|
85
94
|
static description = bilingual({
|
|
86
|
-
zh: '
|
|
87
|
-
en: '
|
|
95
|
+
zh: '初始化一个 omk 项目:在目标目录铺好待测知识载体(skills/)与评测用例(eval-samples.json),供 omk eval / doctor / evolve / observe / list 操作。默认是两版 code-review skill 的 A/B 起步模板。',
|
|
96
|
+
en: 'Initialize an omk project: scaffold knowledge artifacts (skills/) and eval samples (eval-samples.json) in the target dir for omk eval / doctor / evolve / observe / list to work on. Ships a two-variant code-review A/B starter template by default.',
|
|
88
97
|
});
|
|
89
98
|
static examples = [
|
|
90
99
|
{
|
|
91
100
|
description: bilingual({
|
|
92
|
-
zh: '
|
|
93
|
-
en: '
|
|
101
|
+
zh: '在当前目录初始化一个 omk 项目',
|
|
102
|
+
en: 'Initialize an omk project in the current directory',
|
|
94
103
|
}),
|
|
95
104
|
command: '<%= config.bin %> init',
|
|
96
105
|
},
|
|
97
106
|
{
|
|
98
107
|
description: bilingual({
|
|
99
|
-
zh: '
|
|
100
|
-
en: '
|
|
108
|
+
zh: '在指定目录初始化一个 omk 项目',
|
|
109
|
+
en: 'Initialize an omk project in a specified directory',
|
|
101
110
|
}),
|
|
102
111
|
command: '<%= config.bin %> init my-project',
|
|
103
112
|
},
|
|
@@ -139,6 +148,9 @@ export default class Init extends BaseCommand {
|
|
|
139
148
|
writeFileSync(join(targetDir, 'eval-samples.json'), INIT_SAMPLES);
|
|
140
149
|
writeFileSync(join(targetDir, 'skills', 'code-review-v1', 'SKILL.md'), INIT_SKILL_V1);
|
|
141
150
|
writeFileSync(join(targetDir, 'skills', 'code-review-v2', 'SKILL.md'), INIT_SKILL_V2);
|
|
151
|
+
// 像 dvc init 那样预置忽略规则,开发者不会误把测量 bulk 提交进库。
|
|
152
|
+
mkdirSync(join(targetDir, '.omk'), { recursive: true });
|
|
153
|
+
writeFileSync(join(targetDir, '.omk', '.gitignore'), INIT_OMK_GITIGNORE);
|
|
142
154
|
console.log(tCli('cli.init.scaffolded', lang, { dir: targetDir }));
|
|
143
155
|
console.log('');
|
|
144
156
|
console.log(tCli('cli.init.next_steps_title', lang));
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { BaseCommand } from '../oclif/base-command.js';
|
|
2
|
-
import { probeSourceState } from '../lib/source-probe.js';
|
|
3
2
|
import { sanitizeCell } from '../lib/cell-format.js';
|
|
4
3
|
import { type ManagedListRow } from '../../managed/index.js';
|
|
5
4
|
import type { CliLang } from '../lib/i18n.js';
|
|
6
|
-
export { probeSourceState };
|
|
7
5
|
/** CJK 全角字符按 2 列计宽,使含中文表头的列也能对齐。 */
|
|
8
6
|
export declare function dispWidth(s: string): number;
|
|
9
7
|
/** 按**显示宽度**截断(不是 code unit):逐码点累加 dispWidth,绝不切断 surrogate 对、CJK 也不溢出列。 */
|
|
@@ -2,12 +2,8 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import { LANG_FLAG, bilingual } from '../oclif/i18n.js';
|
|
3
3
|
import { BaseCommand } from '../oclif/base-command.js';
|
|
4
4
|
import { tCli } from '../lib/i18n.js';
|
|
5
|
-
import { probeSourceState } from '../lib/source-probe.js';
|
|
6
5
|
import { sanitizeCell } from '../lib/cell-format.js';
|
|
7
|
-
import {
|
|
8
|
-
// 源探测(含 DoS / 软硬链 / 投毒守卫)已抽到 ../lib/source-probe.js,promote 复用同一套守卫与 drift 判定。
|
|
9
|
-
// 此处 re-export 保持既有 import 入口不破(test/cli/list-probe.test.ts 仍从本文件取 probeSourceState)。
|
|
10
|
-
export { probeSourceState };
|
|
6
|
+
import { globalManagedDir, listManagedRows, managedDir, resolveManagedDir, } from '../../managed/index.js';
|
|
11
7
|
/** CJK 全角字符按 2 列计宽,使含中文表头的列也能对齐。 */
|
|
12
8
|
export function dispWidth(s) {
|
|
13
9
|
let w = 0;
|
|
@@ -82,8 +78,7 @@ export default class List extends BaseCommand {
|
|
|
82
78
|
const lang = this.lang;
|
|
83
79
|
await this.runWithCliExit(async () => {
|
|
84
80
|
const dir = flags.global ? globalManagedDir() : resolveManagedDir(managedDir());
|
|
85
|
-
const
|
|
86
|
-
const rows = buildManagedListRows(records, probeSourceState);
|
|
81
|
+
const rows = listManagedRows(dir);
|
|
87
82
|
if (flags.json) {
|
|
88
83
|
// 版本化信封 —— 与 eval / doctor / diagnosis 等机读出口一致(都带 schemaVersion),让未来字段增删 /
|
|
89
84
|
// 改名是可检测的版本 bump,而非脚本静默 break。rows 的逐行形态见 ManagedListRow。
|
|
@@ -7,6 +7,7 @@ export default class ObserveInbox extends BaseCommand {
|
|
|
7
7
|
static flags: {
|
|
8
8
|
lang: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
'input-dir': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
skill: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
explore: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|