gh skill の基本を理解した後、チームに展開しようとすると新しい問いが生まれます。「誰がスキルを更新できるか」「バックエンドとフロントエンドで違うスキルが必要では」「スキルが壊れていないかどうかをどう検証するか」——これらに答えるには、スキル管理を「インフラ」として設計する必要があります。
ここでは複数のAIエージェントを使うチームが直面する実際の課題と、それに対処するアーキテクチャを詳しく解説します。
スキルライブラリの設計原則
スキルを設計するとき、最も重要な判断は「何をスキルにするか」です。私がたどり着いた原則は三つです。
1. AIエージェントへの指示のみをスキルにする:コードそのものや設定ファイルはスキルではありません。「このコードを書くときにAIにどう振る舞ってほしいか」という指示だけをスキルに含めます。
2. スキルは小さく保つ:一つのスキルファイルに詰め込みすぎると、更新が難しくなります。1ファイル = 1つの責任領域(コードレビュー、テスト作成、コミットメッセージなど)に保ちます。
3. 全エージェントに適用できるスキルと、特定エージェント専用のスキルを分ける:前者は汎用層、後者はエージェント固有層として管理します。
この原則に基づいたリポジトリ構造:
enterprise-skills/
├── skill.yaml
├── README.md
├── CHANGELOG.md
├── .github/
│ └── workflows/
│ ├── validate.yml
│ └── release.yml
├── universal/ # 全エージェント共通
│ ├── code-review.md
│ ├── commit-conventions.md
│ ├── testing-principles.md
│ └── security-checklist.md
├── domain/ # ドメイン別
│ ├── frontend/
│ │ ├── react.md
│ │ └── accessibility.md
│ ├── backend/
│ │ ├── api-design.md
│ │ └── database.md
│ └── mobile/
│ ├── ios.md
│ └── android.md
├── agent-specific/ # エージェント固有
│ ├── claude-code/
│ │ ├── hooks.md
│ │ └── tool-restrictions.md
│ └── copilot/
│ └── completion-hints.md
└── profiles/ # プロファイル定義
├── frontend.yaml
├── backend.yaml
└── mobile.yaml
プロファイルによるスキルの役割別配布
チームの役割によって必要なスキルは異なります。skill.yaml のプロファイル機能でこれを制御します:
# skill.yaml
name: enterprise-skills
version: 3.0.0
# グローバルエージェント設定
agents:
- claude-code
- copilot
- cursor
- gemini-cli
# プロファイル定義
profiles:
# フロントエンドエンジニア向け
frontend:
description: "React・TypeScript中心の開発向け"
skills:
- universal/code-review.md
- universal/commit-conventions.md
- universal/testing-principles.md
- domain/frontend/react.md
- domain/frontend/accessibility.md
agents: [claude-code, copilot, cursor]
agent_overrides:
claude-code:
append: agent-specific/claude-code/hooks.md
# バックエンドエンジニア向け
backend:
description: "API設計・DB中心の開発向け"
skills:
- universal/code-review.md
- universal/commit-conventions.md
- universal/security-checklist.md
- domain/backend/api-design.md
- domain/backend/database.md
agents: [claude-code, copilot, cursor, gemini-cli]
# モバイル開発者向け
mobile-android:
description: "Androidアプリ開発向け"
skills:
- universal/code-review.md
- universal/testing-principles.md
- domain/mobile/android.md
agents: [claude-code, copilot, cursor]
# 全部入り(フルスタック・アーキテクト向け)
fullstack:
extends: [frontend, backend]
additional_skills:
- universal/security-checklist.mdエンジニアのオンボーディング時:
# フロントエンドエンジニアの場合
gh skill install company/enterprise-skills --profile frontend
# バックエンドエンジニアの場合
gh skill install company/enterprise-skills --profile backend
# 複数プロファイルの組み合わせ
gh skill install company/enterprise-skills --profile frontend,backendエージェント間の解釈能力差を設計で吸収する
最も難しい設計上の課題は「Claude Codeは理解できるが、Copilotは無視する」という指示の扱いです。
私のアプローチは「三層構造」です:
第一層:命令文(全エージェント対応)
<!-- universal/code-review.md の書き方 -->
## コードレビュー手順
コードレビューを求められたら、以下を確認してください:
- 型安全性の問題がないか
- エラーハンドリングが適切か
- パフォーマンス上の問題がないか
問題を発見した場合、具体的な改善コードを示してください。この層は全エージェントが理解できる「命令文」で書きます。条件分岐や複雑なロジックは含めません。
第二層:拡張指示(Claude Code + Cursor)
<!-- agent-specific/claude-code/hooks.md -->
## 自動チェックの設定(Claude Code専用)
コードを保存する前に:
1. TypeScriptの型チェック実行(tsc --noEmit)
2. ESLintの実行(eslint --fix)
3. 変更ファイルの確認
テストファイルを変更した場合は、関連するテストスイートを実行すること。第三層:提案ヒント(Copilot補完最適化)
<!-- agent-specific/copilot/completion-hints.md -->
コード補完時の優先事項:
- TypeScript strict modeに準拠
- async/awaitを優先(Promiseチェーンは避ける)
- 型エイリアスよりinterfaceを優先この三層構造により、Claude Codeユーザーは最も詳細な指示を受け、Copilotユーザーは補完ヒントのみを受け取ります。エージェントが「理解できない指示を無視する」という特性を逆手に取った設計です。
GitHub ActionsによるCI/CD検証パイプライン
スキルをPRマージ前に自動検証します:
# .github/workflows/validate.yml
name: Validate Skills
on:
pull_request:
paths:
- '**.md'
- 'skill.yaml'
- 'profiles/*.yaml'
jobs:
validate-structure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install gh skill
run: gh extension install github-actions/gh-skills
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Validate skill.yaml
run: gh skill validate --strict
- name: Check all profiles are valid
run: |
for profile in profiles/*.yaml; do
gh skill validate --profile "$profile"
echo "✅ $profile OK"
done
- name: Dry-run for each agent
run: |
for agent in claude-code copilot cursor; do
gh skill dry-run --agent "$agent" --profile frontend
gh skill dry-run --agent "$agent" --profile backend
echo "✅ $agent: all profiles dry-run OK"
done
- name: Check character encoding and formatting
run: |
# 全スキルファイルのエンコーディング確認
find . -name "*.md" | while read f; do
file -b --mime-encoding "$f" | grep -q "utf-8" || {
echo "❌ Not UTF-8: $f"
exit 1
}
done
echo "✅ All files are UTF-8"
- name: Validate CHANGELOG is updated
run: |
CHANGED_SKILLS=$(git diff --name-only origin/main | grep "\.md$" | grep -v CHANGELOG | wc -l)
CHANGED_CHANGELOG=$(git diff --name-only origin/main | grep "CHANGELOG.md" | wc -l)
if [ "$CHANGED_SKILLS" -gt 0 ] && [ "$CHANGED_CHANGELOG" -eq 0 ]; then
echo "❌ スキルが変更されましたがCHANGELOGが更新されていません"
exit 1
fi
# スキルの互換性テスト
compatibility-test:
runs-on: ubuntu-latest
needs: validate-structure
steps:
- uses: actions/checkout@v4
- name: Test skill content quality
run: |
python scripts/validate_skill_quality.py# scripts/validate_skill_quality.py
import os
import re
from pathlib import Path
def check_skill_quality(filepath: str) -> list[str]:
"""スキルファイルの品質をチェック"""
issues = []
content = Path(filepath).read_text(encoding="utf-8")
# 禁止パターン
FORBIDDEN_PATTERNS = [
(r'\bmust\b.*\bor\b.*\bwill\b', "曖昧な「mustまたはwill」の使用"),
(r'^#\s+TODO', "未完成のTODOセクション"),
(r'placeholder|TBD|未定義', "未定義のプレースホルダー"),
]
for pattern, message in FORBIDDEN_PATTERNS:
if re.search(pattern, content, re.IGNORECASE | re.MULTILINE):
issues.append(f"{filepath}: {message}")
# 最小長チェック
if len(content) < 100:
issues.append(f"{filepath}: スキル内容が短すぎます({len(content)}文字)")
# H2見出しの存在確認
if not re.search(r'^## ', content, re.MULTILINE):
issues.append(f"{filepath}: 構造化された見出し(##)がありません")
return issues
# 全スキルファイルをチェック
all_issues = []
for skill_file in Path("universal").rglob("*.md"):
all_issues.extend(check_skill_quality(str(skill_file)))
if all_issues:
for issue in all_issues:
print(f"❌ {issue}")
exit(1)
print(f"✅ 全スキルファイルの品質チェックが通過しました")スキルの変更管理とコミュニケーション
スキルが更新されたとき、チームメンバーが気づいて更新する仕組みが必要です。
# .github/workflows/release.yml
name: Release and Notify
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate release notes from CHANGELOG
run: |
# CHANGELOGから最新バージョンのセクションを抽出
python scripts/extract_changelog_section.py > release_notes.md
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
body_path: release_notes.md
- name: Send Slack notification
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///')
# CHANGELOGからbreaking changesを検出
BREAKING=$(grep -c "Breaking change" release_notes.md || true)
if [ "$BREAKING" -gt 0 ]; then
URGENCY="⚠️ *破壊的変更あり* — スキル更新前に変更内容を確認してください"
else
URGENCY="✅ 通常のアップデート"
fi
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d "{
\"text\": \"🎯 enterprise-skills ${VERSION} がリリースされました\n${URGENCY}\n更新コマンド: \`gh skill update enterprise-skills\`\"
}"この設計を通じて見えてきたこと
AIエージェントへの指示を「管理された資産」として扱い始めると、興味深い変化が起きます。チームの会話の中に「このスキルの書き方は正確か」という議論が生まれてきます。
linterがコードの記述方法を統一するように、SKILL.mdのCIがAIへの指示の品質を可視化します。これまで個人の経験と勘に委ねられていた「AIをうまく使う技術」が、チームの共有知識になっていく感覚があります。
もちろん、全てのチームにとって最適かどうかはわかりません。スキル管理に時間をかけるよりも、エージェントを自由に使ったほうが生産性が上がるチームもあるでしょう。でも「AIの動作の一貫性」を重要視するチームには、この設計は価値を提供できると思っています。
個人開発12年の現場で実感したこと
線引きするときの3つの判断軸
- 失敗時の影響が金銭やユーザー体験にどれだけ波及するか
- 復旧オペレーションが明文化されているか
- 観測ログから人間が再現できる粒度に整っているか