AIコーディング 2026.05.05
Claude Code本番運用ガイド【2026年版】Hooks設定・権限管理・25エージェント構成の実装
Hooksアーキテクチャの基本設定
// .claude/settings.json
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git log*)",
"Bash(gh pr list*)",
"Read(*)"
],
"deny": [
"Bash(git push --force*)",
"Bash(rm -rf*)",
"Bash(DROP TABLE*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "bash .claude/hooks/pre_tool_check.sh"
}]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "bash .claude/hooks/post_tool_log.sh"
}]
}
]
}
}
gh コマンドの readonly ラッパー
#!/bin/bash
# .claude/hooks/gh-readonly-wrapper.sh
# write操作をブロックして読み込みのみ許可する
COMMAND="$*"
# 書き込み操作のブロックリスト
WRITE_OPERATIONS=("pr create" "pr merge" "pr close" "issue create" "issue close" "release create" "push")
for op in "${WRITE_OPERATIONS[@]}"; do
if echo "$COMMAND" | grep -q "$op"; then
echo "❌ 書き込み操作はブロックされました: gh $COMMAND"
echo " 本番環境での書き込みは手動確認後に実行してください"
exit 1
fi
done
# 読み込み操作は通す
echo "✅ 読み込み操作を実行: gh $COMMAND"
gh $COMMAND
操作ログのフック実装
# .claude/hooks/pre_tool_check.py
# ツール実行前にログを記録して危険操作を検知する
import sys
import json
import logging
from datetime import datetime
from pathlib import Path
logging.basicConfig(
filename=".claude/agent_operations.log",
level=logging.INFO,
format="%(asctime)s %(message)s"
)
def check_tool_call(tool_name: str, tool_input: dict) -> bool:
"""ツール実行前の安全チェック"""
DANGEROUS_PATTERNS = [
"rm -rf", "DROP TABLE", "git push --force",
"DELETE FROM", "TRUNCATE", "git reset --hard"
]
input_str = json.dumps(tool_input)
for pattern in DANGEROUS_PATTERNS:
if pattern.lower() in input_str.lower():
logging.warning(f"BLOCKED: {tool_name} with pattern '{pattern}': {input_str[:200]}")
print(f"⚠️ 危険な操作をブロック: '{pattern}'")
return False
logging.info(f"ALLOWED: {tool_name}: {input_str[:100]}")
return True
# Claude Codeからhookとして呼ばれる
if __name__ == "__main__":
hook_data = json.loads(sys.stdin.read())
tool_name = hook_data.get("tool_name", "")
tool_input = hook_data.get("tool_input", {})
if not check_tool_call(tool_name, tool_input):
sys.exit(1) # ← exit code 1でClaude Codeは実行を停止
sys.exit(0) # ← exit code 0で実行を許可
25エージェント構成のコスト管理
# agent_cost_tracker.py
# 複数エージェントの使用量を一元管理する
import anthropic
from datetime import date
import json
from pathlib import Path
client = anthropic.Anthropic()
MODELS = {
"haiku": "claude-haiku-4-5-20251001",
"sonnet": "claude-sonnet-4-5",
"opus": "claude-opus-5",
}
PRICES = {
"claude-haiku-4-5-20251001": (0.80, 4.00),
"claude-sonnet-4-5": (3.00, 15.00),
"claude-opus-5": (15.00, 75.00),
}
usage_log_path = Path(".claude/daily_usage.json")
def track_usage(agent_name: str, model: str, input_tokens: int, output_tokens: int) -> float:
"""エージェントの使用量を記録してコストを返す"""
inp, out = PRICES.get(model, (3.00, 15.00))
cost = (input_tokens * inp + output_tokens * out) / 1_000_000
today = str(date.today())
log = json.loads(usage_log_path.read_text()) if usage_log_path.exists() else {}
if today not in log:
log[today] = {}
if agent_name not in log[today]:
log[today][agent_name] = {"cost": 0.0, "calls": 0}
log[today][agent_name]["cost"] += cost
log[today][agent_name]["calls"] += 1
usage_log_path.write_text(json.dumps(log, indent=2))
return cost
def daily_summary() -> dict:
"""今日の全エージェントのコスト合計"""
if not usage_log_path.exists():
return {}
log = json.loads(usage_log_path.read_text())
today = str(date.today())
return log.get(today, {})
print("今日のエージェント別コスト:")
for agent, data in daily_summary().items():
print(f" {agent}: ${data['cost']:.4f} ({data['calls']}回呼び出し)")