ブログ一覧に戻る

SKILL.mdの書き方|Claude Code Skills設計ベストプラクティス【肥大化防止】

Claude CodeのSKILL.md設計ガイド。肥大化を87%削減するスクリプト化戦略を実例付きで解説。Claude Code Skillsの作り方からベストプラクティスまで網羅。

21分で読める
SKILL.mdの書き方|Claude Code Skills設計ベストプラクティス【肥大化防止】

SKILL.mdとは?Claude Code Skillsの核心ファイル

Claude Codeの~/.claude/skills/ディレクトリに配置するSKILL.mdは、AIエージェントの振る舞いを定義するMarkdownファイルです。このファイルの設計次第で、Claude Codeの使い勝手とコスト効率が大きく変わります。

この記事で解決する課題:

  • SKILL.mdが肥大化してcontext tokenを消費する
  • Claude Code Skillsの作り方がわからない
  • ベストプラクティスに沿った設計方法を知りたい

「Claude Codeにスキル作らせたら、SKILL.mdが300行超えた...」

AIに任せると便利だけど、気づけばSKILL.mdが肥大化して、context tokenを食い尽くす。そんな経験、ありませんか?

Claude Code公式ドキュメントでは「SKILL.mdは500行以下に保つ」「スクリプトはcontext消費なしで実行できる」というベストプラクティスが示されています。本記事では、この原則をブログ自動公開システムという具体的なユースケースに適用し、87%のSKILL.md削減を達成した実践例を共有します。

この記事で学べること

  • Claude Code Skillsの肥大化が起きる理由
  • 「決定論的」vs「非決定論的」処理の見分け方
  • SKILL.mdを最小化するスクリプト分離パターン
  • 完全動作するスクリプト実装例
  • 実際に遭遇した落とし穴と対処法

前提条件

  • Claude Codeを使ったことがある
  • ~/.claude/skills/ の基本的な構造を理解している
  • シェルスクリプトの基礎知識

💡 公式リファレンス

なぜSKILL.mdは肥大化するのか

AIは「親切」すぎる

Claude CodeにSkillを作らせると、こんなことが起きます:

# SKILL.md(肥大化例)

## Usage

...(30行)

## Workflow

1. まず〇〇を確認します
2. 次に△△を判定します
3. もし□□の場合は...
4. そうでなければ...
   ...(100行続く)

## Examples

...(50行)

## Troubleshooting

...(30行)

親切心からすべての分岐とエッジケースを自然言語で記述しようとする。結果、読み込むだけでcontextの10%が消費される...なんてことも(コーヒー1杯分のコストが毎回飛んでいく)。

本当の問題は「再解釈コスト」

SKILL.mdに書かれた自然言語の手順は、毎回AIが解釈し直す必要があります。

「次の公開日を計算する」
  ↓ AIが解釈
「今日の日付を取得して、曜日を判定して、
  火曜か木曜なら次のその曜日を...」
  ↓ 再解釈のたびにトークン消費

これが決定論的な処理(入力が同じなら出力も同じ)であれば、スクリプトで一発解決です。

決定論的 vs 非決定論的処理

決定論的処理(スクリプト向き)

処理特徴
日付計算入力→出力が固定次回公開日の算出
ファイル存在チェックYes/No判定seedディレクトリの確認
JSON生成テンプレート出力manifest.json作成
文字列変換ルールベースslug生成、パス変換

これらはスクリプト化すべき。

非決定論的処理(AI向き)

処理特徴
文章生成創造性が必要ブログ記事本文
判断・選択文脈依存カテゴリ推定、タグ提案
対話・確認ユーザー意図の理解切り口の選択

これらはAIに任せる。

完全動作するスクリプト実装

ディレクトリ構成

.claude/skills/blog-publish/
├── SKILL.md              # 最小限の呼び出し定義(42行)
└── scripts/
    ├── orchestrate.sh    # メインエントリーポイント
    ├── detect_mode.sh    # モード判定
    └── get_next_date.sh  # 日付計算

get_next_date.sh - 次回公開日の計算

「次の火曜または木曜」を計算する完全なスクリプト:

#!/bin/bash
# get_next_date.sh - 次の火曜or木曜を計算
# Usage: ./get_next_date.sh [base_date]
# Output: YYYY-MM-DD

set -euo pipefail

BASE_DATE="${1:-$(date +%Y-%m-%d)}"

# 曜日を取得(0=日, 1=月, ..., 6=土)
day_of_week=$(date -j -f "%Y-%m-%d" "$BASE_DATE" "+%w" 2>/dev/null || \
              date -d "$BASE_DATE" "+%w")

# 次の火曜(2)または木曜(4)までの日数を計算
case $day_of_week in
  0) days_to_tue=2; days_to_thu=4 ;;  # 日曜
  1) days_to_tue=1; days_to_thu=3 ;;  # 月曜
  2) days_to_tue=7; days_to_thu=2 ;;  # 火曜 → 次は木曜
  3) days_to_tue=6; days_to_thu=1 ;;  # 水曜
  4) days_to_tue=5; days_to_thu=7 ;;  # 木曜 → 次は火曜
  5) days_to_tue=4; days_to_thu=6 ;;  # 金曜
  6) days_to_tue=3; days_to_thu=5 ;;  # 土曜
esac

# 早いほうを選択
if [[ $days_to_tue -le $days_to_thu ]]; then
  days_to_add=$days_to_tue
else
  days_to_add=$days_to_thu
fi

# 日付を計算(macOS/Linux両対応)
if date -v +1d > /dev/null 2>&1; then
  # macOS
  date -j -v +${days_to_add}d -f "%Y-%m-%d" "$BASE_DATE" "+%Y-%m-%d"
else
  # Linux
  date -d "$BASE_DATE + $days_to_add days" "+%Y-%m-%d"
fi

detect_mode.sh - ソース種別の判定

#!/bin/bash
# detect_mode.sh - 入力ソースの種別を判定
# Usage: ./detect_mode.sh <source>
# Output: repo | export | blog | unknown

set -euo pipefail

SOURCE="$1"

if [[ "$SOURCE" =~ ^https://github\.com/ ]]; then
  echo "repo"
elif [[ "$SOURCE" =~ \.mdx$ ]]; then
  echo "blog"
elif [[ "$SOURCE" =~ \.md$ ]]; then
  echo "export"
else
  echo "unknown"
fi

orchestrate.sh - メインエントリーポイント

#!/bin/bash
# orchestrate.sh - Skill初期化のオーケストレーター
# Usage: ./orchestrate.sh <source> [--date YYYY-MM-DD]

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SOURCE="$1"
DATE_ARG=""

# 引数パース
shift
while [[ $# -gt 0 ]]; do
  case "$1" in
    --date) DATE_ARG="$2"; shift 2 ;;
    *) shift ;;
  esac
done

# 各処理を実行
MODE=$(bash "$SCRIPT_DIR/detect_mode.sh" "$SOURCE")

if [[ -n "$DATE_ARG" ]]; then
  PUBLISH_DATE="$DATE_ARG"
else
  PUBLISH_DATE=$(bash "$SCRIPT_DIR/get_next_date.sh")
fi

# seedディレクトリの確認
SEED_EXISTS=false
if [[ "$MODE" == "repo" ]]; then
  # GitHubURLからowner/repoを抽出
  REPO_PATH=$(echo "$SOURCE" | sed 's|https://github.com/||' | sed 's|\.git$||')
  SEED_DIR="seed/${REPO_PATH//\//-}"
  [[ -d "$SEED_DIR" ]] && SEED_EXISTS=true
fi

# JSON出力
jq -n \
  --arg mode "$MODE" \
  --arg source "$SOURCE" \
  --arg publish_date "$PUBLISH_DATE" \
  --argjson seed_exists "$SEED_EXISTS" \
  '{
    mode: $mode,
    source: $source,
    publish_date: $publish_date,
    seed_exists: $seed_exists
  }'

SKILL.md - 最小限の定義

# blog-publish

## Usage

`/blog-publish <source> [--date next|YYYY-MM-DD]`

## Init

`bash scripts/orchestrate.sh <source> [--date DATE]`

Returns: `{mode, source, publish_date, seed_exists}`

## Phase Execution

| Phase     | Condition     | Skill                |
| --------- | ------------- | -------------------- |
| Seed      | mode=repo     | AskUser: reuse?      |
| Blog      | !--skip-blog  | `seed-to-blog`       |
| Thumbnail | !--skip-thumb | `generate-thumbnail` |
| SNS       | !--skip-sns   | `sns-announce`       |

42行。 AIが知るべきは「何を呼び出すか」だけ。

スクリプト言語の選択

Bash vs Python vs Node.js

言語メリットデメリット向いている処理
Bash依存なし、軽量複雑なロジックが辛いファイル操作、コマンド連携
Python可読性高い、ライブラリ豊富環境依存データ処理、API連携
Node.jsJSON処理が得意node_modules問題Web API、非同期処理

私たちはBash + jqの組み合わせを採用しました。理由:

  1. 依存が少ない - jqさえあれば動く
  2. Claude Codeとの相性 - シェル実行が最もシンプル
  3. デバッグしやすい - 各スクリプトを単独テスト可能

実際に遭遇した落とし穴

落とし穴1: macOS/Linux互換性

最初のバージョンではdateコマンドの互換性を考慮していませんでした。

# ❌ macOSでしか動かない
date -v +1d

# ❌ Linuxでしか動かない
date -d "+1 day"

# ✅ 両対応
if date -v +1d > /dev/null 2>&1; then
  date -v +${days}d -f "%Y-%m-%d" "$BASE" "+%Y-%m-%d"
else
  date -d "$BASE + $days days" "+%Y-%m-%d"
fi

落とし穴2: JSON出力のエスケープ

タイトルに引用符が含まれているとJSONが壊れました。

# ❌ 引用符でJSONが壊れる
echo "{\"title\": \"$TITLE\"}"

# ✅ jqを使えば安全
jq -n --arg title "$TITLE" '{title: $title}'

落とし穴3: エラーハンドリング

set -eだけでは不十分でした。

# ❌ パイプの途中で失敗しても気づかない
cat file.txt | grep pattern | wc -l

# ✅ pipefailで全段階をチェック
set -euo pipefail

効果測定

Token消費量の比較

項目BeforeAfter削減率
SKILL.md サイズ312行42行87%削減
1回の実行コスト~8,000 tokens~1,200 tokens85%削減
エラー発生率月5回程度月1回未満80%減少

なぜエラーが減るのか

自然言語の曖昧さがなくなるから。

# Before(自然言語)

「次の火曜または木曜のうち、早いほうを選ぶ」
→ AIが「早いほう」を誤解釈する可能性

# After(スクリプト)

if [[$days_to_tue -le $days_to_thu]]; then
days_to_add=$days_to_tue
fi
→ 常に同じ結果

応用:他のSkillへの適用

同じパターンを他のSkillにも適用しています:

Skillスクリプト化した処理効果
generate-thumbnail画像パス生成、存在チェック50%削減
sns-announceハッシュタグ生成、文字数計算40%削減
cross-post-publish記事一覧取得、カテゴリフィルタ60%削減

まとめ

Claude Code Skillsを設計する際の原則:

  1. 決定論的処理はスクリプト化 - 日付計算、ファイル操作、JSON生成
  2. SKILL.mdは呼び出し定義のみ - 「何を」呼ぶかだけ記述
  3. AIには創造性が必要な部分を任せる - 文章生成、判断、対話
  4. OS互換性とエラーハンドリングを忘れずに - set -euo pipefailと両OS対応

AIに「次の火曜を教えて」と毎回聞くのは、電卓を持ってるのにソロバンの達人を雇うようなもの。適材適所で行きましょう。


お問い合わせはこちら

実験に参加しませんか?

正解のない問いに、一緒に挑みましょう。まずはお気軽にご相談ください。