Claude Code 実践マスタークラス #1 — コマンド・フック・サブエージェント3ステップ自動化

Claude Code 実践マスタークラス #1 — コマンド・フック・サブエージェント3ステップ自動化

スラッシュコマンド(.claude/commands/)でタスクを定義し、settings.jsonフックでイベントに連結し、サブエージェント(.claude/agents/)に委任する3ステップのClaude Code自動化パターンを実際のブログ自動化システム構築事例でコード例を交えて解説します。

今読んでいるこの記事は、今朝11時30分に自動実行されたlaunchdジョブがClaude Codeを起動し、/daily-tech-blogスラッシュコマンドを実行し、サブエージェントたちがリサーチと翻訳を分担して作った結果物である可能性が高い。

この数ヶ月、私はこの自動化パイプラインを自分で組み、動かしてきた。完璧ではない。タイムアウトが起きる。ビルドが落ちる。ある言語のバージョンだけ生成されて終わる日もある。それでも、このシステムがなければ毎日4言語で記事を出すのは物理的に無理だった。

このシリーズはその過程で学んだことを整理する。第1回の今回は、核心となる3ステップを最初から作る方法を扱う。スラッシュコマンドフック、そしてサブエージェントだ。

Step 1: スラッシュコマンド: .claude/commands/ フォルダがすべて

Claude Codeで/commit/review/deployのようなコマンドを作る方法は驚くほどシンプルだ。.claude/commands/ディレクトリに.mdファイルを1つ置くだけでいい。

ファイル名がそのままコマンド名になる:

.claude/
└── commands/
    ├── commit.md          → /commit
    ├── daily-review.md    → /daily-review
    └── publish.md         → /publish

ファイルの中身は自然言語の指示文だ。Markdownに見えるが、コードのように動作する:

# Publish Command

Validate and publish the blog post to production.

## Usage
/publish <slug>

## Workflow
1. Run npm run validate:publishing
2. Run npm run build
3. Run git add and commit with the slug
4. Run git push origin main

Report errors clearly with the step number.

これだけだ。Claude Codeセッションで/publish my-post-slugと入力すれば、上記のワークフローがそのまま実行される。Claudeが各ステップを解釈してツール呼び出しに変換する。

この構造を初めて見たとき驚いたのは、プログラミング言語が要らないという点だった。手順をテキストで書けば、Claudeが状況に合わせて実行してくれる。もちろん意図と違う解釈をされることもあって、この予測しづらさは正直まだ手こずっている。

コマンド作成のコツ

「何をすべきか」だけでなく、「なぜこの順序か」「どんな状況で違う動作をすべきか」を合わせて書くと、はるかに精度が上がる:

# Daily Tech Blog

Research, write, validate, and publish one daily article.

## Context
- Today's date: use `date +%F`
- Blog repo: ~/Documents/workspace/www.jangwook.net
- Content types: how-to (Mon-Wed), news (Thu-Fri), series (Sat-Sun)

## Failure Handling
- If sandbox test fails: switch to Source Review lane
- If build fails 3 times: stop and report
- Never ask the user — this runs unattended

“Never ask the user”の一行が自律実行モードを実現する鍵だ。これがないと、Claudeは不確かな状況のたびに確認を求めて止まる。cronジョブでは致命的になる。

Step 2: settings.jsonフック: イベント駆動の自動化

スラッシュコマンドが「何をすべきか」を定義するなら、フックは「いつ自動的に動くべきか」を定義する。

.claude/settings.jsonhooksフィールドにイベント-コマンドのペアを登録する:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/send-telegram.sh 'Claudeがタスクを完了しました'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[audit] $(date) — ファイル書き込み発生\" >> ~/.claude/audit.log"
          }
        ]
      }
    ]
  }
}

4種類のフックタイプ

タイプ実行タイミング主な用途
PreToolUseClaudeがツールを呼び出す直前危険なコマンドのブロック、監査ログ
PostToolUseツール呼び出しが完了した直後ファイル保存後のlint、コミット後の通知
StopClaudeが応答を完全に停止するとき完了通知、クリーンアップ
SessionStartClaudeセッションが開始するとき時間コンテキストの注入、環境設定

私の設定で最も便利だったのはStopフックだ。長い自動化タスク(30分〜1時間)が終わると、Telegramで通知が届く。これを設定してから「まだ終わったかな?」とターミナルを覗き込む習慣が完全になくなった。

permissionsの設定: 許可リストベースのセキュリティ

フックと合わせて必ず設定すべきなのがpermissionsだ。Claude Codeはデフォルトで、すべてのBashコマンド実行前にユーザーの承認を要求する。自動化環境ではこの動作がパイプラインを止めてしまう。

許可するコマンドを事前に登録しておけば、承認プロンプトなしで実行される:

{
  "permissions": {
    "allow": [
      "Bash(git log:*)",
      "Bash(git diff:*)",
      "Bash(git add:*)",
      "Bash(git commit:*)",
      "Bash(git push:*)",
      "Bash(npm run build:*)",
      "Bash(npm run validate:*)"
    ]
  }
}

注意: 許可リストを広く設定しすぎると(Bash(*)のように)、意図しないコマンドが実行される可能性がある。実際に必要なコマンドパターンだけを登録するのが安全だ。

フックを実際のコードレビューパイプラインに適用した詳細な事例は、Claude Code Hookで構築する自動化コードレビューシステムで確認できる。

Step 3: サブエージェント委任: .claude/agents/ 専門化されたAI

リサーチ + 記事執筆 + SEO最適化 + 翻訳 + ビルドをすべて1つのClaudeに任せると、各分野の集中度が下がる。トークンコンテキストも無駄になる。

サブエージェントは、各役割に特化した別個のClaudeインスタンスを作る概念だ。.claude/agents/フォルダにfrontmatterを持つMarkdownファイルとして定義する:

---
name: writing-assistant
description: Technical blog post writer. Use when creating multilingual (ko/ja/en/zh) developer content.
tools: Read, Write, WebSearch
---

You are a technical writer specializing in developer-focused content.

Core rules:
- Write for developers who will actually run the code
- Include at least 3 first-person experience references
- Verify technical claims before writing
- Never fabricate benchmarks or logs

frontmatterのdescriptionフィールドが重要だ。オーケストレーターClaudeが「どのエージェントをいつ使うべきか」を判断するときにこのフィールドを参照する。曖昧に書くと、見当違いのエージェントが呼ばれるか、完全に無視される。

toolsフィールドには、そのエージェントが実際に必要なツールだけを列挙する。Write権限のないリサーチエージェントは、誤ってファイルを修正することができない。役割の特化と権限の制限を同時に実現する方法だ。

このブログでは現在19のエージェントが稼働している:

  • writing-assistant: 4言語の記事執筆
  • seo-optimizer: メタタグ、内部リンクの最適化
  • web-researcher: トレンドリサーチとファクトチェック
  • content-recommender: relatedPostsの生成
  • image-generator: ヒーロー画像ブリーフの作成

エージェントをチームとして組織するより複雑なパターンは、Claude Code Agent Teams完全ガイドで扱った。

3ステップ統合: 実際の自動化パイプライン

理論より実際に動くパイプラインを見た方が理解しやすい。このブログの毎日の自動化フローはこうなっている:

macOS launchd (毎日11:30)

daily-tech-blog.sh

claude --dangerously-skip-permissions "/daily-tech-blog"

/daily-tech-blog スラッシュコマンド実行
    ├── トレンドリサーチ (サブエージェント: web-researcher)
    ├── サンドボックステスト (mktemp)
    ├── 4言語記事執筆 (サブエージェント: writing-assistant)
    ├── npm run validate:publishing
    ├── npm run build
    └── git push origin main

Stopフック → send-telegram.sh → Telegram通知

最も重要なファイルはdaily-tech-blog.shだ。Claudeを呼び出すコア部分:

run_with_timeout "$MAX_TIMEOUT" claude --dangerously-skip-permissions \
  "/daily-tech-blog" \
  < /dev/null >> "$LOG_FILE" 2>&1 || CLAUDE_EC=$?

--dangerously-skip-permissionsはすべての権限プロンプトをスキップする。名前が示すように危険だ。許可リストが適切に定義されている状態でのみ使うべきで、個人の自動化プロジェクト以外への使用は勧めない。

< /dev/nullはstdinを閉じ、Claudeが対話型入力を待ち続けるのを防ぐ。cronコンテキストでは必須だ。

実際の実行ログはこのようになっている:

launchd plistの設定も参考になる:

<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>11</integer>
    <key>Minute</key>
    <integer>30</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/jangwook/logs/launchd-daily-tech-blog.log</string>

ログをファイルにリダイレクトしておくと、「なぜ今日の記事が公開されなかったのか」をデバッグするときに非常に役立つ。

実際に始める方法: 最小設定5分

この3ステップを初めて導入する人のために、最小限の動作サンプルをまとめる。既存のプロジェクトにそのまま適用できる。

1. フォルダ構造の作成

mkdir -p .claude/commands .claude/agents

2. 最初のスラッシュコマンド (.claude/commands/review.md)

# Review Command

Review recent changes before committing.

## Steps
1. Run git diff --staged to see staged changes
2. Check for: hardcoded secrets, console.log, TODO comments
3. Suggest improvements or approve with "LGTM"

For detailed security analysis, delegate to @checker agent.

3. 完了通知フックの設定 (.claude/settings.json)

{
  "permissions": {
    "allow": ["Bash(git:*)", "Bash(npm:*)"]
  },
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "say 'Claudeがタスクを完了しました'"
      }]
    }]
  }
}

4. 最初のエージェント定義 (.claude/agents/checker.md)

---
name: checker
description: Code quality reviewer. Use when checking files for issues before commit.
tools: Read, Grep
---

Review the provided files for syntax errors, obvious bugs, and security issues.
Rate: SAFE / CAUTION / CRITICAL

この4つのファイルが最小動作単位だ。これだけで/reviewがステージング済みの変更を分析し、作業終了時に音声通知を受け取り、詳細な検査が必要な場合はcheckerエージェントに委任できる。

正直な評価: うまくいかないこと

このシステムを3ヶ月運用した結果、いくつかの現実的な制約に直面した。

コストの問題: 4言語で2,500語以上の記事を毎日自動生成すると、月々のAPIコストが予想以上に積み上がる。Anthropic Maxサブスクリプションでなんとか管理しているが、このコストを受け入れずにこの規模の自動化は難しい。

タイムアウト処理: ビルドが遅かったり、サブエージェントのチェーンが長くなると、60分のタイムアウトに引っかかる。記事が半分だけ生成された状態で中断する。タイムアウト検知後のクリーンアップロジックがなければ、リポジトリの状態が壊れる。

エージェント品質の非決定性: 同じコマンドを2回実行しても結果が異なることがある。記事の品質、内部リンクの位置、relatedPostsの選択。どれも日によって変わる。これはLLMの性質上避けられず、QAループ(validate:publishing、astro check、build)で最低限の品質基準を保つのが現実的だ。

正直に言えば、このシステムを「プロダクションレディ」と呼ぶのは時期尚早だ。私の基準では「個人の自動化には十分安定しているレベル」だ。チーム規模で使うには、エラー回復、状態管理、監査ログをより堅固に設計する必要がある。

エージェントワークフローを体系的に考えるフレームワークが必要なら、Claude Code エージェントワークフローパターン5種類が参考になる。

次回: #2 MCPサーバー連携

第1回は.claude/フォルダ内で完結する自動化を扱った。

第2回ではさらに一歩進む。MCPサーバーを自作してClaude Codeに外部ツールを接続する方法だ。Notionデータベースを読み込み、Slackにメッセージを送り、PostgreSQLにクエリを投げる。こうした外部システム連携がスラッシュコマンド1つで可能になる構造を解説する。

MCPサーバーを構築した経験があるなら、MCPサーバー構築実践ガイドが良い事前読書になる。


この記事の.claude/ディレクトリ構造とシェルスクリプトのサンプルは、実際のjangwook.netブログ自動化システムから直接取得したものです。

よくある質問

スラッシュコマンドを作るのにプログラミングは必要ですか?
いいえ。.claude/commands/ ディレクトリに .md ファイルを1つ置くだけで、ファイル名がそのままコマンド名になります。ファイルの中身は自然言語の指示文で、Claudeが各ステップを解釈してツール呼び出しに変換します。
--dangerously-skip-permissions フラグは安全に使えますか?
名前の通り危険なフラグで、すべての権限プロンプトをスキップします。許可リストが適切に定義されている状態でのみ使うべきで、本文では個人の自動化プロジェクト以外への使用を勧めていません。
フックにはどんな種類があり、どれが最も便利でしたか?
PreToolUse、PostToolUse、Stop、SessionStart の4種類があります。筆者は長い自動化タスクが終わるとTelegramに通知を送るStopフックが最も便利だったと述べています。
frontmatterのdescriptionフィールドはなぜ重要なのですか?
オーケストレーターのClaudeが、どのエージェントをいつ使うべきかを判断するときにこのフィールドを参照するからです。曖昧に書くと、見当違いのエージェントが呼ばれるか、完全に無視されます。

他の言語で読む

この記事は役に立ちましたか?

より良いコンテンツを作成するための力になります。コーヒー一杯で応援してください。

著者について

jw

Kim Jangwook

AI/LLM専門フルスタック開発者

10年以上のWeb開発経験を活かし、AIエージェントシステム、LLMアプリケーション、自動化ソリューションを構築しています。Claude Code、MCP、RAGシステムの実践的な知見を共有します。

ブログリストへ