Créer des Skills
Guide pas à pas pour ajouter de nouvelles expertises à ton projet Arc OS.
Avant d'écrire un nouveau skill — vérifie la bibliothèque globale
Arc OS est livré avec 40+ skills génériques dans skills/ couvrant les rôles courants. Avant d'en rédiger un nouveau, regarde si l'existant couvre ton besoin :
| Domaine | Skills disponibles |
|---|---|
| Engineering | commit-discipline, tdd-flow, react-best-practices, postgres-patterns, mcp-builder |
| Frontend / Design | frontend-design-principles, svg-graphics, slide-deck-html, service-design, scss-modular-architecture, responsive-mobile-first |
| Startup ops | market-analysis, gtm-strategy, financial-modeling, pitch-deck, customer-development, fundraising, founder-brand, metrics-dashboard, legal-startup |
| Marketing | seo-foundations, founder-brand |
| Process | standup-aggregator, commit-discipline |
| Database | postgres-patterns |
Liste complète : GET /api/crm/skills?category=<X> ou bun -e "console.log(JSON.stringify(require('./skills/_registry.json').skills.map(s=>s.name),null,2))".
Skill = déclenché par triggers, pas toujours actif
Un skill s'auto-injecte dans le prompt du worker quand le message correspond à ses triggers (dans _registry.json). Le champ description est de la logique de déclenchement ("quand l'utiliser"), pas un résumé. Le Context Router choisit les 5 meilleures correspondances par message.
Qu'est-ce qu'un Skill
Un skill est un ensemble d'instructions portable qui enseigne à ton bot IA une capacité spécifique. Les skills vivent dans le répertoire skills/ et se composent de :
<name>.md— le fichier d'instructions (requis)<name>.evals.json— règles de validation qualité automatiques (optionnel, mais recommandé)references/— documents d'appui, schémas, exemples (optionnel)
skills/
├── _registry.json ← Central registry
├── my-new-skill/
│ ├── skill.md ← What the AI should do
│ ├── my-new-skill.evals.json ← How to validate the output
│ └── references/ ← Context documents
└── library/
└── domain-expert.md ← Simple single-file skills
Étape 1 : Rédiger skill.md
C'est l'instruction centrale. Claude lit ce fichier lorsque le skill est activé.
Template
# Skill Name
## Purpose
What this skill accomplishes in one sentence.
## When to Use
- Trigger condition 1
- Trigger condition 2
## Protocol
1. First action
2. Second action
3. Third action
## Output Format
What the response should look like.
## Constraints
- What NOT to do
- Safety boundaries
Bonnes pratiques
- Moins de 500 mots — un skill trop long dilue le contexte
- Mode impératif — "Vérifie le dépôt" plutôt que "Tu devrais vérifier"
- Exemples concrets — montre les paires entrée/sortie attendues
- Références d'outils précises — nomme les commandes, les API ou les patterns à utiliser
Exemple : Odoo Expert
# Odoo Expert
## Purpose
Odoo ERP module development: models, views, security, QWeb templates.
## When to Use
- Building or modifying Odoo modules
- Writing QWeb templates
- Configuring access rules and record rules
- Working with the ORM (create, write, search, browse)
## Protocol
1. Check if modifying an existing module or creating new
2. Follow Odoo 17 conventions (manifest, models/, views/, security/)
3. Use self.env['model.name'] for ORM operations
4. Always use _t() or t-call for translatable strings
5. Test with --test-tags after changes
## Constraints
- Never use cr.execute() for direct SQL — use ORM
- Never use sudo() unless security context explicitly requires it
- No inline JavaScript in QWeb templates
Étape 2 : Rédiger les evals (règles de qualité)
Les evals sont des règles déclaratives qui valident automatiquement chaque réponse. Elles s'exécutent après que Claude génère sa sortie et ajoutent des notes de bas de page si des règles échouent.
Nommage du fichier
skills/<name>/<name>.evals.json
Schéma
{
"version": 1,
"skill": "my-new-skill",
"rules": [
{
"id": "ms-001",
"name": "Human-readable rule description",
"type": "rule_type",
"value": "for string/length types",
"pattern": "for regex types",
"severity": "warning"
}
]
}
Types de règles disponibles
| Type | Ce qu'il vérifie | Champ utilisé |
|---|---|---|
string_contains |
La réponse contient un texte littéral | value (string) |
string_not_contains |
La réponse ne contient PAS le texte | value (string) |
regex_match |
La réponse correspond à la regex | pattern (string) |
regex_not_match |
La réponse ne correspond PAS à la regex | pattern (string) |
max_length |
Longueur de la réponse <= N caractères | value (number) |
min_length |
Longueur de la réponse >= N caractères | value (number) |
Niveaux de sévérité
warning— Indique un problème de qualité. Affiché sous forme de⚠️.info— Note informative. Affichée sous forme deℹ️.
Exemple
{
"version": 1,
"skill": "odoo-expert",
"rules": [
{
"id": "oe-001",
"name": "Must use ORM, not raw SQL",
"type": "string_not_contains",
"value": "cr.execute",
"severity": "warning"
},
{
"id": "oe-002",
"name": "Must use translation helpers",
"type": "regex_match",
"pattern": "_t\\(|t-call|t-esc",
"severity": "warning"
},
{
"id": "oe-003",
"name": "Response under 5000 chars",
"type": "max_length",
"value": 5000,
"severity": "info"
}
]
}
Conseils
- Commence par 2-3 règles. Ajoutes-en au fur et à mesure que tu identifies des patterns à partir des corrections.
- La sécurité d'abord :
string_not_containspour les commandes dangereuses,regex_not_matchpour les patterns de credentials. - Convention d'ID :
<skill-prefix>-<numéro>(ex.oe-001,gm-002). - Teste : Envoie un message censé déclencher le skill, vérifie que les warnings apparaissent correctement.
Étape 3 : Enregistrer dans _registry.json
Le registre informe le Context Router de l'existence de ton skill afin qu'il puisse être recommandé pour les messages pertinents.
Ajouter une entrée
{
"name": "my-new-skill",
"description": "One sentence (shown in SKILLS_HINT, max 80 chars).",
"triggers": ["direct-command", "explicit-request", "пряма команда"],
"keywords": ["broader", "semantic", "related-term"],
"agents": ["rick"],
"category": ["complex"],
"phase": "21.5"
}
Triggers vs Keywords
Triggers (2 points chacun) : Signaux d'invocation directe. L'utilisateur demande explicitement ce skill.
- "deploy", "review", "scaffold", "odoo", "audit"
Keywords (1 point chacun) : Termes plus larges qui suggèrent une pertinence sans être des commandes.
- "OWASP", "Docker", "CI/CD", "production", "module"
Bonnes pratiques
- Inclus des équivalents ukrainiens dans les triggers pour les équipes bilingues
- Garde les descriptions sous 80 caractères (tronquées dans SKILLS_HINT)
- 5 à 8 triggers par skill (plus = matching trop bruyant)
- Les keywords sont optionnels, mais améliorent significativement le routage
Étape 4 : Skills de bibliothèque (format simplifié)
Pour une expertise métier qui ne nécessite ni evals ni répertoire dédié, utilise skills/library/ :
skills/library/
├── docker-ops.md
├── postgres-pro.md
└── react-patterns.md
Ce sont des fichiers .md simples. Pas de répertoire, pas d'evals, pas de references. Idéal pour les skills riches en connaissances et pauvres en validation.
Checklist
-
skills/<name>/skill.md— instruction avec Purpose, Protocol, Constraints -
skills/<name>/<name>.evals.json— au moins 2 règles de validation - Entrée dans
skills/_registry.json— avec triggers et keywords - Le nom est cohérent partout — nom du répertoire = nom du skill dans les evals = nom dans le registre
- Test : envoie un message trigger → vérifie SKILLS_HINT dans les logs
- Test : déclenche intentionnellement un échec d'eval → vérifie que le warning apparaît
Ce qui se passe à l'exécution
1. Bot starts → loadRegistry() reads _registry.json
loadEvals() reads all .evals.json files
loadLearnings() reads learnings.md
2. Message arrives → routeContext() scores skills → SKILLS_HINT injected
formatForPrompt() adds LEARNINGS block
3. Claude responds → checkOutput() runs eval rules
formatEvalWarnings() appends footnotes
4. Fix It / thumbs-down → addLearning() writes to learnings.md
qualityTracker logs feedback
5. Nightly → findUnderperformingSkills() checks metrics
Proposals sent to CEO for approval
Seeder les skills dans la DB globale
Les skills en DB (table skills_global) sont le SSOT du Context Router. Le fichier sur disque est la source de vérité pour le contenu ; la DB est l'index rapide pour le matching + l'auto-injection. Ils se synchronisent via des scripts de seed.
Seeder générique par catégorie
# Reads skills/_registry.json, filters by category, upserts skills_global rows.
# Idempotent + drift-aware (skip if no change, update on content drift).
bun scripts/seed-skill-category.ts --category engineering
bun scripts/seed-skill-category.ts --category frontend
bun scripts/seed-skill-category.ts --category marketing
Fonctionne pour n'importe quelle catégorie présente dans _registry.json. Prend la première catégorie du tableau comme valeur de colonne DB.
Seeders spécifiques
scripts/seed-startup-skills.ts— codé en dur pour category="startup" (10 skills startup operations)scripts/migrate-skills-to-db.ts— migration en masse historique (tous les fichiers, insert uniquement, pas d'update)
Quand les lancer
- Après création d'un nouveau
.md+ entrée_registry.json→ lancer le seeder une fois - Après mise à jour du contenu d'un
.mdexistant → le seeder drift-aware met à jour la DB - Bootstrap d'un nouveau projet → les seeders tournent via
vps-sync.shStep 1.5 (prévu)
Vérification
sqlite3 data/citadel.db "SELECT name, category, owner_project FROM skills_global ORDER BY id"
owner_project = NULL → global (visible par tous les projets). owner_project = 'my-project' → réservé au projet (seul ce projet le voit).
Skills project-only vs globaux
| Type de skill | owner_project |
Cas d'usage |
|---|---|---|
| Global (template) | NULL | Méthodologie générique (TDD, Conventional Commits, AARRR) — utile à n'importe quel projet |
| Project-only | '<project-name>' |
Documents propres à NOTRE codebase (p. ex. crm-api-reference ne vit que dans arc-v2) |
Passer un skill existant en project-only :
sqlite3 data/citadel.db "UPDATE skills_global SET owner_project='my-project' WHERE name='internal-api-docs'"
Le filtre listForProject gère la visibilité : les autres projets ne verront pas un skill project-only.