# How Job Portal Skill Auto-Discovery Works in `.agents/skills/`: A Technical Deep Dive

> Explore how AI Job Search auto-discovers job portal skills by scanning .agents/skills/*/SKILL.md. Integrate new portals effortlessly with this technical deep dive.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: deep-dive
- Published: 2026-08-05

---

**The AI-Job-Search framework automatically discovers job portal integrations by scanning `.agents/skills/*/SKILL.md` manifests at runtime, requiring zero registration code for new portals.**

The **MadsLorentzen/ai-job-search** repository implements a **plug-and-play skill architecture** that treats each job portal as a self-contained module. This design allows the `/scrape` command to dynamically discover and invoke portal-specific logic without hardcoding integrations or modifying core framework code.

## The Four-Step Discovery Mechanism

The auto-discovery system operates through a predictable pipeline that executes every time the scraper runs.

### Step 1: Directory Tree Scanning

The framework walks the `/.agents/skills/` directory tree, searching for [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) files in each sub-directory. According to the job-scraper skill documentation, this path pattern `.agents/skills/*/SKILL.md` is the canonical discovery target【/.claude/skills/job-scraper/SKILL.md†L61-L66】.

### Step 2: Manifest Parsing

Each [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) serves as a **declarative contract** specifying:

- **CLI commands**: `search` and `detail` subcommands
- **Supported flags**: query parameters, location filters, pagination options
- **Output formats**: JSON, CSV, or structured text
- **Enabled flag**: boolean `enabled:` key controlling activation

The manifest uses YAML syntax for human-readable configuration.

### Step 3: Runtime Registration

For every valid and enabled manifest, the scraper loads the corresponding CLI implementation—typically located in a `cli/` sub-folder within the same directory—and adds it to the available portal registry. As the README states: "`/scrape` auto-discovers any installed skill that follows the contract – nothing to register, nothing to wire up"【/README.md†L309】.

### Step 4: Dynamic Extension

New portals become immediately discoverable after creation. The `/add-portal` command scaffolds the required structure, and because discovery re-scans the directory on each run, no restart or rebuild is necessary.

## Practical Implementation Examples

### Running the Scraper

```bash

# Execute scrape across all discovered portal skills

$ ai-job-search /scrape --query "Data Scientist" --location "Berlin, Germany"

```

### Adding a New Portal

```bash

# Scaffold a new portal integration

$ ai-job-search /add-portal --name mylocalboard

# Generated structure:

# .agents/skills/mylocalboard/

# ├─ SKILL.md          ← manifest declares cli, flags, output format

# └─ cli/              ← implementation of search and detail commands

```

The skill becomes instantly available to `/scrape` without framework modifications.

### Core Discovery Logic (Simplified)

```python
import pathlib
import yaml

def discover_portal_skills(base_path: str = ".agents/skills") -> list[dict]:
    """
    Auto-discover all enabled job portal skills.
    
    Mirrors the actual implementation in MadsLorentzen/ai-job-search.
    """
    skills = []
    for skill_dir in pathlib.Path(base_path).glob("*/"):
        manifest_path = skill_dir / "SKILL.md"
        
        if not manifest_path.is_file():
            continue
            
        manifest = yaml.safe_load(manifest_path.read_text())
        
        # Skip disabled skills silently

        if not manifest.get("enabled", True):
            continue
            
        skills.append({
            "name": skill_dir.name,
            "cli_path": skill_dir / "cli",
            "manifest": manifest,
        })
        
    return skills

```

## Key Source Files and Their Roles

| File Path | Purpose |
|-----------|---------|
| [`/README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//README.md) | Documents the auto-discovery contract and `/scrape` behavior【/README.md†L309】 |
| [`/.claude/skills/job-scraper/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//.claude/skills/job-scraper/SKILL.md) | Specifies the exact discovery algorithm and path pattern【/.claude/skills/job-scraper/SKILL.md†L61-L66】 |
| `/.agents/skills/<portal>/SKILL.md` | Per-portal manifest declaring CLI interface and enabled status |
| `/.agents/skills/<portal>/cli/` | Executable implementation of portal-specific commands |
| [`/CONTRIBUTING.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main//CONTRIBUTING.md) | Notes that Codex and Antigravity skills are auto-discovered today【/CONTRIBUTING.md†L57】 |

## Design Benefits of Skill Auto-Discovery

This architecture delivers three core advantages:

- **Zero registration overhead**: New portals require no imports, no registry updates, no central configuration files
- **Isolation and safety**: Each skill operates in its own directory with explicit interface contracts
- **Runtime flexibility**: Enable, disable, or modify portals by changing files on disk—no redeployment needed

The `enabled:` flag in each [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) provides granular control without deleting skill code, supporting A/B testing and gradual rollouts.

## Summary

- **Discovery target**: `.agents/skills/*/SKILL.md` pattern with YAML manifests
- **Activation criteria**: File exists + `enabled: true` (defaults to true when omitted)
- **CLI location**: Co-located `cli/` directory inside each skill folder
- **Extension method**: `/add-portal` scaffold + automatic re-scan on next `/scrape`
- **Zero core changes**: Framework remains untouched when adding new job portals

## Frequently Asked Questions

### What happens if a SKILL.md is malformed or missing?

The discovery logic skips invalid entries gracefully. If [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) is absent or contains unparseable YAML, that directory is silently excluded from the skill registry. No errors propagate to halt the scraping process.

### Can multiple versions of the same portal skill coexist?

The directory-based naming convention enforces uniqueness at the folder level. Since `skill_dir.name` becomes the identifier, duplicate portal names would collide. Version management should occur within a single skill's implementation or through git-based deployment strategies.

### How does the framework distinguish between job-scraper skills and other skill types?

The `/.agents/skills/` path houses job-portal integrations specifically. The job-scraper skill itself resides under `/.claude/skills/` and orchestrates discovery. This separation allows other agent capabilities to use distinct discovery mechanisms without interference.

### Is there a performance cost to re-scanning on every `/scrape` invocation?

For typical skill counts (dozens of portals), directory traversal and YAML parsing incur negligible overhead. The CONTRIBUTING guide notes current production usage with Codex and Antigravity skills【/CONTRIBUTING.md†L57】, suggesting the design scales adequately for real-world workloads.