Getting gh skill working is one thing. Operating it reliably for a team of twenty engineers across four AI agents is another. The questions that emerge: who owns which skills? How do frontend and backend engineers get different skill sets? How do you catch broken skills before they deploy?
This article addresses those questions with a production-ready architecture.
Design Principles for Skill Libraries
Before structure, some principles that reduce future pain:
1. Skills contain only AI instructions: Not code, not config files. Only guidance about how AI agents should behave. What to check during review. How to format commits. When to ask for confirmation.
2. One file, one responsibility: A skill file for code review. A separate file for testing standards. A separate file for commit conventions. Monolithic skill files are hard to update and hard to review.
3. Separate universal skills from agent-specific skills: Universal skills work across all agents. Agent-specific skills (Claude Code hooks, Copilot hints) live in their own layer and get delivered only to their target agent.
Repository Structure
enterprise-skills/
├── skill.yaml
├── CHANGELOG.md
├── .github/workflows/
│ ├── validate.yml
│ └── release.yml
├── universal/ # All agents
│ ├── 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/
│ └── android.md
├── agent-specific/
│ ├── claude-code/
│ │ ├── hooks.md
│ │ └── tool-restrictions.md
│ └── copilot/
│ └── completion-hints.md
└── profiles/
├── frontend.yaml
├── backend.yaml
└── mobile.yaml
Profile-Based Skill Distribution
# skill.yaml
name: enterprise-skills
version: 3.0.0
agents: [claude-code, copilot, cursor, gemini-cli]
profiles:
frontend:
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
copilot:
append: agent-specific/copilot/completion-hints.md
backend:
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:
skills:
- universal/code-review.md
- universal/testing-principles.md
- domain/mobile/android.md
fullstack:
extends: [frontend, backend]
additional_skills:
- universal/security-checklist.mdInstallation:
# At onboarding
gh skill install company/enterprise-skills --profile frontend
# Multiple profiles
gh skill install company/enterprise-skills --profile frontend,backendThree-Layer Agent Compatibility Architecture
The hardest design challenge: Claude Code understands nuanced conditional instructions; Copilot mostly doesn't. Writing skills that work across both requires intentional layering.
Layer 1 — Universal imperatives (all agents):
<!-- universal/code-review.md -->
## Code Review Process
When asked to review code, check:
- Type safety issues and null handling
- Error handling completeness
- Performance concerns (unnecessary allocations)
- Testability of the design
Report each issue with: the problematic code, the specific problem, and a corrected example.Keep this layer to direct imperatives. No conditionals. No "if environment is X, do Y." Agents either follow it or ignore it harmlessly.
Layer 2 — Extended workflow instructions (Claude Code, Cursor):
<!-- agent-specific/claude-code/hooks.md -->
## Automated Checks (Claude Code)
Before writing to any of these paths, confirm:
- `production.env`
- `database/migrations/`
- `src/auth/`
After test file changes, run the related test suite and report results.
When the review shows more than 3 issues, ask whether to fix them interactively or report a summary.Layer 3 — Completion hints (Copilot optimization):
<!-- agent-specific/copilot/completion-hints.md -->
Completion priorities:
- TypeScript strict mode compliance
- async/await over Promise chains
- Prefer interfaces over type aliases
- Error handlers must include a log statementThe design exploits agents' natural behavior: they ignore what they don't understand. Claude Code gets everything; Copilot gets the universal layer plus the simple hints; both produce more consistent output than without any skill definitions.
CI Validation Pipeline
# .github/workflows/validate.yml
name: Validate Skills
on:
pull_request:
paths: ['**.md', 'skill.yaml', 'profiles/*.yaml']
jobs:
validate:
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 structure
run: gh skill validate --strict
- name: Validate all profiles
run: |
for profile in profiles/*.yaml; do
gh skill validate --profile "$profile"
done
- name: Dry-run cross-agent compatibility
run: |
for agent in claude-code copilot cursor; do
for profile in frontend backend; do
gh skill dry-run --agent "$agent" --profile "$profile"
done
done
- name: Quality checks
run: python scripts/validate_skill_quality.py
- name: Enforce CHANGELOG update
run: |
CHANGED=$(git diff --name-only origin/main | grep "\.md$" | grep -v CHANGELOG | wc -l)
CHANGELOG=$(git diff --name-only origin/main | grep "CHANGELOG.md" | wc -l)
if [ "$CHANGED" -gt 0 ] && [ "$CHANGELOG" -eq 0 ]; then
echo "Skill files changed without CHANGELOG update"
exit 1
fi# scripts/validate_skill_quality.py
import re
from pathlib import Path
def check_skill(filepath):
issues = []
content = Path(filepath).read_text(encoding="utf-8")
if len(content) < 100:
issues.append(f"{filepath}: Too short ({len(content)} chars)")
if not re.search(r'^## ', content, re.MULTILINE):
issues.append(f"{filepath}: Missing ## headings")
if re.search(r'TODO|TBD|placeholder', content, re.IGNORECASE):
issues.append(f"{filepath}: Contains placeholder text")
return issues
all_issues = []
for f in Path("universal").rglob("*.md"):
all_issues.extend(check_skill(str(f)))
if all_issues:
for issue in all_issues:
print(f"❌ {issue}")
exit(1)
print("✅ All skill quality checks passed")Automated Release Notifications
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
body_path: CHANGELOG.md
- name: Notify team
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///')
BREAKING=$(grep -c "Breaking" CHANGELOG.md || true)
NOTE="✅ Standard update"
[ "$BREAKING" -gt 0 ] && NOTE="⚠️ Breaking changes — review before updating"
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-d "{\"text\": \"🎯 enterprise-skills ${VERSION} released\n${NOTE}\nUpdate: gh skill update enterprise-skills\"}"What This Architecture Changes
The most meaningful outcome isn't that AI agents become more consistent — though they do. It's that skill definitions become visible artifacts with owners and review history.
When someone proposes "let's add TypeScript strict mode to the code review skill," that conversation happens in a PR, with CI evidence that the change works across all agents. Disagreements get resolved explicitly. The team's collective understanding of how to use AI agents gets codified.
Linters make code style visible and objective. This architecture does the same for AI agent behavior. Whether your team needs that level of rigor depends on how much you've already felt the cost of inconsistency — but for teams that have, the investment pays back quickly.