【Claude Codeスキル入門 第7回】検証スキルを作る(後編)― verify.sh、reading-diff.md、CLAUDE.mdを短くする、動かしたときの流れ

Claude Codeスキル入門 第7回 検証スキルを作る(後編)― verify.sh、reading-diff.md、CLAUDE.mdを短くする、動かしたときの流れ 生成AI

「検証をスキルにしたのに、2>&1 | cat を付けたせいで Ansible の終了コードが cat の 0 になり、成功したのか失敗したのか分かりません」
「スキルを作ったら、CLAUDE.md に書いてある検証の手順は消してよいのでしょうか」

前回の第6回で、検証スキル ansible-verify のフォルダーと SKILL.md を作りました。ただし、SKILL.md には「何を・どの順で」しか書いてありません。決まった処理(3つのコマンドを順に動かして結果をまとめる)をスクリプトに、読み方の知識を別ファイルに、それぞれ切り出して初めて、スキルは短く保てます。

第7回では、フォルダーの残りの2つ(scripts/verify.sh と reference/reading-diff.md)の中身、スキルができた後の CLAUDE.md の整理、そして依頼から人の承認までを通した流れを扱います。冒頭の1つ目の相談は「verify.sh の書き方」、2つ目は「CLAUDE.md を短くする」の節で答えます。スキルの作り方の全体は、連載「Claude Code実践入門」第7回の「Ansibleでの検証の型」(①〜⑥の6段階)が前提です。数値・仕様は2026年10月時点です。


scripts/verify.sh(案)

verify.sh は、①〜③(lint・構文確認・差分確認)を順に実行し、結果を RESULT 行にまとめるスクリプトです。Claude には、中身を読ませずに実行させます。決まった処理はスクリプトに閉じ込めると、中身は会話に入らず、出力だけがトークンを使います。

作りの方針は次の6点です。

  • 各コマンドに 2>&1 | cat:Claude Code で Ansible を動かすための付け方。Code タブだけでなく CLI でもエラーが出る(連載「Claude Code実践入門」第4回の「チームで共有するCLAUDE.mdのひな形(筆者の例)」の「環境の癖」)
  • 本当の終了コード:| cat の後の $? は cat の 0。${PIPESTATUS[0]} で Ansible 側の終了コードを取る
  • RESULT 行:最後に RESULT lint=0 syntax=0 check=0 の形で1行にまとめる。0 以外が1つでもあれば、まだ通っていない
  • 途中で止まらない:set -e を付けず、①が失敗しても②③まで実行して RESULT を出す(3つの結果を一度に見るため)
  • prod はスクリプト自身で止める:inventory に prod を含むときは何も実行せず RESULT skipped=prod で終わる
  • 呼び方と確認:bash を付けて呼ぶ。筆者の記録では、2.1.302 で verify.sh を実行したとき、lint と構文確認は 0 で、RESULT 行まで出た(第11回の手順6の囲み)
verify.sh の流れを上から下へ示した図。見出しは「verify.sh の流れ」。最初は「verify.sh <inventory> <playbook>」。次の橙色の箱「inventory に prod を含む?」から、「はい」の矢印で右の赤い箱「何も実行せず RESULT skipped=prod で終わる」へ進む。「いいえ」の場合は、① ansible-lint、② ansible-playbook --syntax-check、③ ansible-playbook --check --diff の3つを順に実行し、最後に緑の箱「RESULT lint=0 syntax=0 check=0 最後に1行にまとめる」に至る。右の枠「スクリプトの中のしかけ」には、各コマンドに 2>&1 | cat を付ける、終了コードは ${PIPESTATUS(0)} で取る(| cat の後の $? は cat の 0)、set -e は付けない(①が失敗しても②③まで実行して RESULT を出す)、の3点がある。右下の橙色の枠に「0 以外が1つでもあれば、まだ通っていない」とある。

スクリプトの案です。使い方は verify.sh <inventory> <playbook> です。

#!/usr/bin/env bash
# 使い方: verify.sh <inventory> <playbook>
inv="${1:?inventory を指定}"; pb="${2:?playbook を指定}"
case "$inv" in *prod*) echo "RESULT skipped=prod"; exit 0;; esac
ansible-lint "$pb" 2>&1 | cat; lint=${PIPESTATUS[0]}
ansible-playbook --syntax-check -i "$inv" "$pb" 2>&1 | cat; syn=${PIPESTATUS[0]}
ansible-playbook --check --diff -i "$inv" "$pb" 2>&1 | cat; chk=${PIPESTATUS[0]}
echo "RESULT lint=$lint syntax=$syn check=$chk"

読み方を順に見ます。

  • 3行目:引数が足りなければ、${1:?…} の形でメッセージを出して終わる
  • 4行目:inventory のパスに prod が入っていれば、何も実行せずに RESULT skipped=prod を出して終わる。本番への実行を止める最後の砦を、スクリプト自身に持たせるための行
  • 5〜7行目:lint・構文確認・差分確認。どれも 2>&1 | cat で出力をパイプに通し、直後に ${PIPESTATUS[0]} で Ansible 側の終了コードを変数に取る
  • 8行目:3つの終了コードを1行にまとめる

この案は、連載「Claude Code実践入門」第4回の「環境の癖」をスクリプトに閉じ込めたものです。「| cat を付けると終了コードが 0 になる」という癖を、Claude が毎回気を付けるのではなく、スクリプトが吸収します。

筆者が試したら

筆者の環境(Ubuntu 26.04.1、ansible-core 2.20.1、ansible-lint 26.1.1、Claude Code 2.1.302 の CLI)で、~/practice の ansible-verify を、非対話の claude -p で「検証して」と頼みました。-p は確認の画面が出せないので、承認が要る操作は実行されずに止まります。

  • 呼び方は bash .claude/skills/ansible-verify/scripts/verify.sh inventory/stg/hosts.ini site.yml。出力の最後は次の行でした
RESULT lint=0 syntax=0 check=4
  • lint と構文確認は 0 で通りました。check が 4 なのは、stg のホスト(192.0.2.11)が文書用のアドレスで、SSH が届かず unreachable だったためです。差分は取れていないので、「通った」とは言えません
  • 筆者が Claude に作らせた verify.sh(2.1.289 の演習の手順5で作らせたもの)は、手本の形のほかに、引数が2つでないときに使い方を出して終了コード 2 で終わる処理を足していました(頼んでいない処理を Claude が足すことがあるので、出来上がりは読んで確かめます)

終了コードの意味は、ansible-core のソースで確かめました。ExitCode の定義では SUCCESS = 0、HOST_FAILED = 2、HOST_UNREACHABLE = 4 で、実行の最後は、unreachable のホストがあれば unreachable、なければ失敗したホストの有無を見て値を返す作りでした(v2.20.1 のタグと devel のソースを確認)。同じ定義には PARSER_ERROR = 4 もあり、ソースに「HOST_UNREACHABLE と衝突している」という趣旨の FIXME の注記が付いています。公式のドキュメント(ansible-playbook のページ)には、終了コードの一覧の節が見つかりませんでした。古い二次情報には unreachable を 3 とするものもありますが、ソースとは食い違うので、この連載では使いません。したがって、RESULT 行の 0 以外を見たら、数字の意味を決めつけず、直前に出ているメッセージ(PLAY RECAP の unreachable や failed)で確かめます。

出典:https://raw.githubusercontent.com/ansible/ansible/v2.20.1/lib/ansible/errors/__init__.py 、https://raw.githubusercontent.com/ansible/ansible/v2.20.1/lib/ansible/plugins/strategy/__init__.py 、https://docs.ansible.com/projects/ansible/latest/cli/ansible-playbook.html

呼び方と、スクリプトの中のコマンド

Bash の権限ルールは、Claude が実行するコマンドの文字列に一致させるものです(https://code.claude.com/docs/ja/permissions )。公式は、ルールは Claude が書いたコマンドの文字列に対して照合され、deny や ask のルールは「プログラムの周りの安全の境界ではない」と説明しています。したがって、スクリプトの中で動くコマンドが個別に確かめられるかどうかについて、筆者は公式に直接の記述を見つけられていません。確かめられない前提にして、危ない操作はスクリプトに入れず、入れるなら verify.sh 自身で止めます(上の case の行)。

筆者の記録では、Claude が ansible-lint 2>&1 | tail -30; echo "== exit: ${PIPESTATUS[0]}" のように ${…} や $? を含むコマンドを直接実行しようとすると、allow に一致せず確認になりました(文言は「A variable in braces in this command can’t be checked before it runs」「The following part requires approval: echo “== exit: $?”」)。終了コードの取り出しをスクリプトの中に閉じ込めておくと、Claude が実行するコマンドは bash …/verify.sh … の1本になり、allow(第5回の「allowed-tools で事前承認する」)に書きやすくなります。


reference/reading-diff.md(案)

reading-diff.md は、Claude が差分を読むときの資料です。SKILL.md に書くと毎回読み込まれて長くなる、読み方の知識を入れます。

  • changed の意味:--check で changed のタスク=本実行で変わる所。ok は変わらない所
  • 計画と突き合わせる:changed が、計画に入っていたホストとタスクか。計画にない changed は、必ず報告に書く
  • 冪等性:本実行の後に、もう一度 --check して changed が残るタスクは冪等でない(⑥で人が頼む)
  • 落とし穴:handler の名前を変えると通知が届かない。名前の変更を見つけたら報告する(連載「Claude Code実践入門」第4回の「書くこと・書かないこと」の「落とし穴」の例)
  • 読ませ方:SKILL.md の手順3から、「差分を確かめるときだけ」とリンクする(第4回の「SKILL.md から「いつ読むか」を書く」)。見出しで探せる形にする

ファイルの案です。

# changed の読み方
- changed = --check で「本実行なら変わる」タスク
- 計画にあるホスト・タスクか。なければ報告に書く
# 冪等性
- 本実行の後の --check で changed が残る = 冪等でない
# 落とし穴
- handler の名前の変更(通知が届かなくなる)

連載「Claude Code実践入門」第7回の「要約ではなく差分から読む」を、Claude が読むための資料にしたものです。人が読む④の代わりではなく、報告に「計画にない変更の有無」を必ず入れさせるための資料です。


CLAUDE.md を短くする

2つ目の相談に答えます。検証の手順をスキルに移したら、CLAUDE.md の同じ手順は、重複するので整理できます。ただし全部は消しません。次の表は、連載「Claude Code実践入門」第4回のひな形(筆者の例)を、スキルを入れた後にどう変えるかの案です。

項目前(Claude Code実践入門 第4回のひな形)後
よく使うコマンド検査・構文確認・差分確認の3行「検証は ansible-verify スキルで行う」の1行
完了の定義lint の指摘0件。–check –diff の結果を貼る完了=RESULT 行が全部0で、changed が計画どおり
規約変数名・モジュールの規約変えない。いつも当てはまる
してはいけないことinventory/prod に実行しない変えない。スキルにも書くが、CLAUDE.md を正とする
環境の癖2>&1 | cat と PLAY RECAP で成否を見る残す。verify.sh 以外の ansible 系コマンドにも当てはまる
CLAUDE.md の前と後を比べた表の図。見出しは「CLAUDE.md の前と後」。列は「項目」「前」「後」。「よく使うコマンド」は、前が検査・構文確認・差分確認の3行、後が「検証は ansible-verify スキルで行う」の1行。「完了の定義」は、前が lint の指摘0件。--check --diff の結果を貼る、後が完了=RESULT 行が全部0で、changed が計画どおり。「規約」は、前が変数名・モジュールの規約、後が変えない。いつも当てはまる。「してはいけないこと」は、前が inventory/prod に実行しない、後が変えない。スキルにも書くが、CLAUDE.md を正とする。「環境の癖」は、前が 2>&1 | cat と PLAY RECAP で成否を見る、後が残す。verify.sh 以外の ansible 系コマンドにも当てはまる。下の橙色の枠に「消すのは、スキルが発動することを確かめてから(スキルは強制ではない)」とある。

考え方は「いつも当てはまることは CLAUDE.md に残し、手順はスキルへ」です。

  • 手順(3行のコマンド)は、スキルが読み込まれたときだけあれば足りるので、CLAUDE.md からは1行の案内にする
  • 規約と「してはいけないこと」は、検証のときだけでなく、いつも守らせたいので残す。スキルにも書くが、正は CLAUDE.md
  • 環境の癖は、verify.sh を通らない ansible 系のコマンドにも当てはまるので残す

公式も、複数の手順からなる説明や、コードベースの一部にだけ関係する説明は、スキルかパスを限ったルールへ移すよう勧めています(原文は「If an entry is a multi-step procedure or only matters for one part of the codebase, move it to a skill or a path-scoped rule instead.」。https://code.claude.com/docs/ja/memory )。

ただし、スキルは強制ではありません。CLAUDE.md の手順を消してよいのは、スキルが発動することを確かめてからです。確かめる前に消すと、スキルが発動しなかったとき、Claude は検証の手順を知らないまま作業を進めます。この確認は、第12回の「早く終わった人」で試せます(筆者は試していません)。


動かしたときの流れ

ここまでの部品を、一度通して動かしたときの様子です。「NTP の参照先を3台にして、検証まで」と頼むと、Claude はまず role を直します。依頼に「検証」が入っていて description と一致するので、ansible-verify が発動します。Claude は verify.sh を読まずに実行し、RESULT 行と changed の一覧を、計画と突き合わせて報告します。ここまでがスキルの仕事です。

報告を受けて、④差分を人が読み、⑤本実行を人が承認します。実行の確認の画面が出るかどうかは、モードと権限ルール、allowed-tools しだいです(第11回の手順6で確かめます)。スキルは③で止まり、先へは進みません。

依頼から人の承認までの流れを示す縦の図。上から、依頼「NTP の参照先を3台にして、検証まで」(人)、「role を直す」(Claude)、「ansible-verify が発動(description と一致)」、点線の小さな箱「確認の画面(出る条件は演習で確かめる)」、「verify.sh を実行」、「RESULT 行と changed の一覧を報告」(この3つがスキル)、赤い横線「止まる」、「④ 人が差分を読む」、「⑤ 人が本実行を承認」(この2つが人)。右側の括弧は、「role を直す」が「Claude」、発動から報告までが「スキル」、④と⑤が「人」
  • 依頼が description と一致すると発動
  • スキルの仕事は報告まで。③で止まる
  • ④差分を読むのと⑤の承認は人

確認の画面が出るかどうか(筆者の記録)

「確認の画面が出る条件」は、筆者の記録では次のようになりました(非対話の claude -p。確認の画面が出る操作は、実行されずに「承認が要る」として止まります)。

  • CLI 2.1.289:allowed-tools を書く前は、bash …/verify.sh … が allow に無く、承認が要って止まった。Claude は、許可済みの個別のコマンドに切り替えて検証した
  • allowed-tools に verify.sh の呼び方を書いたスキルは、呼ぶ時点でスキルの呼び出しそのものが承認待ちになった(記録の文言は「Execute skill: ansible-verify」。公式の文書に、この記述は見つかっていません)。Skill(ansible-verify) を許可すると、呼び出しの確認は消えた
  • CLI 2.1.302:Skill(ansible-verify) を許可した状態で「検証して」と頼むと、verify.sh が確認なしで実行され、RESULT 行まで出た(2.1.289 の -p では承認待ちだった)。CLI の CHANGELOG には、2.1.295 に「Skill ツールが応答の完了より先に終わると、スキルの allowed-tools が落ちて、-p で Bash が拒否される不具合の修正」があります

詳しくは第5回の「筆者が試したら:allowed-tools はいつ効いたか」と、第11回の手順6で扱います。Code タブの画面で同じ結果になるかは、筆者は確かめていません。


考えてみよう

  1. verify.sh が set -e を使わず、①が失敗しても②③まで実行するのは、なぜでしょうか
  2. 検証を頼んだら RESULT lint=0 syntax=0 check=4 と出ました。これを「検証に通った」と報告してよいでしょうか。何を見て確かめますか
  3. CLAUDE.md の「よく使うコマンド」を、いきなり1行に減らしてはいけない理由は何でしょうか

ヒント:1は「scripts/verify.sh(案)」の方針の4つ目、2は「筆者が試したら」、3は「CLAUDE.md を短くする」の最後の段落を見直してください。


まとめ

  • verify.sh は①〜③を順に動かし、RESULT 行にまとめる。各コマンドに 2>&1 | cat、終了コードは ${PIPESTATUS[0]}、set -e は付けない。Claude には読ませずに実行させる
  • prod はスクリプト自身で止める(RESULT skipped=prod)。Bash の権限ルールは Claude が書いたコマンドの文字列に対するもので、スクリプトの中は個別に確かめられない前提にする
  • RESULT 行に 0 以外があれば、通っていない。筆者の ~/practice では check=4(unreachable)。数字の意味を決めつけず、PLAY RECAP で確かめる
  • reading-diff.md は Claude が差分を読むための資料。changed・冪等性・落とし穴を書き、報告に「計画にない変更の有無」を入れさせる
  • CLAUDE.md は、いつも当てはまること(規約・してはいけないこと・環境の癖)を残し、手順をスキルへ。消すのはスキルが発動すると確かめてから
  • スキルの仕事は③の報告まで。④差分を読むのと⑤本実行の承認は人

次回の第8回では、できたスキルをチームに共有する方法(Git)と、.claude/ フォルダーの全体、共有の手順を扱います。


連載「Claude Codeスキル入門」全12回

全回の目次と、読む順番・目的別の読み方は、Claude Codeスキル入門の入口のページにまとめています。

  1. スキルとは何か
  2. SKILL.mdを書く
  3. 発動する説明を書く
  4. 複数のファイルに分ける
  5. スクリプトとallowed-tools
  6. 検証スキルを作る(前編)
  7. 検証スキルを作る(後編)(この記事)
  8. Gitでチームに共有する
  9. うまく動かないとき
  10. 演習(前編):作る・確かめる・発動させる
  11. 演習(中編):分ける・事前承認
  12. 演習(後編):共有と振り返り
seichan

プロフィール詳細は「プロフィール」をご覧ください。
AWS Certified Solutions Architect – Professional (https://www.credly.com/badges/fd8feb88-6f1f-42ff-a94b-445bb6661cb8/public_url)
Nutanix Certified Professional - Multicloud Infrastructure 6 (https://www.credly.com/badges/5c081bef-a425-42e4-809c-85138e676630/public_url)
Palo Alto Networks System Engineer (PSE) - Hardware Firewall Professional (https://www.credly.com/badges/69dc3880-4a51-47eb-8843-47ba6ea7d013/public_url)

seichanをフォローする
生成AI
シェアする
seichanをフォローする

コメント

タイトルとURLをコピーしました