Aller au contenu

Structure d'un dépôt de catalog

Un catalog est un dépôt git ordinaire : catalog.json à la racine, plus les fichiers qui portent le contenu de chaque artifact. Chaque nature a une convention de chemin fixe. Quand agent-rigger installe une entrée, il résout l’id de l’entrée en un chemin sous ces répertoires ; un fichier au mauvais endroit n’est pas trouvé.

Deux natures ne portent aucun fichier. Un serveur mcp est déclaré en ligne dans catalog.json via son champ config, et un tool est déclaré en ligne via ses champs check et install. Aucun des deux n’a de répertoire dans le dépôt.

Pour une entrée dont l’id est <nature>:<name> (par exemple skill:diagnose, nom diagnose) :

Nature Chemin Notes
skill skills/<name>/SKILL.md Tout le répertoire skills/<name>/ est le skill. Tout ce qu’il contient, y compris les sous-dossiers comme scripts/, est copié dans le store.
agent agents/<name>.md Un unique fichier Markdown.
context contexts/<name>/AGENTS.md Le fichier AGENTS.md dans un répertoire nommé.
guardrail guardrails/<name>/ Contient les fichiers de descripteur ci-dessous.
hook hooks/<name>.ts Un script dans le répertoire partagé hooks/. Voir hooks.
plugin plugins/<name>.<ext> (opencode) Résolu par basename : l’extension n’est pas fixe. Les plugins Claude Code ne portent aucun fichier ; ils sont installés en déléguant à claude, indexés via un .claude-plugin/marketplace.json à la racine.
mcp aucun En ligne dans catalog.json (config).
tool aucun En ligne dans catalog.json (check, install).

Un guardrail vit sous guardrails/<name>/. Les fichiers que contient le répertoire dépendent des assistants que l’entrée cible :

Fichier Assistant Règle
deny.json Claude Code Requis et non vide. Les règles de blocage. Un guardrail est fail-closed : une liste deny vide ou absente est une erreur.
allow.json Claude Code Optionnel. Les règles d’autorisation.
permission.json opencode Le descripteur permission natif d’opencode, chargé tel quel. Un guardrail opencode natif l’exige : absent ou vide est une erreur bloquante. Il n’est jamais traduit depuis les règles Claude.

Un guardrail qui cible les deux assistants peut porter les trois fichiers dans un seul répertoire. Un guardrail qui n’en cible qu’un ne peut porter que les fichiers de cet assistant. Scinder les deux en entrées nommées séparément (par exemple guardrail:claude et guardrail:opencode) est également valide, puisque <name> est arbitraire.

Tout ce qui se trouve sous hooks/ est copié dans le store en bloc, pas seulement le <name>.ts que nomme une entrée hook donnée. C’est ce qui permet aux scripts de hook de partager du code : un helper dans hooks/_shared/hook-lib.ts est présent à l’exécution pour tout hook qui l’importe.

L’underscore en tête est la convention qui marque un chemin comme du code partagé plutôt qu’une entrée. Une entrée hook:<name> correspond à hooks/<name>.ts ; un fichier ou répertoire dont le nom commence par _ n’est jamais une entrée hook, seulement une dépendance de l’une d’elles.

Le <name> dérivé d’un id d’entrée (la partie après le préfixe <nature>:) sert à construire les chemins du store, les chemins cibles et les symlinks. Il doit correspondre à l’allowlist [a-zA-Z0-9._-]+ : lettres, chiffres, underscore, tiret et point uniquement. Un nom vide, ., .., ou tout nom contenant /, \ ou tout autre caractère est rejeté avant qu’aucun chemin ne soit construit. Voir confiance et sécurité pour la menace contre laquelle cela protège.

my-catalog/
├── catalog.json # meta + entries (mcp and tool live here, inline)
├── .claude-plugin/
│ └── marketplace.json # names the marketplace for Claude Code plugins
├── skills/
│ └── diagnose/
│ ├── SKILL.md # skill:diagnose (whole folder is the skill)
│ └── scripts/ # copied into the store with it
├── agents/
│ └── reviewer.md # agent:reviewer (one file)
├── contexts/
│ └── team/
│ └── AGENTS.md # context:team
├── guardrails/
│ ├── claude/
│ │ ├── deny.json # guardrail:claude (required, non-empty)
│ │ └── allow.json # optional
│ └── opencode/
│ └── permission.json # guardrail:opencode (native descriptor)
├── hooks/
│ ├── _shared/
│ │ └── hook-lib.ts # shared code, not an entry (leading _)
│ └── guard-command.ts # hook:guard-command
└── plugins/
└── my-plugin.js # plugin:my-plugin (opencode, basename lookup)