🧩

そのSKILL.md、別のスキルと発火が被ってない? 衝突と壊れた参照をCIで落とすリンタを作った

に公開

スキルが増えると、静かに事故る

Claude の Agent Skills(SKILL.md)を書き始めたころ、私は快適だった。困ったのはスキルが 5 個、10 個と増えてからだ。二つの不安が出てきた。

一つは参照の腐り。SKILL.md に「前処理は scripts/extract.py を使う」と書いたのに、あとでそのスクリプトをリネームした。SKILL.md は古いパスを指したまま。誰も気づかない。エージェントはそれを信じて動く。

もう一つがもっと厄介で、スキル同士の「発火の被り」だ。スキルは description のトリガ文で呼ばれる。似た説明のスキルを二つ置くと、同じ入力でどちらが起動するか分からなくなる。しかも静かに、間違ったほうが動く。

この二つを PR の時点で機械的に落とすツール、skills-lint を作った。

まず既存ツールを探した。そして、被っていた

正直に書く。作る前に「どうせ誰か作ってるだろう」と思って探した。案の定、トリガの衝突検出は pulser のような先行ツールが既にやっていた。有料の SkillCheck にも同種の機能がある。ここで私は一度がっかりした。

ただ、よく見ると隙間があった。衝突を見るツールは参照整合を見ない。参照整合を見るツールは npm 依存だったり有料だったりする。「参照整合 + 衝突検出 + 依存ゼロ + 無料で毎 PR 走る」を一本に束ねたものは、無かった。

だから束ねた。それが skills-lint だ。

何を落とすか

検出するのは三つ。

  • 参照整合: SKILL.md 本文の `scripts/run.py`[doc](references/guide.md) が実在するか。言語は問わない。
  • frontmatter: name が小文字ハイフン形式か、description(発火トリガ)があるか。
  • 衝突: 二つのスキルで name が重複していないか(インストール衝突)、description が近すぎないか。

三つ目の「近すぎ」の判定には、文字バイグラムの類似度(Jaccard)を使った。形態素解析のような重い依存を持ち込まずに済むし、日本語のトリガ文でも効く。しきい値は 0.7 と高めにして、誤検出を潰した。

わざと似せた二つのスキルを食わせると、こうなる。

✗ examples/bad/summarizer-a/SKILL.md — 1 件
  :4  参照 `scripts/missing.py` が存在しません
  examples/bad/summarizer-b/SKILL.md:1  description が summarizer-a と高類似 (0.94) — 同じ入力で取り違える恐れ
exit code: 1

腐った参照と、類似 0.94 の衝突を同時に捕まえて、CI を落とす。これだけだ。

使い方

ローカルなら 1 行。

npx @hyuga/skills-lint                 # .claude/skills / skills を自動探索

CI に置くなら GitHub Action で。

- uses: hyuga611/skills-lint@v1
  with:
    paths: .claude/skills

指摘は PR にインライン注釈で出て、ジョブが落ちる。人が意識しなくても毎 PR 走る。これが定着の本体だと私は思っている。

姉妹ツール reflint も同じ日に更新した

skills-lint は、以前作った reflintAGENTS.md / llms.txt の参照整合リンタ)と同じ型で作っている。依存ゼロ、純粋関数の scan と CLI を分離、GitHub Action、PR 注釈。

その reflint も同じ日に v0.2.0 を出した。llms.txt の中の markdown リンク先が、リポジトリ内に実在するかを検証する機能だ。既存の llms.txt ツールはフォーマット検査か外部リンクの死活まで。リポ内の参照整合を CI で見るものは、見当たらなかった。ここは競合がいない。

持ち帰り

スキルが増える時代、一番静かな事故は「発火の被り」と「腐った参照」だ。目視では気づけない。だから CI で落とす。

同じ痛みを持っている人がいたら、ぜひ。

GitHubで編集を提案

Discussion