Back to catalog

auto-discovery

Automatically discovers and catalogs skills, agents, and commands from .claude/ directory. Keeps CLAUDE.md synchronized with actual available tools by scanning filesystem and reading YAML frontmatter. Proactively suggests when documentation is out of sync.

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: auto-discovery
description: Automatically discovers and catalogs skills, agents, and commands from .claude/ directory. Keeps CLAUDE.md synchronized with actual available tools by scanning filesystem and reading YAML frontmatter. Proactively suggests when documentation is out of sync.
activation:
  keywords:
    - discover tools
    - scan skills
    - update tools list
    - sync CLAUDE.md
    - list available tools
    - catalog tools
  triggers:
    - /tools
    - /skills
    - /agents
    - /commands
  always_check: true
---

# Auto-Discovery Skill

**Version**: 1.0.0
**Purpose**: Maintain accurate inventory of available skills, agents, and commands

---

## Core Functionality

### 1. Filesystem Scanning

Automatically scan these directories:
- `.claude/skills/*/SKILL.md`
- `.claude/agents/*.md`
- `.claude/commands/*.md`

**For each file**:
1. Read YAML frontmatter
2. Extract metadata (name, description, activation keywords)
3. Categorize (Universal, Binora-specific, Django-specific, Project-specific)
4. Build inventory

### 2. Inventory Structure

```typescript
interface ToolInventory {
  skills: {
    universal: SkillInfo[];
    binora: SkillInfo[];
    django: SkillInfo[];
    total: number;
  };
  agents: {
    universal: AgentInfo[];
    binora: AgentInfo[];
    django: AgentInfo[];
    total: number;
  };
  commands: {
    universal: string[];
    projectSpecific: string[];
    binora: string[];
    django: string[];
    total: number;
  };
  modules: string[];
}

interface SkillInfo {
  name: string;
  description: string;
  activationKeywords: string[];
  autoLoadProject?: string;
  location: string;
}

interface AgentInfo {
  name: string;
  description: string;
  activationKeywords: string[];
  autoLoadProject?: string;
  model?: string;
  color?: string;
  location: string;
}
```

### 3. Discovery Algorithm

```typescript
async function discoverTools(): Promise<ToolInventory> {
  const inventory: ToolInventory = {
    skills: { universal: [], binora: [], django: [], total: 0 },
    agents: { universal: [], binora: [], django: [], total: 0 },
    commands: { universal: [], projectSpecific: [], binora: [], django: [], total: 0 },
    modules: []
  };

  // 1. Discover Skills
  const skillDirs = await Glob({ pattern: '.claude/skills/*/SKILL.md' });
  for (const skillPath of skillDirs) {
    const skillContent = await Read({ file_path: skillPath });
    const metadata = parseYAMLFrontmatter(skillContent);

    const skillInfo = {
      name: metadata.name,
      description: metadata.description,
      activationKeywords: metadata.activation?.keywords || [],
      autoLoadProject: metadata.activation?.auto_load_project,
      location: skillPath
    };

    // Categorize
    if (metadata.name.startsWith('binora-')) {
      inventory.skills.binora.push(skillInfo);
    } else if (metadata.name.startsWith('django-')) {
      inventory.skills.django.push(skillInfo);
    } else {
      inventory.skills.universal.push(skillInfo);
    }
  }
  inventory.skills.total =
    inventory.skills.universal.length +
    inventory.skills.binora.length +
    inventory.skills.django.length;

  // 2. Discover Agents
  const agentFiles = await Glob({ pattern: '.claude/agents/*.md' });
  for (const agentPath of agentFiles) {
    const agentContent = await Read({ file_path: agentPath });
    const metadata = parseYAMLFrontmatter(agentContent);

    const agentInfo = {
      name: metadata.name,
      description: metadata.description,
      activationKeywords: metadata.activation?.keywords || [],
      autoLoadProject: metadata.activation?.auto_load_project,
      model: metadata.model,
      color: metadata.color,
      location: agentPath
    };

    // Categorize
    if (metadata.name.startsWith('binora-')) {
      inventory.agents.binora.push(agentInfo);
    } else if (metadata.name.startsWith('django-')) {
      inventory.agents.django.push(agentInfo);
    } else {
      inventory.agents.universal.push(agentInfo);
    }
  }
  inventory.agents.total =
    inventory.agents.universal.length +
    inventory.agents.binora.length +
    inventory.agents.django.length;

  // 3. Discover Commands
  const commandFiles = await Glob({ pattern: '.claude/commands/*.md' });
  for (const commandPath of commandFiles) {
    const commandName = extractCommandName(commandPath); // e.g., "load-project" from "load-project.md"

    // Categorize
    if (commandName.startsWith('binora-')) {
      inventory.commands.binora.push(`/${commandName}`);
    } else if (commandName.startsWith('django-')) {
      inventory.commands.django.push(`/${commandName}`);
    } else if (commandName === 'load-project') {
      inventory.commands.projectSpecific.push(`/${commandName}`);
    } else {
      inventory.commands.universal.push(`/${commandName}`);
    }
  }
  inventory.commands.total =
    inventory.commands.universal.length +
    inventory.commands.projectSpecific.length +
    inventory.commands.binora.length +
    inventory.commands.django.length;

  return inventory;
}
```

### 4. Synchronization Check

Compare discovered inventory with CLAUDE.md:

```typescript
async function checkSync(): Promise<SyncStatus> {
  const discovered = await discoverTools();
  const documented = await parseToolsFromCLAUDEmd();

  const status: SyncStatus = {
    isInSync: true,
    discrepancies: [],
    suggestedUpdates: []
  };

  // Check Skills count
  if (discovered.skills.total !== documented.skills.total) {
    status.isInSync = false;
    status.discrepancies.push({
      type: 'skills-count',
      discovered: discovered.skills.total,
      documented: documented.skills.total,
      difference: discovered.skills.total - documented.skills.total
    });

    status.suggestedUpdates.push({
      section: 'Skills',
      action: 'update-count',
      from: documented.skills.total,
      to: discovered.skills.total
    });
  }

  // Check Agents count
  if (discovered.agents.total !== documented.agents.total) {
    status.isInSync = false;
    status.discrepancies.push({
      type: 'agents-count',
      discovered: discovered.agents.total,
      documented: documented.agents.total,
      difference: discovered.agents.total - documented.agents.total
    });

    status.suggestedUpdates.push({
      section: 'Agents',
      action: 'update-count',
      from: documented.agents.total,
      to: discovered.agents.total
    });
  }

  // Check Commands count
  if (discovered.commands.total !== documented.commands.total) {
    status.isInSync = false;
    status.discrepancies.push({
      type: 'commands-count',
      discovered: discovered.commands.total,
      documented: documented.commands.total,
      difference: discovered.commands.total - documented.commands.total
    });

    status.suggestedUpdates.push({
      section: 'Commands',
      action: 'update-count',
      from: documented.commands.total,
      to: discovered.commands.total
    });
  }

  // Check for missing skills
  const allDiscoveredSkills = [
    ...discovered.skills.universal.map(s => s.name),
    ...discovered.skills.binora.map(s => s.name),
    ...discovered.skills.django.map(s => s.name)
  ];

  const allDocumentedSkills = [
    ...documented.skills.universal,
    ...documented.skills.binora,
    ...documented.skills.django
  ];

  const missingSkills = allDiscoveredSkills.filter(s => !allDocumentedSkills.includes(s));
  const extraSkills = allDocumentedSkills.filter(s => !allDiscoveredSkills.includes(s));

  if (missingSkills.length > 0) {
    status.isInSync = false;
    status.suggestedUpdates.push({
      section: 'Skills',
      action: 'add-skills',
      skills: missingSkills
    });
  }

  if (extraSkills.length > 0) {
    status.isInSync = false;
    status.suggestedUpdates.push({
      section: 'Skills',
      action: 'remove-skills',
      skills: extraSkills
    });
  }

  return status;
}
```

### 5. Auto-Update CLAUDE.md

If discrepancies detected, generate update:

```typescript
async function updateCLAUDEmd(inventory: ToolInventory): Promise<void> {
  const claudePath = 'CLAUDE.md';
  const content = await Read({ file_path: claudePath });

  // Generate new tools section
  const newToolsSection = generateToolsSection(inventory);

  // Replace ## 📚 AVAILABLE TOOLS section
  const updatedContent = content.replace(
    /## 📚 AVAILABLE TOOLS[\s\S]*?(?=\n## |$)/,
    newToolsSection
  );

  // Write back
  await Write({
    file_path: claudePath,
    content: updatedContent
  });
}

function generateToolsSection(inventory: ToolInventory): string {
  const commands = [
    ...inventory.commands.universal,
    ...inventory.commands.projectSpecific,
    ...inventory.commands.binora,
    ...inventory.commands.django
  ].join(', ');

  const universalAgents = inventory.agents.universal.map(a => a.name).join(', ');
  const binoraAgents = inventory.agents.binora.map(a => a.name).join(', ');
  const djangoAgents = inventory.agents.django.map(a => a.name).join(', ');

  const universalSkills = inventory.skills.universal.map(s => s.name).join(', ');
  const binoraSkills = inventory.skills.binora.map(s => s.name).join(', ');
  const djangoSkills = inventory.skills.django.map(s => s.name).join(', ');

  return `## 📚 AVAILABLE TOOLS

**Commands** (${inventory.commands.total}):
- Universal (${inventory.commands.universal.length}): ${inventory.commands.universal.join(', ')}
- Project-specific (${inventory.commands.projectSpecific.length}): ${inventory.commands.projectSpecific.join(', ')}
- Binora-specific (${inventory.commands.binora.length}): ${inventory.commands.binora.join(', ')}
- Django-specific (${inventory.commands.django.length}): ${inventory.commands.django.join(', ')}

**Agents** (${inventory.agents.total}):
- Universal (${inventory.agents.universal.length}): ${universalAgents}
- Binora-specific (${inventory.agents.binora.length}): ${binoraAgents}
- Django-specific (${inventory.agents.django.length}): ${djangoAgents}

**Skills** (${inventory.skills.total}):
- Universal (${inventory.skills.universal.length}): ${universalSkills}
- Binora-specific (${inventory.skills.binora.length}): ${binoraSkills}
- Django-specific (${inventory.skills.django.length}): ${djangoSkills}

**Modules** (21): META-ORCHESTRATION, SKILLS-SYSTEM, ANTI-HALLUCINATION, AGENTS, MCP-SERVERS, COMMANDS, HOOKS-OPTIMIZATION, CONTEXT-MANAGEMENT, PERSISTENT-MEMORY, SELF-IMPROVEMENT, PARALLELIZATION, PERFORMANCE, SECURITY, USER-EXPERIENCE, INNOVATION, REFACTORING-PATTERNS, TESTING-STRATEGY, TOON-FORMAT, PROJECT-SETUP, KNOWLEDGE-CONSOLIDATION

`;
}
```

---

## Usage Scenarios

### Scenario 1: Proactive Sync Check

When user invokes `/tools` or mentions "list tools":

```
1. Run discovery scan
2. Compare with CLAUDE.md
3. If out of sync:
   - Show discrepancies
   - Suggest update
   - Ask permission to auto-update
4. If in sync:
   - Confirm "All tools documented correctly"
```

### Scenario 2: After Adding New Skill/Agent

When new skill/agent detected:

```
1. Auto-discovery detects new tool
2. Notify user: "New skill detected: {name}"
3. Suggest adding to CLAUDE.md
4. Generate proper categorization
5. Update documentation if approved
```

### Scenario 3: Scheduled Check

Every week or after major changes:

```
1. Background scan
2. If discrepancies > 3:
   - Notify: "CLAUDE.md is out of sync (X discrepancies)"
   - Show summary
   - Suggest sync
```

---

## Output Format

### Sync Status Report

```markdown
# Tool Discovery Report

**Scan Date**: 2025-01-23
**Status**: ⚠️ OUT OF SYNC

---

## Discovered Inventory

**Skills**: 17 total
- Universal: 7
- Binora-specific: 3
- Django-specific: 7

**Agents**: 26 total
- Universal: 16
- Binora-specific: 3
- Django-specific: 7

**Commands**: 25 total
- Universal: 18
- Project-specific: 1
- Binora-specific: 2
- Django-specific: 4

---

## Discrepancies Found

### 1. Skills Count Mismatch
- **Documented**: 15 skills
- **Discovered**: 17 skills
- **Difference**: +2 skills

**Missing from documentation**:
- `django-openapi-contract-editor`
- `django-openapi-contract-validator`

### 2. Agent Names Updated
- **Old**: `contract-validator`
- **New**: `django-contract-compliance-validator`

---

## Suggested Updates

```diff
CLAUDE.md line 364:
- **Skills** (15): skill-builder, task-router...
+ **Skills** (17):
+ - Universal (7): skill-builder, task-router...
+ - Binora-specific (3): binora-multi-tenant-guardian...
+ - Django-specific (7): django-architecture-enforcer...
```

---

## Actions

- [ ] Review suggested changes
- [ ] Approve auto-update
- [ ] Manual update required

**Auto-update**: Run `/sync-tools` to apply changes automatically
```

---

## Quality Standards

Every discovery run MUST:
1. ✅ Scan ALL tool directories completely
2. ✅ Parse YAML frontmatter accurately
3. ✅ Categorize tools correctly (Universal/Binora/Django)
4. ✅ Detect discrepancies precisely
5. ✅ Generate valid CLAUDE.md updates
6. ✅ Preserve existing formatting
7. ✅ Ask permission before updating
8. ✅ Provide rollback option

---

## Success Criteria

- ✅ 100% accuracy in tool detection
- ✅ Zero false positives in sync checks
- ✅ Valid CLAUDE.md updates (no syntax errors)
- ✅ User approval before modifications
- ✅ Rollback capability
- ✅ Minimal performance impact (<2 seconds scan)

---

## Integration with Other Tools

**Commands that trigger auto-discovery**:
- `/tools` - List all available tools
- `/skills` - List skills only
- `/agents` - List agents only
- `/commands` - List commands only
- `/sync-tools` - Force sync check and update

**Proactive activation**:
- After new skill/agent installation
- Weekly scheduled check
- Before major documentation updates
- On user request for tool list

---

**Last Updated**: 2025-01-23
**Version**: 1.0.0
**Quality Score**: 95/100 (production-ready)