audit-discovery-symlinks
À propos
Cette compétence audite et répare les liens symboliques de découverte de code Claude pour les compétences, agents et équipes en comparant les registres aux répertoires .claude/. Elle détecte les liens symboliques manquants, cassés ou superflus, distingue le contenu de l'almanach, et peut optionnellement réparer les problèmes. Utilisez-la après l'ajout de nouveaux composants, des modifications du dépôt, ou lorsque les commandes slash échouent, comme vérification de maintenance.
Installation rapide
Claude Code
Recommandénpx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/audit-discovery-symlinksCopiez et collez cette commande dans Claude Code pour installer cette compétence
Documentation
audit-discovery-symlinks
When to Use
- After adding new skills, agents, or teams to the almanac
- After a repository rename or move that may have broken absolute symlinks
- When slash commands or agents are not found in Claude Code
- As a periodic health check to catch drift between registries and discovery paths
- When onboarding a new project that should discover shared almanac content
Do NOT use for creating the initial symlink hub from scratch. See the symlink-architecture guide for first-time setup.
Inputs
| Parameter | Type | Required | Description |
|---|---|---|---|
almanac_path | string | No | Absolute path to agent-almanac root. Auto-detected from .claude/ symlink targets or cwd if omitted |
scope | enum | No | project, global, or both (default: both) |
fix_mode | enum | No | report (default: audit only), auto (fix all safe issues), interactive (prompt before each fix) |
Procedure
Step 1: Identify Almanac Path
Locate the agent-almanac root directory.
# Auto-detect from current project's .claude/agents symlink
ALMANAC_PATH=$(readlink -f .claude/agents 2>/dev/null | sed 's|/agents$||')
# Fallback: check if cwd is the almanac
if [ -z "$ALMANAC_PATH" ] || [ ! -f "$ALMANAC_PATH/skills/_registry.yml" ]; then
if [ -f "skills/_registry.yml" ]; then
ALMANAC_PATH=$(pwd)
fi
fi
# Fallback: check global agents symlink
if [ -z "$ALMANAC_PATH" ] || [ ! -f "$ALMANAC_PATH/skills/_registry.yml" ]; then
ALMANAC_PATH=$(readlink -f ~/.claude/agents 2>/dev/null | sed 's|/agents$||')
fi
echo "Almanac path: $ALMANAC_PATH"
Got: ALMANAC_PATH points to a directory containing skills/_registry.yml, agents/_registry.yml, and teams/_registry.yml.
If fail: If auto-detection fails, ask the user for the almanac_path input. The almanac root is the directory containing skills/, agents/, teams/, and their registries.
Step 2: Inventory Registries
Extract the canonical lists of skills, agents, and teams from their registries.
# Count registered skills (entries with "- id:" under domain sections)
REGISTERED_SKILLS=$(grep '^ \{6\}- id:' "$ALMANAC_PATH/skills/_registry.yml" | awk '{print $3}' | sort)
REGISTERED_SKILL_COUNT=$(echo "$REGISTERED_SKILLS" | wc -l)
# Count registered agents
REGISTERED_AGENTS=$(grep '^ \{2\}- id:' "$ALMANAC_PATH/agents/_registry.yml" | awk '{print $3}' | sort)
REGISTERED_AGENT_COUNT=$(echo "$REGISTERED_AGENTS" | wc -l)
# Count registered teams
REGISTERED_TEAMS=$(grep '^ \{2\}- id:' "$ALMANAC_PATH/teams/_registry.yml" | awk '{print $3}' | sort)
REGISTERED_TEAM_COUNT=$(echo "$REGISTERED_TEAMS" | wc -l)
echo "Registered: $REGISTERED_SKILL_COUNT skills, $REGISTERED_AGENT_COUNT agents, $REGISTERED_TEAM_COUNT teams"
Got: Counts match the total_skills, total_agents, total_teams values in each registry header.
If fail: If counts diverge from the header totals, the registry itself is out of sync. Note the discrepancy in the report but continue with the actual - id: entries as the source of truth.
Step 3: Audit Project-Level Symlinks
Check .claude/skills/*, .claude/agents, .claude/teams in the current project directory.
PROJECT_CLAUDE=".claude"
# --- Skills ---
# Items on disk (excluding _template)
PROJECT_SKILLS=$(ls "$PROJECT_CLAUDE/skills/" 2>/dev/null | grep -v '^_template$' | sort)
PROJECT_SKILL_COUNT=$(echo "$PROJECT_SKILLS" | grep -c .)
# Missing: in registry but not in project .claude/skills/
MISSING_PROJECT_SKILLS=$(comm -23 <(echo "$REGISTERED_SKILLS") <(echo "$PROJECT_SKILLS"))
# Broken: symlink exists but target doesn't resolve
BROKEN_PROJECT_SKILLS=$(find "$PROJECT_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -printf '%f\n' 2>/dev/null | sort)
# Extraneous: in project but not in registry (and not external)
EXTRA_PROJECT_SKILLS=$(comm -13 <(echo "$REGISTERED_SKILLS") <(echo "$PROJECT_SKILLS"))
# --- Agents ---
if [ -L "$PROJECT_CLAUDE/agents" ] || [ -d "$PROJECT_CLAUDE/agents" ]; then
PROJECT_AGENT_STATUS="OK"
test -d "$PROJECT_CLAUDE/agents" || PROJECT_AGENT_STATUS="BROKEN"
PROJECT_AGENT_COUNT=$(ls "$PROJECT_CLAUDE/agents/"*.md 2>/dev/null | wc -l)
else
PROJECT_AGENT_STATUS="MISSING"
PROJECT_AGENT_COUNT=0
fi
# --- Teams ---
# Teams are NOT symlinked. TeamCreate uses ~/.claude/teams/ for runtime state.
# A .claude/teams symlink is a misconfiguration — warn if found.
if [ -L "$PROJECT_CLAUDE/teams" ]; then
PROJECT_TEAM_STATUS="MISCONFIGURED"
PROJECT_TEAM_COUNT=0
# Stale symlink — should be removed to avoid collision with TeamCreate
else
PROJECT_TEAM_STATUS="OK"
PROJECT_TEAM_COUNT=0
fi
Got: Zero missing, zero broken. Extraneous items are classified and explained.
If fail: If .claude/ does not exist at all, the project has no discovery setup. Note this and skip to global audit.
Step 4: Audit Global Symlinks
Check ~/.claude/skills/* and ~/.claude/agents. Also check that ~/.claude/teams is NOT a symlink (it should be absent or a directory for TeamCreate runtime state).
GLOBAL_CLAUDE="$HOME/.claude"
# --- Skills ---
GLOBAL_SKILLS_ALL=$(ls "$GLOBAL_CLAUDE/skills/" 2>/dev/null | sort)
# Classify each entry: almanac vs external
ALMANAC_GLOBAL_SKILLS=""
EXTERNAL_GLOBAL_SKILLS=""
for item in $GLOBAL_SKILLS_ALL; do
target=$(readlink -f "$GLOBAL_CLAUDE/skills/$item" 2>/dev/null)
if [ -z "$target" ]; then
# Real directory (not a symlink) — external
EXTERNAL_GLOBAL_SKILLS="$EXTERNAL_GLOBAL_SKILLS $item"
elif echo "$target" | grep -q "^$ALMANAC_PATH"; then
ALMANAC_GLOBAL_SKILLS="$ALMANAC_GLOBAL_SKILLS $item"
else
EXTERNAL_GLOBAL_SKILLS="$EXTERNAL_GLOBAL_SKILLS $item"
fi
done
# Filter: _template is always extraneous for almanac content
ALMANAC_GLOBAL_SKILLS=$(echo "$ALMANAC_GLOBAL_SKILLS" | tr ' ' '\n' | grep -v '^_template$' | grep -v '^$' | sort)
# Missing: in registry but not in global almanac skills
MISSING_GLOBAL_SKILLS=$(comm -23 <(echo "$REGISTERED_SKILLS") <(echo "$ALMANAC_GLOBAL_SKILLS"))
# Broken: symlink exists but target doesn't resolve
BROKEN_GLOBAL_SKILLS=$(find "$GLOBAL_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -printf '%f\n' 2>/dev/null | sort)
# Stale almanac entries: in global almanac set but not in registry
STALE_GLOBAL_SKILLS=$(comm -13 <(echo "$REGISTERED_SKILLS") <(echo "$ALMANAC_GLOBAL_SKILLS"))
# --- Agents ---
if [ -L "$GLOBAL_CLAUDE/agents" ] || [ -d "$GLOBAL_CLAUDE/agents" ]; then
GLOBAL_AGENT_STATUS="OK"
test -d "$GLOBAL_CLAUDE/agents" || GLOBAL_AGENT_STATUS="BROKEN"
GLOBAL_AGENT_COUNT=$(ls "$GLOBAL_CLAUDE/agents/"*.md 2>/dev/null | wc -l)
else
GLOBAL_AGENT_STATUS="MISSING"
GLOBAL_AGENT_COUNT=0
fi
# --- Teams ---
# Teams are NOT symlinked. TeamCreate uses ~/.claude/teams/ for runtime state.
# A ~/.claude/teams symlink is a misconfiguration — warn if found.
if [ -L "$GLOBAL_CLAUDE/teams" ]; then
GLOBAL_TEAM_STATUS="MISCONFIGURED"
GLOBAL_TEAM_COUNT=0
# Stale symlink — should be removed to avoid collision with TeamCreate
else
GLOBAL_TEAM_STATUS="OK"
GLOBAL_TEAM_COUNT=0
fi
Got: Zero missing almanac skills, zero broken. External content (peon-ping, etc.) is listed but not flagged as errors.
If fail: If ~/.claude/ does not exist, the global hub is not set up. Refer to the symlink-architecture guide for initial setup.
Step 5: Generate Audit Report
Produce a summary table covering both layers.
# Discovery Symlink Audit Report
**Date**: YYYY-MM-DD
**Almanac**: <almanac_path>
**Scope**: both | project | global
## Summary
| Content | Registered | Project | Global (almanac) | Global (external) |
|---------|------------|---------|-------------------|-------------------|
| Skills | N | N | N | N |
| Agents | N | STATUS | STATUS | — |
| Teams | N | STATUS | STATUS | — |
## Issues
### Missing (registered but no symlink)
- Project skills: [list or "none"]
- Global skills: [list or "none"]
### Broken (symlink exists, target gone)
- Project: [list or "none"]
- Global: [list or "none"]
### Extraneous
- Stale almanac (in discovery but not registry): [list or "none"]
- _template in discovery path: [yes/no]
- External content (non-almanac): [list — informational only]
Got: A clear, actionable report. Zero issues means a clean bill of health.
If fail: If report generation itself fails, output raw counts and lists to the console as fallback.
Step 6: Repair (Optional)
If fix_mode is auto or interactive, fix the issues found.
6a. Create missing project symlinks:
for skill in $MISSING_PROJECT_SKILLS; do
ln -s "../../skills/$skill" "$PROJECT_CLAUDE/skills/$skill"
done
6b. Create missing global symlinks:
for skill in $MISSING_GLOBAL_SKILLS; do
ln -s "$ALMANAC_PATH/skills/$skill" "$GLOBAL_CLAUDE/skills/$skill"
done
6c. Remove broken symlinks:
# Project
for broken in $BROKEN_PROJECT_SKILLS; do
rm "$PROJECT_CLAUDE/skills/$broken"
done
# Global
for broken in $BROKEN_GLOBAL_SKILLS; do
rm "$GLOBAL_CLAUDE/skills/$broken"
done
6d. Remove stale almanac entries:
# Only remove items that target the almanac path but aren't in the registry
for stale in $STALE_GLOBAL_SKILLS; do
rm "$GLOBAL_CLAUDE/skills/$stale"
done
# Remove _template if present
rm -f "$GLOBAL_CLAUDE/skills/_template"
rm -f "$PROJECT_CLAUDE/skills/_template"
6e. Fix missing directory symlinks (agents/teams):
# Project agents
if [ "$PROJECT_AGENT_STATUS" = "MISSING" ]; then
ln -s ../agents "$PROJECT_CLAUDE/agents"
fi
# Project teams
if [ "$PROJECT_TEAM_STATUS" = "MISSING" ]; then
ln -s ../teams "$PROJECT_CLAUDE/teams"
fi
# Global agents
if [ "$GLOBAL_AGENT_STATUS" = "MISSING" ]; then
ln -s "$ALMANAC_PATH/agents" "$GLOBAL_CLAUDE/agents"
fi
# Global teams
if [ "$GLOBAL_TEAM_STATUS" = "MISSING" ]; then
ln -sf "$ALMANAC_PATH/teams" "$GLOBAL_CLAUDE/teams"
fi
Important: Never remove items classified as external. These belong to other projects (e.g., peon-ping) and must be preserved.
Got: All missing symlinks created, all broken symlinks removed, all stale almanac entries cleaned. External content untouched.
If fail: If ln -s fails due to an existing file/directory at the target path (e.g., empty directory instead of symlink), remove the blocker first with rmdir (for empty dirs) or flag for manual review (for non-empty dirs).
Step 7: Verify
Re-run the audit checks from Steps 3-4 to confirm repairs.
echo "=== Post-repair verification ==="
echo "Project skills: $(ls "$PROJECT_CLAUDE/skills/" 2>/dev/null | grep -v '^_template$' | wc -l)"
echo "Global skills (almanac): $(echo "$ALMANAC_GLOBAL_SKILLS" | wc -w)"
echo "Broken project: $(find "$PROJECT_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l)"
echo "Broken global: $(find "$GLOBAL_CLAUDE/skills/" -maxdepth 1 -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l)"
echo "Project agents: $PROJECT_AGENT_STATUS ($PROJECT_AGENT_COUNT .md files)"
echo "Global agents: $GLOBAL_AGENT_STATUS ($GLOBAL_AGENT_COUNT .md files)"
echo "Project teams: $PROJECT_TEAM_STATUS ($PROJECT_TEAM_COUNT .md files)"
echo "Global teams: $GLOBAL_TEAM_STATUS ($GLOBAL_TEAM_COUNT .md files)"
Got: Zero missing, zero broken. Counts match registered totals (for almanac content). External content listed separately.
If fail: If issues remain after repair, report the specific failures. Common causes: permission errors on ~/.claude/, NTFS path length limits on /mnt/ paths, or a non-empty directory blocking symlink creation.
Validation
- Almanac path correctly identified and contains all three registries
- Registry counts match
total_*header values (or discrepancy noted) - Project-level skills, agents, and teams audited
- Global-level skills, agents, and teams audited
- External content (non-almanac) identified and excluded from issue counts
-
_templateentries flagged as extraneous (never belongs in discovery paths) - Audit report generated with clear counts and actionable lists
- If
fix_modeisauto: all safe repairs applied, external content untouched - Post-repair verification confirms zero missing, zero broken
Pitfalls
-
Confusing external content with missing almanac content:
~/.claude/skills/may contain skills from other projects (e.g., peon-ping). Always check whether a symlink target is under the almanac path before classifying it as stale or extraneous. -
Removing external content: Never delete items that don't target the almanac. They belong to other projects and are intentional.
-
Symlinking
_templatedirectories: Templates are scaffolding, not consumable content. The_templatedirectory should never appear in.claude/skills/or.claude/agents/. Bulk sync scripts must explicitly skip it. -
Stale
.claude/teamssymlink: A.claude/teamssymlink pointing to team definitions is a misconfiguration. Claude Code'sTeamCreateuses~/.claude/teams/for runtime state (config.json, inboxes). If this path is a symlink to the almanac'steams/directory, runtime artifacts will be written into the git-tracked repository. Remove any.claude/teamssymlink found at project or global level. -
Relative vs absolute paths: Project-level skill symlinks use relative paths (
../../skills/<name>). Global symlinks use absolute paths (/path/to/almanac/skills/<name>). Mixing these patterns causes breakage on moves. -
Registry header vs actual count: The
total_skillsfield in the registry header may be stale if someone added entries without updating the count. Trust the actual- id:entries, not the header.
Related Skills
- repair-broken-references -- general broken link and reference repair
- tidy-project-structure -- project directory organization
- create-skill -- includes symlink creation for new skills (Step 13)
- create-agent -- includes discovery verification (Step 10)
- create-team -- team creation with registry integration
Dépôt GitHub
Compétences associées
content-collections
MétaCette compétence propose une configuration éprouvée en production pour Content Collections, un outil axé sur TypeScript qui transforme des fichiers Markdown/MDX en collections de données typées de manière sûre avec une validation Zod. Utilisez-la lors de la création de blogs, de sites de documentation ou d'applications Vite + React riches en contenu pour garantir la sécurité de typage et la validation automatique du contenu. Elle couvre tout, de la configuration du plugin Vite et de la compilation MDX à l'optimisation des déploiements et la validation des schémas.
polymarket
MétaCette compétence permet aux développeurs de créer des applications avec la plateforme de marchés prédictifs Polymarket, incluant l'intégration d'API pour le trading et les données de marché. Elle fournit également une diffusion de données en temps réel via WebSocket pour surveiller les transactions en direct et l'activité du marché. Utilisez-la pour mettre en œuvre des stratégies de trading ou pour créer des outils traitant les mises à jour de marché en direct.
creating-opencode-plugins
MétaCette compétence aide les développeurs à créer des plugins OpenCode qui s'interconnectent avec plus de 25 types d'événements tels que les commandes, les fichiers et les opérations LSP. Elle fournit la structure du plugin, les spécifications de l'API événementielle et les modèles d'implémentation pour les modules JavaScript/TypeScript. Utilisez-la lorsque vous avez besoin d'intercepter, de surveiller ou d'étendre le cycle de vie de l'assistant IA OpenCode avec une logique personnalisée pilotée par les événements.
sglang
MétaSGLang est un framework de service LLM haute performance spécialisé dans la génération rapide et structurée pour les workflows JSON, regex et agentiques grâce à son cache de préfixe RadixAttention. Il offre une inférence nettement plus rapide, particulièrement pour les tâches avec des préfixes répétés, ce qui le rend idéal pour les sorties complexes et structurées ainsi que les conversations multi-tours. Choisissez SGLang plutôt que des alternatives comme vLLM lorsque vous avez besoin d'un décodage contraint ou que vous construisez des applications avec un partage étendu de préfixes.
