Skill Creatorの正体はnpxではなく/plugin installだった話
技術ブログプラットフォーム「CodeNote」の開発日記。今日はコードを一行も書いていない。代わりに、Claude Codeの「スキル」「サブエージェント」まわりの開発基盤を整える一日だった。
スキルとは、決まった作業を自動でこなしてくれる手順書のようなものだ。サブエージェントとは、特定の役割に特化した、Claude Codeの中のもう一人の担当者だと思えばいい。
今日やったことは3つ。
- 公式の Skill Creator(スキルを作るためのスキル)を導入した
- それを使って「今日の作業をブログにまとめるスキル」(
blog-diary)を作った - 記事の仕上げを担当する「ブロガー」サブエージェント(
blogger)を作った
そして、一番の発見は「Skill Creatorの入れ方が、事前に想定していた方法とまったく違った」ことだった。順番に振り返る。
そもそもの発端
きっかけは単純だった。次の3点セットを作りたい、というアイデアからのスタートだ。
- Skill Creator(Anthropic公式のスキル作成支援ツール)の導入
- それを使ったブログ記事作成スキル
- 記事の仕上げを担当する「ブロガー」サブエージェント
3つは1→2→3の順で依存している。2は1のツールを使って作り、3は2から呼び出される。
まずセキュリティ観点を固めた
アイデアをドキュメント化した直後、「個人情報などセキュリティ上の問題行動を起こさないようにしてください」という指示が入った。
考えてみれば当然だ。このスキルはgitの履歴やソースコードを読んで記事を書く。放っておくと .env の値やコミット作者のメールアドレスが、そのまま記事に混ざりかねない。
そこで、ドキュメントに「共通のセキュリティ・個人情報保護上の留意事項」という章を追加した。
.env*や service_role キーなどの秘密情報は本文に一切転記しない- コミット作者名・メールアドレス等の個人情報も転記しない
- 自動投稿はせず、著者本人が公開前にレビューする
- スキル・サブエージェントに渡すツールは必要最小限に絞る
この4つを、あとで作る実物の SKILL.md やサブエージェントの指示に、そのまま制約として落とし込むことになる。
導入方針が「自動化」から「手動」に変わった
最初、Skill Creatorの導入は自動インストールの方向で検討していた。「Dev Containerを作り直すたびに手動インストールしたくない」という要望からだ。Dockerfileかpackage.jsonに自動インストールを仕込む案を考えていた。
ところが後日、「手動インストールの方針に変更します」という指示があった。Dockerfile案・postinstall案は取り下げ、ドキュメントから削除した。
理由はサプライチェーンリスクだ。仕入れ先の部品に問題があると、その部品を使った製品すべてに影響が出る。それと同じで、外部のスキルを無条件に取り込む仕組みにしてしまうと、SKILL.md の中身を確認しないまま導入することになりかねない。手動にしておけば、導入者が内容を確認してから意図的にインストールできる。
いざ導入したら、想定と違う方法だった
ドキュメント上は npx skills add anthropics/skills --skill skill-creator --agent claude-code というコマンドを「要検証」として書いていた。Web検索で見つけた情報をもとにした仮説だったからだ。
ところが実際にインストールされたのは、まったく別の経路だった。Claude Codeのプラグイン機能(後から追加できる拡張機能の仕組み)を使い、
/plugin install skill-creator@claude-plugins-official
を実行し、/reload-plugins で反映する、という方法である。ドキュメントに書いていた仮説は外れていた。
bind mountの外にキャッシュされる、という落とし穴
この違いは地味だが重要だった。
プラグインは ~/.claude/plugins/cache/ というユーザーのホームディレクトリ配下にキャッシュされる。一方、このプロジェクトの .devcontainer/docker-compose.yml は、リポジトリ(/workspaces/dev)だけをホストとバインドマウント(ホストPC上のフォルダを、コンテナの中にそのまま重ねて見せる仕組み)していて、ホームディレクトリはマウント対象外だ。
つまり、Dev Containerを作り直す(Rebuild)と、せっかく入れたSkill Creatorはコンテナと一緒に消える。
「.claude/skills/ に生成物をコミットすれば、1回入れれば以降は git pull だけで済む」と考えていた当初の想定は、この時点で崩れた。結局、「開発者が必要になったタイミングで手動で入れ直す」という、最初に立てた方針そのままの運用にせざるを得なかった。
権限まわりでも既存の型に合わせた
Zenn記事のヒントには、次のような工夫があった。
disable-model-invocation: true(Claudeが自動判断で勝手にスキルを起動しないようにする設定)を設定する- スキルに渡すツールを最小限にする
ただし、このプロジェクトの既存スキル(basic-design や tdd-cycle など)を見る限り、SKILL.md のfrontmatter(ファイル先頭に書くYAML形式の設定部分)は name と description の2つしか使っていない。ツール制限のfrontmatterも前例がなかった。
前例のない設定を無理に持ち込むより、実際にこのプロジェクトで機能することが確認できている型に合わせるほうが安全だと判断した。代わりに、以下の形で同じ狙いを別の手段で実現した。
- スキルの
descriptionに「ユーザーが明示的に依頼したときのみ実行する。指示なく自発的に実行しない」と明記する - 記事の仕上げを担当する
bloggerサブエージェントには、code-reviewerサブエージェントと同じ書式でtools: Read, Grep, Globと明記し、Write・Bashを持たせない
ファイルへの書き込み(Write)はスキル本体だけが持ち、記事の言い回しを整えるサブエージェント側には持たせない。そんな役割分担にしている。
今日の学び
- ドキュメント上の「要検証」は、実際に検証しないと分からない。
npx skills addという仮説は、手を動かしてみるまで正しいかどうか分からなかった - 自動化の前提(bind mountの範囲)が変わると、リスク評価も変わる。ホームディレクトリがリポジトリの外だと分かった時点で、「1回入れればコミットで済む」という当初の楽観は崩れた
- プロジェクトに前例のない設定は、無理に真似ない。目新しいfrontmatterのオプションより、動作確認済みの型(
name/descriptionのみ、サブエージェントのtools制限)に寄せるほうが手堅い
明日は、今日作った blog-diary スキルとブロガーサブエージェントを実際に動かして、この記事自体を仕上げてみるところから始まる。……というのは実はもう今この瞬間の話で、この記事そのものがその1本目の出力だったりする。
本記事は Sonnet 5(claude-sonnet-5)が生成しました。