はじめに
Claude Code のスキルコレクションを定期的に棚卸しする /skill-stocktake コマンドを作った。「スキルの品質を AI に判断させたい」という動機は単純だったが、その過程で 4 回の設計やり直しと、予想していなかった発見があった。
AI にファイル一覧を取らせたら、毎回結果が違う。
引数を渡し忘れる。タイムスタンプのフォーマットが揺れる。同じプロンプトで同じファイル群を処理させても、出力が再現しない。AI の判断力と機械的な正確さは、まったく別の能力だった。
この記事はその全過程の記録だ。設計哲学の試行錯誤、「AI と決定的コードの境界」を見つけた実装、スクリプトが炙り出したバグとセキュリティホール、そしてエコシステムへの公開で見えた現実。
解きたかった問題
Claude Code のスキルには多様な出自がある。自分で書いたもの、Everything Claude Code コミュニティから導入したもの、外部リポジトリからコピーしたもの、AI が自動抽出したもの。
これらが 20〜50 ファイル溜まると、定期的な棚卸しが必要になる。
- 使えるか? — 参照している API やツールが古くなっていないか
- 重複していないか? — 似た内容のスキルが複数ないか
- コストに見合うか? — コンテキストウィンドウを消費する価値があるか
手動でやると 1 時間以上かかる。AI に任せたい。では、どう設計するか。
設計 — 4 つのバージョンを 2 週間で
V1-V2: 出自で判断を分岐する(失敗)
最初の設計は、スキルの出自(origin フィールド)をルーティングキーにした。「ECC から導入したスキルは Freshness のみ確認」「auto-extracted で MEMORY.md と重複なら削除」というルールテーブルだ。
直感的に正しそうだったが、2 つの問題があった。
-
出自カテゴリは増え続ける。
original、ECC、auto-extracted、{org/repo}、skill-create——新しい導入経路が増えるたびにルールを追加する必要がある。品質ベースのルールなら出自の数に関係なく一定だ。 - 出自は品質を予測しない。 自作スキルが低品質なこともあれば、外部から導入したスキルが最も有用なこともある。
V2 で origin フィールドをディレクトリ検出に置き換えても、「出自で分岐する」という構造問題は同じだった。
出自を疑う: 異分野からの視点
V2 の失敗後、根本的な問いが浮かんだ。「出自を無視して品質だけで評価するシステム」は他の領域にあるか? 8 分野を調べた。そのうち 2 つが設計の方向を決定的に変えた。
パリの審判(1976年)。 フランスのワイン専門家 9 名がブラインドテイスティングを行い、カリフォルニアワインがフランスの最高級シャトーを上回った。原産地(出自)を知ることが評価を歪めることの実証だ。これは V3 の設計原則「品質評価はブラインドで行う」の直接的な根拠になった。
Danger Model(2002年)。 古典的な免疫理論は「自己 vs 非自己」——出自ベースの識別だった。しかし Polly Matzinger の Danger Model はこれを否定する。免疫系は「非自己」に反応するのではなく「危険シグナル」に反応する。腸内細菌(非自己)を許容し、自己組織を攻撃することもある(自己免疫疾患)。出自ではなく「実際の危険性」で判断するシステムへの進化だ。
この 2 つのアナロジーからは、同じパターンが浮かび上がる。評価はブラインドで行い、行動判断は文脈を持つ人間が行う。 そして「何が危険か」は出自からは予測できない。この洞察は設計だけでなく、後にスクリプトのセキュリティ設計にも再登場する。
V3: 4 次元ルーブリック + 2 階建てアーキテクチャ
アナロジーから「評価はブラインドで、出自を見ない」という原則を得た。これを設計に反映した V3 は 2 つの層を持つ。
1 階(品質評価)。 各スキルを単体でブラインド評価する。他のスキルは参照しない。4 つの次元でスコアリングする。
| 次元 | 問い |
|---|---|
| Specificity | 具体的なコード例や手順があるか? |
| Actionability | 読んですぐ行動に移せるか? |
| Scope | 対象領域を適切にカバーしているか? |
| Coverage | 主要なケースを網羅しているか? |
2 階(コレクション判断)。 1 階のスコアに加えて、重複チェックと使用実績を考慮し、Keep / Improve / Retire を判定する。
元の 6 次元から 2 つを削除した根拠も重要だ。Freshness(技術参照の鮮度)は、「現在の環境で」という前提でスコアリングすれば他の 4 次元に吸収される。古い API の具体例は Specificity が下がり、古いツールの手順は Actionability が下がる。Freshness は「書かれた当時の環境」を前提にした場合の補正パッチだった。
Non-redundancy(非重複性)は性質が異なる。「この絵は良いか?」と「このコレクションにこの絵が必要か?」は別の問いだ。前者は作品単体で判断でき、後者はコレクション全体を見て判断する。Non-redundancy を 1 階に置くのは、この 2 つを混同していた。だから 2 階に移した。
V3 は V1 の 130 行から 80 行に減った。しかし、構造的な問題がまだ残っていた。
4 者ペルソナレビューが V3 を壊した
V3 を 4 つの AI ペルソナに同時レビューさせた。4 つの視点から同時に批判を受けることで、単一レビューでは見えない構造的な欠陥が炙り出される。
心理測定学者の指摘。 「Specificity と Actionability の相関が高い(推定 r=0.70-0.85)。豊富なコード例があるスキルは定義上すぐ行動できる。合計スコアは『具体性・深さ』を 3 重カウントしている。」
つまり、4 次元に減らしてもまだ冗長だった。独立に見える次元が同じ潜在因子を測定していた。さらに AI の中心傾向バイアスにより、スコアは 2.5-4.5 の狭い範囲に圧縮される。判定閾値がこの集中帯に重なり、同じスキルが実行ごとに Keep と Improve を行き来する。再現性がない。
ミニマリストの指摘(最も破壊的)。 「ルーブリックはピタゴラスイッチ(Rube Goldberg machine)だ。AI はスキルを読んで既に品質を把握している。それを数値に分解して合計して閾値と比較して判定に変換する——AI は最初から判定を知っている。」
この指摘は V3 だけでなく、ルーブリックという方法論そのものを否定した。AI に対してルーブリックを使うことの本質的な問題は、AI は人間と違い、次元ごとの独立したスコアリングが苦手だということだ。人間の審査員なら「Specificity は 4、Actionability は 2」と独立に採点できる。AI は全体の印象に引きずられる。ルーブリックは人間の評価を構造化するためのツールであり、AI の評価を構造化するには別のアプローチが必要だった。
V4: チェックリスト + AI 判断
全ての議論を経て、設計の核心は一文に収まった。
AI がスキップしがちなチェックをリストで強制し、判断自体は AI に委ねる。
| Version | 次元数 | 判定行 | 指示行数 |
|---|---|---|---|
| V1 | 6 + origin 分岐 | 12 | 約 130 |
| V2 | 6 | 10 | 約 130 |
| V3 | 4 + 2 階建て | 5 | 約 80 |
| V4 | 0(チェックリストのみ) | 0 | 約 40 |
130 行が 40 行になった。しかしこの圧縮は「情報を削った」のではなく「本質を見つけた」結果だ。ルーブリックの代わりに残したのは 4 つの確認事項だけだ。
- 他スキルとの内容重複を確認したか?
- MEMORY.md / CLAUDE.md との重複を確認したか?
- 技術要素の鮮度を確認したか?
- 使用実績を考慮したか?
判定(Keep / Improve / Update / Retire / Merge)は AI に一任する。AI は「このスキルは有用か?」というホリスティックな判断は得意だ。苦手なのは「特定のチェックをスキップしないこと」だ。チェックリストは AI の得意・不得意に沿った設計になっている。
転換点 — AI に機械的処理を任せたら壊れた
V4 の設計は確定した。次は実装だ。
V4 の初版では Phase 1(スキルのインベントリ取得)を「haiku subagent でファイル列挙・mtime 取得」と記述していた。AI にファイルシステムを歩かせ、スキル一覧を JSON にまとめさせる設計だ。
3 つの問題
非決定的。 AI がスクリプトの引数を渡し忘れる。scan.sh はプロジェクトレベルのスキルディレクトリをオプション引数で受け取る設計だった。AI はこの引数を非決定的に省略し、プロジェクトスキルがサイレントにスキップされた。修正後は 3 段階のフォールバックを導入した。
CWD_SKILLS_DIR="${SKILL_STOCKTAKE_PROJECT_DIR:-${1:-$PWD/.claude/skills}}"
環境変数 > 位置引数 > $PWD デフォルトの順で解決する。AI のプレースホルダ解決に依存しない設計だ。
mtime のフォーマットも揺れる。2026-02-15T08:30:00Z のときもあれば、日付部分だけで時刻を T00:00:00Z と近似するときもある。
テスト不能。 subagent の出力は毎回異なる。自動テストを書こうにも期待値を定義できない。
コスト無駄。 ファイル一覧の取得は find と date -u -r でできる処理だ。AI トークンを消費する意味がない。
具体例: T00:00:00Z 近似バグ
AI が evaluated_at を生成する際、日付だけを使い時刻を T00:00:00Z と近似することがある。Quick Scan は「mtime > evaluated_at のファイルだけ再評価」するので、午前 0 時以降の変更が全て「変更なし」と判定される。テストスイートではこのパターンを明示的に検出する。
// evaluated_at が "2026-02-15T00:00:00Z" の場合
// 同日の "2026-02-15T08:30:00Z" に変更されたファイルは「変更あり」だが
// AI が生成した midnight placeholder は別の意味を持つ
決断: 「機械的処理はスクリプトに、品質判断は AI に」
Phase 1 を 3 本のシェルスクリプトに置き換えた。
| スクリプト | 役割 | 行数 |
|---|---|---|
scan.sh |
スキルファイル列挙 + frontmatter 抽出 + UTC mtime + 使用頻度集計 | 約 170 |
quick-diff.sh |
前回結果と mtime を比較し、変更・新規ファイルを検出 | 約 90 |
save-results.sh |
評価結果を results.json にマージ(既存結果を保持) |
約 60 |
Phase 2(品質判断)だけが AI の仕事として残った。チェックリストを適用し、各スキルに Keep / Improve / Update / Retire / Merge の判定を出す。この分業により、Phase 1 の出力が完全に再現可能になった。
AI の出力は外部入力 — スクリプトが炙り出したもの
スクリプトを書き、テストを書き、code-reviewer と security-reviewer にレビューさせた。その過程で見えてきた原則がある。
AI の出力は、外部 API のレスポンスと同じレベルの不信で扱わなければならない。
設計セクションで紹介した Danger Model のアナロジーがここで再登場する。古典的な免疫理論が「自己 vs 非自己」(出自ベース)だったように、私たちは直感的に「AI が生成したデータだから信頼できる」と考えがちだ。しかし免疫系が「危険シグナル」で判断するように、スクリプトも「この入力は危険か?」で判断すべきだ。出自に関係なく。
以下の事例は全てこの原則の証拠だ。
証拠 1: is_new バグ — 新規ファイルの検出漏れ
quick-diff.sh の初版では、新規ファイル(results.json に未登録)も mtime フィルタを通過しなければ出力されなかった。
# Before (buggy)
[[ "$mtime" > "$evaluated_at" ]] || continue
# is_new チェックはこの行の後 — 古い mtime の新規ファイルに到達しない
# After (fixed)
if echo "$known_paths" | grep -qxF "$dp"; then
is_new="false"
[[ "$mtime" > "$evaluated_at" ]] || continue # 既知: mtime ゲート
else
is_new="true"
# 新規: mtime に関係なく常に出力
fi
新規ファイルは mtime によらず常に検出対象とする。既知ファイルだけ mtime でフィルタする。「データの種類で処理を分ける」という判断だ。
証拠 2: セキュリティレビューが見つけた 3 つの危険シグナル
security-reviewer agent にレビューさせたところ、3 件の脆弱性が見つかった。
TMPDIR injection。 trap "rm -rf $tmpdir" EXIT でダブルクォート内の変数展開を使うと、TMPDIR 環境変数にシェルメタ文字を注入できる。クリーンアップ関数方式に修正した。
_scan_cleanup() { rm -rf "$_scan_tmpdir"; }
trap _scan_cleanup RETURN
grep substring false-positive。 grep -qF "$path" は部分文字列マッチだ。python-patterns が python-patterns-v2 にもマッチし、新規スキルが既知と誤判定される。-x(完全行一致)で修正した。
evaluated_at validation の欠如。 evaluated_at が null や不正文字列の場合、ISO 8601 文字列比較の結果が予測不能になる。正規表現で事前バリデーションを追加した。
if [[ ! "$evaluated_at" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]]; then
echo "Error: invalid evaluated_at: $evaluated_at" >&2
exit 1
fi
証拠 3: タイムスタンプの強制上書き
save-results.sh は AI が生成した評価結果 JSON を results.json にマージする。AI が evaluated_at に古いタイムスタンプを渡しても、スクリプトが常に現在の UTC 時刻で上書きする。AI の出力を「信頼する」のではなく「上書きする」設計だ。テストでは「1999 年のタイムスタンプが現在時刻に上書きされること」を検証している。
設計判断をテストとして残す
bats-core で 4 ファイル・39 テストを書いた。テストは仕様の検証だけでなく、なぜその設計にしたかの記録でもある。
特筆すべきは「stat と date -u -r の結果が異なることを検証する」テストだ。stat -f "%Sm" はローカルタイムゾーンの mtime を返す。Quick Scan の mtime 比較は UTC ベースのため、stat を使うと JST 環境で 9 時間のずれが生じる。このテストは意図的に両者の差異を検証し、設計判断(stat ではなく date -u -r を使う理由)をテストとして残している。
エコシステムに公開する
スキルが動くようになり、コミュニティへの公開を決めた。ここで Claude Code のスキル公開エコシステムの実態が見えてきた。
anthropics/skills — 事実上の内部リポジトリ
Anthropic 公式の anthropics/skills リポジトリに PR を出すことを最初に検討した。しかし PR 履歴を調べると、事実は明確だった。
- マージ済み PR の著者は全員 Anthropic 社員(klazuka, maheshmurag, mattpic-ant 等)
- 外部コミュニティからの PR は 100 件超が OPEN のまま放置(2026 年 2 月時点)
- スパム的な PR も混在(「Casino Bus Protocol skill」等)
外部からの貢献を受け入れる体制は現時点では整っていない。
公開チャネルの選択
調査の結果、以下のチャネルが実質的に機能していた。
| チャネル | 性質 | 登録方法 |
|---|---|---|
| SkillsMP | マーケットプレイス(96,000+ スキル) | 自動(GitHub クロール、要 2 stars) |
| VoltAgent/awesome-agent-skills | Awesome リスト(383+ スキル) | PR |
| travisvn/awesome-claude-skills | Awesome リスト(厳格) | PR(要 10 stars) |
| ECC | コミュニティ共有リポ | PR |
独立リポジトリを選んだ理由
最終的に、スキルごとに独立リポジトリを作成した。
claude-skill-stocktake/
├── .claude-plugin/
│ └── marketplace.json # SkillsMP 自動インデックス用
├── skills/
│ └── skill-stocktake/
│ ├── SKILL.md
│ └── scripts/
│ ├── scan.sh
│ ├── quick-diff.sh
│ └── save-results.sh
├── README.md
└── LICENSE (MIT)
この構造を選んだ理由は 3 つだ。
- 発見性。 Awesome リスト PR にはリポジトリ URL が必要だ。ECC のモノリポ内だと個別スキルの発見性が低い。
- 自動インデックス。 SkillsMP が GitHub を自動クロールしてインデックスする。独立リポなら申請不要で登録される。
-
自己完結性。 スクリプト群・テスト・ドキュメントを 1 リポで管理でき、
/plugin marketplace addで直接インストールできる。
まとめ: 学んだこと
この記事を通じて繰り返し登場したのは、1 つの問いだ。「AI の能力をどう分類し、分類ごとにどう扱うか?」
設計では「判断力はあるがスコアリングは苦手」と気づいてルーブリックを廃止した。実装では「判断は得意だが正確な繰り返しは苦手」と気づいてスクリプトに分離した。セキュリティでは「AI の出力は出自に関係なく検証すべき」と気づいてバリデーションを追加した。全て同じ構造だ。AI の能力の「種類」を識別し、種類ごとに異なる信頼レベルを設定する。
以下はその具体的な教訓だ。
1. AI の判断力と機械的精度は別物
What: ファイル列挙・mtime 比較・JSON マージは決定的なスクリプトに、品質判断は AI に委譲する。
Why: AI は「このスキルは有用か?」という総合判断は得意だが、「引数を毎回正確に渡す」は苦手だ。得意な仕事と苦手な仕事を正しく識別して分業することが、システム全体の信頼性を決める。
代替案: AI の出力をバリデーションするラッパーも考えたが、「正しい出力を検証する」より「最初から正しい出力を出すスクリプトを書く」方が単純だった。
2. ルーブリックは人間のためのツール
What: 6 次元ルーブリック(130 行)を廃止し、4 項目のチェックリスト(40 行)に置き換えた。
Why: ルーブリックは人間の評価を構造化するために発明された道具だ。人間は次元ごとに独立して採点できるが、AI は全体の印象に引きずられる。次元間の高い相関(r>0.7)と中心傾向バイアスにより、スコアは狭い範囲に圧縮され再現性がない。AI にはチェックリスト(確認済みか未確認の二値)の方が合う。
代替案: 次元を 2 つに減らすことも検討したが、「AI は最初から判定を知っている」という指摘が決定打になった。
3. AI の出力は出自ではなく危険性で判断する
What: TMPDIR injection、grep の substring false-positive、evaluated_at validation。いずれもセキュリティレビューで発見した。
Why: Danger Model の教訓がここで効く。「AI が作ったデータだから安全」(出自ベース)ではなく、「この入力は危険なパターンを含むか?」(危険シグナルベース)で判断する。AI の出力を処理するスクリプトは、ユーザー入力を処理するコードと同じバリデーション姿勢が必要だ。
4. スキル公開エコシステムは断片化している
What: Anthropic 公式リポは外部 PR を受け入れず、コミュニティ主導のチャネルが複数乱立している。
Why: Agent Skills 仕様は存在するが、エコシステムの成熟はこれからだ。現時点では独立リポ + 複数チャネルへの並列登録が最も確実な公開戦略になる。
おわりに
「AI にどこまで任せるか」——この問いはスキル棚卸しコマンドに限らない。AI を使ったツールを作るとき、私たちは常にこの境界を設計している。
この記事の経験から言えることは、その境界は「AI の能力の限界」ではなく「AI の能力の種類」によって決まるということだ。判断力と正確さは別の軸にある。AI が得意なことを AI に、機械が得意なことを機械に。この分業を意識するだけで、AI ツールの信頼性は大きく変わる。
skill-stocktake は claude-skill-stocktake リポジトリで公開しています。