1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code の skills を WordPress プラグイン開発で使う — 配布zipに開発ツールを混ぜかけた話と、リリース前チェックを任せた SKILL.md

1
Posted at

配布用の zip を作って、中身を unzip -l で何気なく眺めていたとき、見覚えのあるファイル名が目に入って指が止まった。Pro 版のライセンスキーを生成する、開発のときにしか使わないツールだった。配布物には、絶対に入ってはいけないものだ。もしこれがそのままユーザーの手に渡っていたら、ライセンス生成の仕組みや内部の秘密情報を解析され、有効な Pro ライセンスを作られるおそれがあった。

間に合った、と言いたいところだけれど、正しくは「たまたま目に入った」だ。除外し忘れていただけで、気づかなければそのまま出していた。その「たまたま」が後から効いてきて、リリースのたびに同じ作業を手でやって、疲れているときに一個飛ばす自分が怖くなった。

それで、リリース前の手順を Claude Code の skill にして、毎回機械的に同じチェックを通すことにした。skills が流行っているから乗った、というより、自分の地味なミスを二度とやらないための仕組みが欲しかった、という順番だ。この記事は、そのとき作った SKILL.md を実物で共有する。動作は Claude Code(2026年5月時点)、macOS、自作プラグインのリポジトリで確認している。

skill とは何か(最小限)

知っている人は読み飛ばしてほしい。Claude Code の skill は、SKILL.md を置いたフォルダだ。SKILL.md--- で囲った YAML フロントマターと、その下の Markdown 本文でできている。Claude Code ではフロントマターの項目はすべて任意で、推奨されるのは description だけ。name は省略するとディレクトリ名が使われる。Claude が skill を自動で使うかどうかは、主にこの description で判断される(省略すると本文の最初の段落が使われる)。トリガーになりそうな言い回しや利用例は、任意の when_to_use に足せる。だから description(と when_to_use)には、自分が実際に打ちそうな言葉を入れておくと、必要なときに自動で読み込まれる。

呼び出しは、/skill名 と明示する方法と、頼んだ内容が description に合致して自動で読み込まれる方法の二通り。個人用に ~/.claude/skills/ に置けば全プロジェクトで使えるし、リポジトリの .claude/skills/ に置けばそのプロジェクト限定になる。仕様の詳しいところは公式(Claude Code Docs の skills ページ)に譲る。

毎回プロンプトに書くのでは足りなかった

最初は、リリースのたびにチェック項目をプロンプトに書いていた。でもプロンプトは終われば消える。次のリリースでまた書くし、書くのが面倒だと省略する。省略したときに限って、今回の事件のような取りこぼしが起きる。

SKILL.md はリポジトリに残る。一度ちゃんと書いておけば、次の自分が同じ手順をそのまま踏める。手順書を頭の中に置くのをやめて、ファイルに固定した、という感覚に近い。

実際の SKILL.md

私のリリース手順は4ステップだ。それをそのまま skill の手順に落とした。.claude/skills/release-build/SKILL.md

---
name: release-build
description: プラグインのリリース前作業を補助する。readme.txtとメインPHPのバージョン更新、readme.txtの変更履歴の追記、配布用zipのビルドを行う。
disable-model-invocation: true
allowed-tools:
  - Read
  - Edit
  - Bash(git status *)
  - Bash(git log *)
  - Bash(scripts/build-zip.sh *)
  - Bash(unzip -l *)
---

# リリース前ビルド補助

リリース時の手順を順番に、抜けなく踏むための skill。
バージョンと変更履歴は確認のうえ書き換えてよいが、
コミット/プッシュは「提案」までに留め、実行しない。

## 注意

`allowed-tools` はここに挙げたツールを「確認なしで使ってよい」と事前承認するもので、
使えるツールの範囲を制限するものではない。
commit / push は実行せず、コマンド案の提示までに留めること。

## 手順

1. バージョン番号を上げる(2か所)
   - `readme.txt``Stable tag:`
   - メインPHPファイルのヘッダの `Version:`
   両方の現在値を読み出して提示し、新しい番号を確認してから、
   この2か所を一致させて書き換える。

2. `readme.txt` の変更履歴を更新する
   `git log <前回のバージョン>..HEAD` を要約し、`== Changelog ==` に
   新バージョンのエントリ下書きを出す。文面を確認してから書き込む。

3. コミットしてプッシュする(提案のみ)
   `git status` で対象を確認し、コミットメッセージ案(例: `Release vX.Y.Z`)と、
   実行すべき `git commit` / `git push` コマンドを「提案」として提示する。
   実際の commit / push はここでは実行しない。ユーザーが自分の手で行う。

4. 配布用zipを作る(Pro版・Free版それぞれ)
   `scripts/build-zip.sh <スラッグ>` を、Pro版・Free版のスラッグそれぞれで実行する。
   ビルド後は必ず `unzip -l` の出力を見せ、配布してはいけないものが
   入っていないことを確認する。特に、開発専用のツール類が混ざっていないか。
   最終チェックは人間が目視で行う。

ここで注意したいのが allowed-tools だ。これは「Claude が使えるツールを絞る」設定ではなく、挙げたツールを確認なしで使ってよい、と事前承認する設定になっている。リストに無いツールも呼び出し自体は可能で、その場合は通常の権限設定に従う。だから本当に使わせたくない操作は、allowed-tools から外すだけでは塞げず、権限設定の deny ルールや disallowed-tools で別途止める必要がある。今回は Bash 全体を白紙委任せず、git statusgit log、ビルドスクリプト、unzip -l のように使うコマンドだけを絞って事前承認している。そして取り返しのつかない commit / push は、allowed-tools の話とは別に、本文の指示で「提案のみ・実行しない」と明示して止めている。

ビルドスクリプト

ステップ4の実体はシェルスクリプトにして、skill から呼ぶ。私は zip -x で除外するやり方を使っているので、それに合わせている。scripts/build-zip.sh

#!/usr/bin/env bash
set -euo pipefail

# 使い方: ./scripts/build-zip.sh <スラッグ> [バージョン]
SLUG="${1:?スラッグを指定してください}"
VERSION="${2:-$(grep -m1 '^Stable tag:' "${SLUG}/readme.txt" | sed 's/.*:[[:space:]]*//')}"
OUT="${SLUG}-${VERSION}.zip"

rm -f "${OUT}"

# プラグインフォルダごと固め、配布に含めないものを -x で除外する
# -X は macOS の拡張属性を付けない指定
zip -rX "${OUT}" "${SLUG}/" \
  -x "*/.git/*" \
     "*/.github/*" \
     "*/.ci/*" \
     "*/.claude/*" \
     "*/docs/*" \
     "*/tools/*" \
     "*/__MACOSX/*" \
     "*.DS_Store" \
     "*/README.md" \
     "*/CLAUDE.md"

echo "built: ${OUT}"
unzip -l "${OUT}"   # 中身を一覧表示。最後に人間が混入チェック

*/docs/**/tools/* が、今回の事件の再発防止だ。開発でしか使わないツール(私の場合はライセンス生成ツール)を tools/ に、ドキュメントを docs/ のような配布しないフォルダにまとめて置き、丸ごと除外する。README.md(GitHub 用)は落とすが readme.txt(WordPress.org 用)は残す、という取り違えにも注意する。

このスクリプトはいくつか前提を置いている。カレントディレクトリ直下に <スラッグ>/readme.txt がある構成を想定しているので、リポジトリのルート自体がプラグイン本体ならパスを直す必要がある。Stable tag:trunk 運用のリポジトリではバージョン名として使えないので、その場合は第2引数でバージョンを明示する。ステップ2で使う git log <前回のバージョン>..HEAD も、その名前の Git タグが存在する前提で、タグが v1.2.3 形式か 1.2.3 形式かはリポジトリの運用に合わせて確認する。

置き場所と、手動起動に倒すまで

このリリースチェックはプラグインごとに微妙に違うので、個人用ではなくリポジトリの .claude/skills/ に置いている。

最初は、description に「リリース」「バージョンを上げる」「配布zipを作る」といった言葉を入れて、自動で拾われるように調整していた。ただ、実際に使ってみると、この skill は readme.txt やメインPHPのバージョンを書き換える。つまり参照用ではなく、副作用のあるワークフローだ。Claude Code 公式でも、commit / deploy 系のようにタイミングを人間が制御したい skill には disable-model-invocation: true が推奨されている。

そこで最終的には、自動ロードではなく /release-build と明示して起動する形にした。少し手間は増えるが、リリース作業だけは「Claude が気を利かせて始める」のではなく、自分が明示的に引き金を引くほうが安心だった。disable-model-invocation: true を付けても description が無駄になるわけではなく、/ の一覧や人間が読むときの説明としては残る。ただ、Claude の自動判断には使われなくなる。

ハマったところと、そこから考えたこと

この skill を作って一番はっきりしたのは、zip -x が「名前を挙げたものしか除外できない」という当たり前の事実だった。今回ヒヤッとしたのは、除外リストに無い新しいファイルが増えていたからで、リストに書いてある限りは安全、という前提そのものが崩れていた。-x のブラックリストは、自分が予想できたものしか守ってくれない。

だから skill にしても、最後の unzip -l の目視だけは省けない。むしろ skill の本当の値打ちは、その目視を毎回必ず通すところにある、と今は思っている。理屈の上では、ホワイトリスト方式(配布してよいファイルだけを明示して固める)にすれば、未知のファイルは構造的に入らない。次はそちらに寄せたいと考えているが、まだ手元の運用は zip -x のままなので、ここは断定せず、目視を最後の砦として残している。

SKILL.md を太らせすぎないことも途中で学んだ。手順の詳しい背景まで全部書くと長くなるので、本体は短く保ち、込み入った補足は別ファイルに逃がして、必要なときだけ読ませる形にした。

どこまで任せるか

バージョンの書き換えや変更履歴の下書きは skill に任せている。けれど、コミットとプッシュは提案までにして、実行は自分の手でやる。取り返しのつかない操作だけは、一拍置いて人間が引き金を引く、という線引きだ。AI エージェントにどこまで実行を許すか、未信頼の入力をどう扱うか、という重たい話は別の記事にまとめたので、ここでは深入りしない。

まとめ

派手な AI 活用ではなく、毎回やる地味な定常作業を SKILL.md に固定する、という使い方が、自分には一番効いた。きっかけは、配布物にライセンス生成ツールを混ぜかけたヒヤッとした一件だった。手順を仕組みに落としたいま、同じミスは起きにくくなったし、未来の自分が同じ手順をそのまま踏める。

プラグインの配布まわりでは、WordPress.org への SVN 公開作業の記録 や、審査で差し戻された全記録 を本家ブログに書いている。この記事を読んで、自分の配布zipの中身を一度 unzip -l で確かめてみた人がいたら、それでこの文章の役目は果たせている。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?