@shirokuma-library/shirokuma-docs 0.3.0-alpha.17 → 0.3.0-alpha.19

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.

Potentially problematic release.


This version of @shirokuma-library/shirokuma-docs might be problematic. Click here for more details.

Files changed (50) hide show
  1. package/THIRD_PARTY_NOTICES.md +1 -14
  2. package/dist/commands/plugin-install-local.d.ts +49 -0
  3. package/dist/commands/plugin-install-local.d.ts.map +1 -0
  4. package/dist/commands/plugin-install-local.js +199 -0
  5. package/dist/commands/plugin-install-local.js.map +1 -0
  6. package/dist/commands/skill/benchmark.d.ts +29 -0
  7. package/dist/commands/skill/benchmark.d.ts.map +1 -0
  8. package/dist/commands/skill/benchmark.js +344 -0
  9. package/dist/commands/skill/benchmark.js.map +1 -0
  10. package/dist/commands/skill/eval.d.ts +36 -0
  11. package/dist/commands/skill/eval.d.ts.map +1 -0
  12. package/dist/commands/skill/eval.js +331 -0
  13. package/dist/commands/skill/eval.js.map +1 -0
  14. package/dist/commands/skill/index.d.ts +13 -0
  15. package/dist/commands/skill/index.d.ts.map +1 -0
  16. package/dist/commands/skill/index.js +122 -0
  17. package/dist/commands/skill/index.js.map +1 -0
  18. package/dist/commands/skill/init-skill.d.ts +16 -0
  19. package/dist/commands/skill/init-skill.d.ts.map +1 -0
  20. package/dist/commands/skill/init-skill.js +175 -0
  21. package/dist/commands/skill/init-skill.js.map +1 -0
  22. package/dist/commands/skill/optimize.d.ts +35 -0
  23. package/dist/commands/skill/optimize.d.ts.map +1 -0
  24. package/dist/commands/skill/optimize.js +326 -0
  25. package/dist/commands/skill/optimize.js.map +1 -0
  26. package/dist/commands/skill/package.d.ts +21 -0
  27. package/dist/commands/skill/package.d.ts.map +1 -0
  28. package/dist/commands/skill/package.js +135 -0
  29. package/dist/commands/skill/package.js.map +1 -0
  30. package/dist/commands/skill/skill-md-parser.d.ts +24 -0
  31. package/dist/commands/skill/skill-md-parser.d.ts.map +1 -0
  32. package/dist/commands/skill/skill-md-parser.js +66 -0
  33. package/dist/commands/skill/skill-md-parser.js.map +1 -0
  34. package/dist/commands/skill/types.d.ts +196 -0
  35. package/dist/commands/skill/types.d.ts.map +1 -0
  36. package/dist/commands/skill/types.js +60 -0
  37. package/dist/commands/skill/types.js.map +1 -0
  38. package/dist/commands/skill/validate.d.ts +20 -0
  39. package/dist/commands/skill/validate.d.ts.map +1 -0
  40. package/dist/commands/skill/validate.js +209 -0
  41. package/dist/commands/skill/validate.js.map +1 -0
  42. package/dist/index.js +16 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/utils/skills-repo.d.ts +28 -1
  45. package/dist/utils/skills-repo.d.ts.map +1 -1
  46. package/dist/utils/skills-repo.js +58 -2
  47. package/dist/utils/skills-repo.js.map +1 -1
  48. package/i18n/cli/en.json +24 -0
  49. package/i18n/cli/ja.json +24 -0
  50. package/package.json +3 -3
@@ -1,18 +1,5 @@
1
1
  # Third-Party Notices
2
2
 
3
3
  This project is licensed under the MIT License.
4
- The following components are licensed under different terms.
5
4
 
6
- ## Apache License 2.0
7
-
8
- The following files are derived from [anthropics/skills](https://github.com/anthropics/skills)
9
- (skill-creator), licensed under Apache License 2.0:
10
-
11
- - plugin/shirokuma-skills-{en,ja}/skills/managing-skills/scripts/init_skill.py
12
- - plugin/shirokuma-skills-{en,ja}/skills/managing-skills/scripts/package_skill.py
13
- - plugin/shirokuma-skills-{en,ja}/skills/managing-skills/scripts/quick_validate.py
14
- - plugin/shirokuma-skills-{en,ja}/skills/managing-skills/reference-workflows.md
15
- - plugin/shirokuma-skills-{en,ja}/skills/managing-skills/reference-output-patterns.md
16
-
17
- These files have been modified for use in shirokuma-docs.
18
- See the LICENSE.txt in each managing-skills/scripts directory for the full Apache License 2.0 text.
5
+ For third-party license notices related to bundled plugins, see `plugin/THIRD_PARTY_NOTICES.md`.
@@ -0,0 +1,49 @@
1
+ /**
2
+ * plugin-install-local command - ローカルプラグインをグローバルキャッシュにインストール
3
+ *
4
+ * @description plugin/{pluginName}/ ディレクトリをグローバルキャッシュ
5
+ * (~/.claude/plugins/cache/shirokuma-library/{pluginName}/local/) にコピーし、
6
+ * ルールを .claude/rules/shirokuma/ に展開する。
7
+ *
8
+ * 開発中のプラグイン変更を `shirokuma-docs update` を実行せずに
9
+ * 即座に反映させるためのコマンド。
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * # 言語設定に基づき自動選択(最も一般的な使い方)
14
+ * shirokuma-docs plugin-install-local
15
+ *
16
+ * # 特定のプラグインを指定
17
+ * shirokuma-docs plugin-install-local --plugin shirokuma-skills-ja
18
+ *
19
+ * # 全プラグインをインストール
20
+ * shirokuma-docs plugin-install-local --all
21
+ *
22
+ * # ドライラン(変更なし)
23
+ * shirokuma-docs plugin-install-local --dry-run
24
+ * ```
25
+ */
26
+ /**
27
+ * plugin-install-local command options
28
+ */
29
+ interface PluginInstallLocalOptions {
30
+ /** Project path */
31
+ project: string;
32
+ /** Plugin name to install (auto-detected from language setting if not specified) */
33
+ plugin?: string;
34
+ /** Install all plugins */
35
+ all?: boolean;
36
+ /** Preview mode (no actual changes) */
37
+ dryRun?: boolean;
38
+ /** Verbose logging */
39
+ verbose?: boolean;
40
+ }
41
+ /**
42
+ * plugin-install-local command handler
43
+ *
44
+ * @param options - Command options
45
+ * @returns Exit code (0 = success, 1 = error)
46
+ */
47
+ export declare function pluginInstallLocalCommand(options: PluginInstallLocalOptions): Promise<number>;
48
+ export {};
49
+ //# sourceMappingURL=plugin-install-local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-install-local.d.ts","sourceRoot":"","sources":["../../src/commands/plugin-install-local.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAoBH;;GAEG;AACH,UAAU,yBAAyB;IACjC,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAYD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6GnG"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * plugin-install-local command - ローカルプラグインをグローバルキャッシュにインストール
3
+ *
4
+ * @description plugin/{pluginName}/ ディレクトリをグローバルキャッシュ
5
+ * (~/.claude/plugins/cache/shirokuma-library/{pluginName}/local/) にコピーし、
6
+ * ルールを .claude/rules/shirokuma/ に展開する。
7
+ *
8
+ * 開発中のプラグイン変更を `shirokuma-docs update` を実行せずに
9
+ * 即座に反映させるためのコマンド。
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * # 言語設定に基づき自動選択(最も一般的な使い方)
14
+ * shirokuma-docs plugin-install-local
15
+ *
16
+ * # 特定のプラグインを指定
17
+ * shirokuma-docs plugin-install-local --plugin shirokuma-skills-ja
18
+ *
19
+ * # 全プラグインをインストール
20
+ * shirokuma-docs plugin-install-local --all
21
+ *
22
+ * # ドライラン(変更なし)
23
+ * shirokuma-docs plugin-install-local --dry-run
24
+ * ```
25
+ */
26
+ import { join } from "node:path";
27
+ import { existsSync } from "node:fs";
28
+ import { validateProjectPath } from "../utils/sanitize.js";
29
+ import { createLogger } from "../utils/logger.js";
30
+ import { t } from "../utils/i18n.js";
31
+ import { PLUGIN_NAME, PLUGIN_NAME_JA, PLUGIN_NAME_HOOKS, getBundledPluginPathFor, deployRules, getGlobalCachePath, getLanguageSetting, installLocalPlugin, } from "../utils/skills-repo.js";
32
+ /**
33
+ * plugin-install-local command handler
34
+ *
35
+ * @param options - Command options
36
+ * @returns Exit code (0 = success, 1 = error)
37
+ */
38
+ export async function pluginInstallLocalCommand(options) {
39
+ const verbose = options.verbose ?? false;
40
+ const logger = createLogger(verbose);
41
+ const T = (key, params) => t(`commands.pluginInstallLocal.${key}`, params);
42
+ const projectPath = validateProjectPath(options.project);
43
+ // .claude ディレクトリの存在確認
44
+ if (!existsSync(join(projectPath, ".claude"))) {
45
+ logger.error(T("errorNoClaudeDir"));
46
+ return 1;
47
+ }
48
+ if (options.dryRun) {
49
+ logger.info(T("dryRunBanner"));
50
+ }
51
+ // 言語設定を1回だけ取得
52
+ const languageSetting = getLanguageSetting(projectPath);
53
+ // インストール対象プラグインを決定
54
+ const pluginsToInstall = resolveTargetPlugins(options, languageSetting);
55
+ if (pluginsToInstall.length === 0) {
56
+ logger.error(T("errorNoPluginResolved"));
57
+ return 1;
58
+ }
59
+ // 各プラグインをインストール
60
+ const installResults = [];
61
+ let hasError = false;
62
+ for (const pluginName of pluginsToInstall) {
63
+ const sourcePath = getBundledPluginPathFor(pluginName);
64
+ if (!existsSync(sourcePath)) {
65
+ logger.error(T("errorPluginDirNotFound", { pluginName, sourcePath }));
66
+ hasError = true;
67
+ continue;
68
+ }
69
+ logger.info(T("installingPlugin", { pluginName }));
70
+ const result = installLocalPlugin(pluginName, sourcePath, { dryRun: options.dryRun });
71
+ installResults.push(result);
72
+ if (result.success) {
73
+ logger.success(T("installedPlugin", { pluginName, cachePath: result.cachePath }));
74
+ }
75
+ else {
76
+ logger.error(T("errorInstallFailed", { pluginName, reason: result.message ?? "unknown error" }));
77
+ hasError = true;
78
+ }
79
+ }
80
+ if (hasError) {
81
+ return 1;
82
+ }
83
+ // ルール展開
84
+ // local/ キャッシュが存在する場合は preferLocal=true で優先解決する
85
+ const useJaRules = languageSetting === "japanese";
86
+ // 言語設定に合わせてルール展開元を選択
87
+ // --plugin 明示指定の場合はそのプラグインを使用、それ以外は言語設定に従う
88
+ let rulesPluginName;
89
+ if (options.plugin) {
90
+ rulesPluginName = options.plugin;
91
+ }
92
+ else {
93
+ rulesPluginName = useJaRules ? PLUGIN_NAME_JA : PLUGIN_NAME;
94
+ }
95
+ // local/ を優先して使用(インストールしたばかりのものを反映)
96
+ const rulesSource = getGlobalCachePath(rulesPluginName, undefined, { preferLocal: true })
97
+ ?? getBundledPluginPathFor(rulesPluginName);
98
+ logger.info(T("deployingRules", { pluginName: rulesPluginName }));
99
+ const deployResult = await deployRules(projectPath, {
100
+ dryRun: options.dryRun,
101
+ verbose,
102
+ bundledPluginPath: rulesSource,
103
+ });
104
+ const rulesDeployed = deployResult.deployed.filter(r => r.status === "deployed" || r.status === "updated").length;
105
+ // サマリー表示
106
+ printSummary(installResults, deployResult.deployed, options.dryRun ?? false, logger, T);
107
+ // セッション再起動案内
108
+ if (!options.dryRun) {
109
+ logger.info("");
110
+ logger.warn(T("restartSessionNotice"));
111
+ logger.info(T("restartSessionHint"));
112
+ }
113
+ if (verbose) {
114
+ const result = {
115
+ pluginName: pluginsToInstall.join(", "),
116
+ cachePath: installResults[0]?.cachePath ?? "",
117
+ rulesDeployed,
118
+ dryRun: options.dryRun ?? false,
119
+ };
120
+ console.log(JSON.stringify(result, null, 2));
121
+ }
122
+ return 0;
123
+ }
124
+ /**
125
+ * インストール対象プラグインの一覧を決定する
126
+ *
127
+ * @param options - コマンドオプション
128
+ * @param languageSetting - 言語設定("japanese" | "english" | null)
129
+ * @returns インストール対象プラグイン名の配列
130
+ */
131
+ function resolveTargetPlugins(options, languageSetting) {
132
+ // --all: 全プラグイン
133
+ if (options.all) {
134
+ return [PLUGIN_NAME, PLUGIN_NAME_JA, PLUGIN_NAME_HOOKS];
135
+ }
136
+ // --plugin: 明示指定
137
+ if (options.plugin) {
138
+ return [options.plugin];
139
+ }
140
+ // 自動選択: 言語設定に基づく
141
+ if (languageSetting === "japanese") {
142
+ return [PLUGIN_NAME_JA, PLUGIN_NAME_HOOKS];
143
+ }
144
+ // デフォルト(英語または未設定)
145
+ return [PLUGIN_NAME, PLUGIN_NAME_HOOKS];
146
+ }
147
+ /**
148
+ * インストール結果サマリーを表示する
149
+ */
150
+ function printSummary(installResults, deployedRules, dryRun, logger, T) {
151
+ logger.info(`\n${T("summaryHeader")}`);
152
+ if (dryRun) {
153
+ logger.info(T("dryRunNote"));
154
+ }
155
+ for (const r of installResults) {
156
+ if (r.success) {
157
+ logger.success(`✓ ${T("summaryPluginInstalled", { pluginName: r.pluginName })}`);
158
+ }
159
+ else {
160
+ logger.error(`✗ ${T("summaryPluginFailed", { pluginName: r.pluginName })}`);
161
+ }
162
+ }
163
+ let deployed = 0, updated = 0, unchanged = 0, errors = 0;
164
+ for (const r of deployedRules) {
165
+ if (r.status === "deployed")
166
+ deployed++;
167
+ else if (r.status === "updated")
168
+ updated++;
169
+ else if (r.status === "unchanged")
170
+ unchanged++;
171
+ else if (r.status === "error")
172
+ errors++;
173
+ }
174
+ const parts = [];
175
+ if (deployed > 0)
176
+ parts.push(T("countDeployed", { count: deployed }));
177
+ if (updated > 0)
178
+ parts.push(T("countUpdated", { count: updated }));
179
+ if (unchanged > 0)
180
+ parts.push(T("countUnchanged", { count: unchanged }));
181
+ if (errors > 0)
182
+ parts.push(T("countError", { count: errors }));
183
+ if (parts.length > 0) {
184
+ const rulesMsg = T("deployedRulesSummary", { details: parts.join(", ") });
185
+ if (errors > 0) {
186
+ logger.error(`✗ ${rulesMsg}`);
187
+ }
188
+ else {
189
+ logger.success(`✓ ${rulesMsg}`);
190
+ }
191
+ }
192
+ if (errors > 0) {
193
+ logger.error(`✗ ${T("completedError", { count: errors })}`);
194
+ }
195
+ else {
196
+ logger.success(`✓ ${T("completedOk")}`);
197
+ }
198
+ }
199
+ //# sourceMappingURL=plugin-install-local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-install-local.js","sourceRoot":"","sources":["../../src/commands/plugin-install-local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * skill benchmark コマンド
3
+ *
4
+ * benchmark ディレクトリの grading.json ファイルを集計して
5
+ * benchmark.json と benchmark.md を生成する。
6
+ *
7
+ * Derived from: https://github.com/anthropics/skills/tree/main/skills/skill-creator
8
+ * Original: scripts/aggregate_benchmark.py, scripts/generate_report.py
9
+ * Original licensed under Apache License 2.0 (Copyright 2025 Anthropic, PBC).
10
+ * Modified for shirokuma-docs: ported to TypeScript with CLI integration.
11
+ * - Python → TypeScript
12
+ * - benchmark.md 形式のレポート生成(Handlebars は不使用、テンプレートリテラルで実装)
13
+ */
14
+ import type { BenchmarkData } from "./types.js";
15
+ export declare function generateBenchmark(benchmarkDir: string, skillName?: string, skillPath?: string): BenchmarkData;
16
+ /**
17
+ * benchmark データから人間が読みやすい Markdown を生成する。
18
+ */
19
+ export declare function generateMarkdown(benchmark: BenchmarkData): string;
20
+ interface BenchmarkOptions {
21
+ benchmarkDir: string;
22
+ skillName?: string;
23
+ skillPath?: string;
24
+ output?: string;
25
+ verbose?: boolean;
26
+ }
27
+ export declare function cmdSkillBenchmark(options: BenchmarkOptions): Promise<number>;
28
+ export {};
29
+ //# sourceMappingURL=benchmark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"benchmark.d.ts","sourceRoot":"","sources":["../../../src/commands/skill/benchmark.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,aAAa,EAA6B,MAAM,YAAY,CAAC;AAuP3E,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,SAAK,EACd,SAAS,SAAK,GACb,aAAa,CA6Cf;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CA6CjE;AAMD,UAAU,gBAAgB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsDlF"}
@@ -0,0 +1,344 @@
1
+ /**
2
+ * skill benchmark コマンド
3
+ *
4
+ * benchmark ディレクトリの grading.json ファイルを集計して
5
+ * benchmark.json と benchmark.md を生成する。
6
+ *
7
+ * Derived from: https://github.com/anthropics/skills/tree/main/skills/skill-creator
8
+ * Original: scripts/aggregate_benchmark.py, scripts/generate_report.py
9
+ * Original licensed under Apache License 2.0 (Copyright 2025 Anthropic, PBC).
10
+ * Modified for shirokuma-docs: ported to TypeScript with CLI integration.
11
+ * - Python → TypeScript
12
+ * - benchmark.md 形式のレポート生成(Handlebars は不使用、テンプレートリテラルで実装)
13
+ */
14
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { join, resolve } from "node:path";
16
+ // =============================================================================
17
+ // 統計計算
18
+ // =============================================================================
19
+ /**
20
+ * 数値配列の mean, stddev, min, max を計算する。
21
+ */
22
+ function calculateStats(values) {
23
+ if (values.length === 0) {
24
+ return { mean: 0, stddev: 0, min: 0, max: 0 };
25
+ }
26
+ const n = values.length;
27
+ const mean = values.reduce((a, b) => a + b, 0) / n;
28
+ const variance = n > 1
29
+ ? values.reduce((sum, x) => sum + (x - mean) ** 2, 0) / (n - 1)
30
+ : 0;
31
+ const stddev = Math.sqrt(variance);
32
+ return {
33
+ mean: Math.round(mean * 10000) / 10000,
34
+ stddev: Math.round(stddev * 10000) / 10000,
35
+ min: Math.round(Math.min(...values) * 10000) / 10000,
36
+ max: Math.round(Math.max(...values) * 10000) / 10000,
37
+ };
38
+ }
39
+ /**
40
+ * benchmark ディレクトリからすべての run 結果を読み込む。
41
+ * 2 種類のレイアウト(workspace / legacy)をサポート。
42
+ */
43
+ function loadRunResults(benchmarkDir) {
44
+ const results = new Map();
45
+ // レイアウト判定
46
+ const runsDir = join(benchmarkDir, "runs");
47
+ const searchDir = existsSync(runsDir) ? runsDir : benchmarkDir;
48
+ // eval-N ディレクトリを昇順でソート
49
+ let evalDirs = [];
50
+ try {
51
+ evalDirs = readdirSync(searchDir, { withFileTypes: true })
52
+ .filter(d => d.isDirectory() && d.name.startsWith("eval-"))
53
+ .map(d => d.name)
54
+ .sort();
55
+ }
56
+ catch {
57
+ return results;
58
+ }
59
+ for (let evalIdx = 0; evalIdx < evalDirs.length; evalIdx++) {
60
+ const evalDirName = evalDirs[evalIdx];
61
+ const evalDirPath = join(searchDir, evalDirName);
62
+ // eval_metadata.json から eval_id を取得
63
+ let evalId = evalIdx;
64
+ const metadataPath = join(evalDirPath, "eval_metadata.json");
65
+ if (existsSync(metadataPath)) {
66
+ try {
67
+ const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
68
+ if (metadata["eval_id"] !== undefined) {
69
+ evalId = metadata["eval_id"];
70
+ }
71
+ }
72
+ catch {
73
+ // フォールバック
74
+ }
75
+ }
76
+ else {
77
+ const parts = evalDirName.split("-");
78
+ const parsed = parseInt(parts[1] ?? "", 10);
79
+ if (!isNaN(parsed))
80
+ evalId = parsed;
81
+ }
82
+ // config ディレクトリを走査
83
+ let configDirs = [];
84
+ try {
85
+ configDirs = readdirSync(evalDirPath, { withFileTypes: true })
86
+ .filter(d => d.isDirectory())
87
+ .map(d => d.name)
88
+ .sort();
89
+ }
90
+ catch {
91
+ continue;
92
+ }
93
+ for (const configName of configDirs) {
94
+ const configPath = join(evalDirPath, configName);
95
+ // run-* ディレクトリが存在しない設定ディレクトリはスキップ
96
+ let runDirs = [];
97
+ try {
98
+ runDirs = readdirSync(configPath, { withFileTypes: true })
99
+ .filter(d => d.isDirectory() && d.name.startsWith("run-"))
100
+ .map(d => d.name)
101
+ .sort();
102
+ }
103
+ catch {
104
+ continue;
105
+ }
106
+ if (runDirs.length === 0)
107
+ continue;
108
+ if (!results.has(configName)) {
109
+ results.set(configName, []);
110
+ }
111
+ for (const runDirName of runDirs) {
112
+ const runPath = join(configPath, runDirName);
113
+ const parts = runDirName.split("-");
114
+ const runNumber = parseInt(parts[1] ?? "1", 10);
115
+ const gradingFile = join(runPath, "grading.json");
116
+ if (!existsSync(gradingFile)) {
117
+ process.stderr.write(`Warning: grading.json not found in ${runPath}\n`);
118
+ continue;
119
+ }
120
+ let grading;
121
+ try {
122
+ grading = JSON.parse(readFileSync(gradingFile, "utf-8"));
123
+ }
124
+ catch (e) {
125
+ process.stderr.write(`Warning: Invalid JSON in ${gradingFile}: ${e instanceof Error ? e.message : String(e)}\n`);
126
+ continue;
127
+ }
128
+ const summary = grading["summary"] ?? {};
129
+ const timing = grading["timing"] ?? {};
130
+ const metrics = grading["execution_metrics"] ?? {};
131
+ let timeSeconds = timing["total_duration_seconds"] ?? 0;
132
+ // timing.json フォールバック
133
+ if (timeSeconds === 0) {
134
+ const timingFile = join(runPath, "timing.json");
135
+ if (existsSync(timingFile)) {
136
+ try {
137
+ const timingData = JSON.parse(readFileSync(timingFile, "utf-8"));
138
+ timeSeconds = timingData["total_duration_seconds"] ?? 0;
139
+ }
140
+ catch {
141
+ // 無視
142
+ }
143
+ }
144
+ }
145
+ const tokens = metrics["output_chars"] ?? 0;
146
+ const rawExpectations = grading["expectations"] ?? [];
147
+ const notesSummary = grading["user_notes_summary"] ?? {};
148
+ const notes = [
149
+ ...(notesSummary["uncertainties"] ?? []),
150
+ ...(notesSummary["needs_review"] ?? []),
151
+ ...(notesSummary["workarounds"] ?? []),
152
+ ];
153
+ results.get(configName).push({
154
+ eval_id: evalId,
155
+ run_number: runNumber,
156
+ configuration: configName,
157
+ pass_rate: summary["pass_rate"] ?? 0,
158
+ passed: summary["passed"] ?? 0,
159
+ failed: summary["failed"] ?? 0,
160
+ total: summary["total"] ?? 0,
161
+ time_seconds: timeSeconds,
162
+ tokens,
163
+ tool_calls: metrics["total_tool_calls"] ?? 0,
164
+ errors: metrics["errors_encountered"] ?? 0,
165
+ expectations: rawExpectations,
166
+ notes,
167
+ });
168
+ }
169
+ }
170
+ }
171
+ return results;
172
+ }
173
+ // =============================================================================
174
+ // 集計
175
+ // =============================================================================
176
+ /**
177
+ * run 結果を設定ごとに集計し、delta を計算する。
178
+ */
179
+ function aggregateResults(results) {
180
+ const runSummary = {};
181
+ const configs = [...results.keys()];
182
+ for (const config of configs) {
183
+ const runs = results.get(config) ?? [];
184
+ if (runs.length === 0) {
185
+ runSummary[config] = {
186
+ pass_rate: { mean: 0, stddev: 0, min: 0, max: 0 },
187
+ time_seconds: { mean: 0, stddev: 0, min: 0, max: 0 },
188
+ tokens: { mean: 0, stddev: 0, min: 0, max: 0 },
189
+ };
190
+ continue;
191
+ }
192
+ runSummary[config] = {
193
+ pass_rate: calculateStats(runs.map(r => r.pass_rate)),
194
+ time_seconds: calculateStats(runs.map(r => r.time_seconds)),
195
+ tokens: calculateStats(runs.map(r => r.tokens)),
196
+ };
197
+ }
198
+ // delta 計算(最初の2つの設定間)
199
+ if (configs.length >= 2) {
200
+ const primary = runSummary[configs[0]];
201
+ const baseline = runSummary[configs[1]];
202
+ const deltaPr = (primary["pass_rate"]?.mean ?? 0) - (baseline["pass_rate"]?.mean ?? 0);
203
+ const deltaTime = (primary["time_seconds"]?.mean ?? 0) - (baseline["time_seconds"]?.mean ?? 0);
204
+ const deltaTokens = (primary["tokens"]?.mean ?? 0) - (baseline["tokens"]?.mean ?? 0);
205
+ runSummary["delta"] = {
206
+ pass_rate: `${deltaPr >= 0 ? "+" : ""}${deltaPr.toFixed(2)}`,
207
+ time_seconds: `${deltaTime >= 0 ? "+" : ""}${deltaTime.toFixed(1)}`,
208
+ tokens: `${deltaTokens >= 0 ? "+" : ""}${deltaTokens.toFixed(0)}`,
209
+ };
210
+ }
211
+ return runSummary;
212
+ }
213
+ // =============================================================================
214
+ // benchmark.json 生成
215
+ // =============================================================================
216
+ export function generateBenchmark(benchmarkDir, skillName = "", skillPath = "") {
217
+ const results = loadRunResults(benchmarkDir);
218
+ const runSummary = aggregateResults(results);
219
+ const runs = [];
220
+ for (const [, entries] of results) {
221
+ for (const entry of entries) {
222
+ runs.push({
223
+ eval_id: entry.eval_id,
224
+ configuration: entry.configuration,
225
+ run_number: entry.run_number,
226
+ result: {
227
+ eval_id: entry.eval_id,
228
+ run_number: entry.run_number,
229
+ pass_rate: entry.pass_rate,
230
+ passed: entry.passed,
231
+ failed: entry.failed,
232
+ total: entry.total,
233
+ time_seconds: entry.time_seconds,
234
+ tokens: entry.tokens,
235
+ tool_calls: entry.tool_calls,
236
+ errors: entry.errors,
237
+ },
238
+ expectations: entry.expectations,
239
+ notes: entry.notes,
240
+ });
241
+ }
242
+ }
243
+ const evalIds = [...new Set(runs.map(r => r.eval_id))].sort();
244
+ return {
245
+ metadata: {
246
+ skill_name: skillName || "<skill-name>",
247
+ skill_path: skillPath || "<path/to/skill>",
248
+ executor_model: "<model-name>",
249
+ analyzer_model: "<model-name>",
250
+ timestamp: new Date().toISOString().replace(/\.\d+Z$/, "Z"),
251
+ evals_run: evalIds,
252
+ runs_per_configuration: 3,
253
+ },
254
+ runs,
255
+ run_summary: runSummary,
256
+ notes: [],
257
+ };
258
+ }
259
+ // =============================================================================
260
+ // benchmark.md 生成
261
+ // =============================================================================
262
+ /**
263
+ * benchmark データから人間が読みやすい Markdown を生成する。
264
+ */
265
+ export function generateMarkdown(benchmark) {
266
+ const { metadata, run_summary } = benchmark;
267
+ // delta を除いた設定名
268
+ const configs = Object.keys(run_summary).filter(k => k !== "delta");
269
+ const configA = configs[0] ?? "config_a";
270
+ const configB = configs[1] ?? "config_b";
271
+ const labelA = configA.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
272
+ const labelB = configB.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
273
+ const summaryA = run_summary[configA];
274
+ const summaryB = run_summary[configB];
275
+ const delta = run_summary["delta"];
276
+ const aPr = summaryA?.["pass_rate"] ?? { mean: 0, stddev: 0 };
277
+ const bPr = summaryB?.["pass_rate"] ?? { mean: 0, stddev: 0 };
278
+ const aTime = summaryA?.["time_seconds"] ?? { mean: 0, stddev: 0 };
279
+ const bTime = summaryB?.["time_seconds"] ?? { mean: 0, stddev: 0 };
280
+ const aTokens = summaryA?.["tokens"] ?? { mean: 0, stddev: 0 };
281
+ const bTokens = summaryB?.["tokens"] ?? { mean: 0, stddev: 0 };
282
+ const lines = [
283
+ `# Skill Benchmark: ${metadata.skill_name}`,
284
+ "",
285
+ `**Model**: ${metadata.executor_model}`,
286
+ `**Date**: ${metadata.timestamp}`,
287
+ `**Evals**: ${metadata.evals_run.join(", ")} (${metadata.runs_per_configuration} runs each per configuration)`,
288
+ "",
289
+ "## Summary",
290
+ "",
291
+ `| Metric | ${labelA} | ${labelB} | Delta |`,
292
+ "|--------|------------|---------------|-------|",
293
+ `| Pass Rate | ${(aPr.mean * 100).toFixed(0)}% ± ${(aPr.stddev * 100).toFixed(0)}% | ${(bPr.mean * 100).toFixed(0)}% ± ${(bPr.stddev * 100).toFixed(0)}% | ${delta?.["pass_rate"] ?? "—"} |`,
294
+ `| Time | ${aTime.mean.toFixed(1)}s ± ${aTime.stddev.toFixed(1)}s | ${bTime.mean.toFixed(1)}s ± ${bTime.stddev.toFixed(1)}s | ${delta?.["time_seconds"] ?? "—"}s |`,
295
+ `| Tokens | ${aTokens.mean.toFixed(0)} ± ${aTokens.stddev.toFixed(0)} | ${bTokens.mean.toFixed(0)} ± ${bTokens.stddev.toFixed(0)} | ${delta?.["tokens"] ?? "—"} |`,
296
+ ];
297
+ if (benchmark.notes.length > 0) {
298
+ lines.push("", "## Notes", "");
299
+ for (const note of benchmark.notes) {
300
+ lines.push(`- ${note}`);
301
+ }
302
+ }
303
+ return lines.join("\n");
304
+ }
305
+ export async function cmdSkillBenchmark(options) {
306
+ const { benchmarkDir: rawBenchmarkDir, skillName = "", skillPath = "", output, verbose, } = options;
307
+ const benchmarkDir = resolve(rawBenchmarkDir);
308
+ if (!existsSync(benchmarkDir)) {
309
+ process.stderr.write(`Directory not found: ${benchmarkDir}\n`);
310
+ return 1;
311
+ }
312
+ if (verbose) {
313
+ process.stderr.write(`Aggregating benchmark: ${benchmarkDir}\n`);
314
+ }
315
+ // benchmark 生成
316
+ const benchmark = generateBenchmark(benchmarkDir, skillName, skillPath);
317
+ // 出力先の決定
318
+ const outputJson = output ? resolve(output) : join(benchmarkDir, "benchmark.json");
319
+ const outputMd = outputJson.replace(/\.json$/, ".md");
320
+ // ディレクトリ作成
321
+ mkdirSync(join(outputJson, ".."), { recursive: true });
322
+ // benchmark.json 書き込み
323
+ writeFileSync(outputJson, JSON.stringify(benchmark, null, 2), "utf-8");
324
+ process.stdout.write(`Generated: ${outputJson}\n`);
325
+ // benchmark.md 書き込み
326
+ const markdown = generateMarkdown(benchmark);
327
+ writeFileSync(outputMd, markdown, "utf-8");
328
+ process.stdout.write(`Generated: ${outputMd}\n`);
329
+ // サマリー表示
330
+ const configs = Object.keys(benchmark.run_summary).filter(k => k !== "delta");
331
+ const delta = benchmark.run_summary["delta"];
332
+ process.stdout.write("\nSummary:\n");
333
+ for (const config of configs) {
334
+ const summary = benchmark.run_summary[config];
335
+ const pr = summary?.["pass_rate"]?.mean ?? 0;
336
+ const label = config.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
337
+ process.stdout.write(` ${label}: ${(pr * 100).toFixed(1)}% pass rate\n`);
338
+ }
339
+ if (delta) {
340
+ process.stdout.write(` Delta: ${delta["pass_rate"] ?? "—"}\n`);
341
+ }
342
+ return 0;
343
+ }
344
+ //# sourceMappingURL=benchmark.js.map