# How to Add a New Job Portal Skill to AI Job Search: Complete Developer Guide

> Learn to add a new job portal skill to AI Job Search. Discover how to integrate new job portals with this developer guide, requiring only a SKILL.md file and a cli folder.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-08-25

---

**AI Job Search discovers job-portal integrations as self-contained CLI skills under the `.agents/skills/` directory, requiring only a [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) metadata file and a `cli/` executable folder to automatically register with the framework.**

The AI Job Search repository (`MadsLorentzen/ai-job-search`) implements a modular skill system that treats each job portal integration as a standalone micro-CLI. When you add a new portal skill following the established contract, the `/scrape` workflow automatically detects and executes it without requiring modifications to core framework code. This article explains how to add a new job portal skill to AI Job Search using either the automated generator or manual implementation.

## Understanding the Job Portal Skill Contract

Every portal skill in AI Job Search must adhere to a strict directory structure and behavioral contract. The framework automatically loads any skill matching this layout under `.agents/skills/`.

A valid skill requires these components:

- **[`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md)** – Contains human-readable description, the `enabled:` flag, supported commands, and usage contract
- **`cli/`** – Directory containing the executable TypeScript/JavaScript source code and a minimal [`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json)
- **Search command** – The CLI must expose a `search` command that accepts `--query` and `--format` parameters
- **Optional detail command** – Additional `detail` command for fetching specific job postings
- **Offline tests** – A `bun test` suite that validates the contract without network calls

The CLI must return results in the unified format used by existing Danish portal skills (`jobbank-search`, `jobdanmark-search`, `jobindex-search`, `jobnet-search`). According to the source implemented in [`.claude/commands/add-portal.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.md), the framework inspects the `enabled:` flag in each [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) to determine whether to activate the skill during scraping operations.

## Method 1: Using the Built-in /add-portal Command

The recommended approach for adding a new job portal skill uses the interactive generator available in Claude Code.

### Step 1: Execute the Generator

Run the following command inside your Claude Code environment:

```bash
/add-portal

```

When prompted, provide the base URL of the job board you wish to integrate (for example, `https://examplejobs.com`).

### Step 2: Review the Scaffold

The command performs several automated operations as defined in [`.claude/commands/add-portal.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.md):

- Inspects the URL to infer search-query patterns and result page structure
- Creates the directory `.agents/skills/<name>-search/`
- Generates [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) with `enabled: true` and command definitions
- Scaffolds [`cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/package.json) with zero runtime dependencies
- Creates [`cli/index.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/index.ts) with a starter `search` implementation
- Adds [`__tests__/skill.test.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/__tests__/skill.test.ts) with offline validation tests
- Executes a live query to verify the generated CLI works

### Step 3: Install Dependencies

Navigate to the generated CLI directory and install development dependencies:

```bash
cd .agents/skills/examplejobs-search/cli
bun install

```

The generated skill contains **zero runtime dependencies**; `bun install` only pulls type definitions for development.

### Step 4: Verify the Skill

Run the test suite to confirm the skill meets the contract:

```bash
bun test

```

The tests must pass without performing real network I/O, as they validate the CLI argument parsing and output format compliance.

## Method 2: Creating a Skill Manually

For full control over the implementation, create the skill structure manually following the template used by existing integrations.

### Required Directory Structure

Create the following layout under `.agents/skills/`:

```

.agents/
└── skills/
    └── myportal-search/
        ├── SKILL.md
        └── cli/
            ├── package.json
            ├── index.ts
            └── __tests__/
                └── skill.test.ts

```

### Configuring SKILL.md

The [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) file must contain the `enabled:` flag and command documentation. Refer to the pattern used in `.agents/skills/<portal>-search/SKILL.md`:

```markdown
---
name: examplejobs-search
enabled: true
description: |
  Search integration for ExampleJobs.com (global tech job board).
commands:
  - search: |
      bun run cli/index.ts --query "<keywords>" --format json
  - detail: |
      bun run cli/index.ts --detail "<job-id>" --format json

```

### Implementing the CLI Interface

The [`cli/index.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/index.ts) file must implement the search contract. Here is a minimal implementation skeleton:

```typescript
#!/usr/bin/env bun
import { argv } from 'bun';

async function search(query: string, format: string) {
  // Implementation should fetch from the job portal API
  // and output in the unified JSON/table/plain format
  const results = await fetchJobResults(query);
  
  if (format === 'json') {
    console.log(JSON.stringify(results, null, 2));
  } else if (format === 'table') {
    console.table(results);
  } else {
    console.log(results.map(r => r.title).join('\n'));
  }
}

async function main() {
  const query = argv.find(arg => arg.startsWith('--query='))?.split('=')[1] ?? '';
  const format = argv.find(arg => arg.startsWith('--format='))?.split('=')[1] ?? 'json';
  
  if (!query) {
    console.error('Missing --query argument');
    process.exit(1);
  }
  
  await search(query, format);
}

main();

```

The CLI must support `--query "<keywords>"` and `--format json|table|plain` arguments to remain compatible with the `/scrape` workflow.

### Testing Requirements

The [`__tests__/skill.test.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/__tests__/skill.test.ts) file must validate the contract without network calls. Use mocking or stubbing to test argument parsing and output formatting:

```bash
bun test

```

As noted in the repository's README (lines 29-34), all tests must run offline to ensure CI/CD reliability.

## Security Considerations for New Skills

Before running a newly generated skill, inspect the [`cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/package.json) to verify:

- **No runtime dependencies** – The scaffold generates skills with zero runtime dependencies; inspect for unexpected additions
- **No postinstall scripts** – Ensure no lifecycle scripts execute during `bun install`
- **Network scope verification** – Confirm all network requests target only the intended job board domain

This security practice mirrors the guidance in the README section regarding borrowing portal skills from other forks.

## Summary

- AI Job Search automatically discovers skills in `.agents/skills/` that contain a valid [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) with `enabled: true`
- Use **`/add-portal`** for automated scaffolding or create the directory structure manually for custom implementations
- Each skill requires a `cli/` folder with a `search` command supporting `--query` and `--format` parameters
- Skills must output results in the unified JSON format used by existing Danish portal integrations
- Tests must run offline using `bun test` without performing actual network requests
- Verify [`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json) for security issues before executing new skills

## Frequently Asked Questions

### What file format does the SKILL.md use?

SKILL.md uses YAML frontmatter with a markdown body. The file must contain an `enabled:` boolean flag (set to `true` to activate the skill) and a `commands:` section listing the available CLI operations. The framework parses this metadata to determine which skills to load during the `/scrape` workflow.

### Can I add a job portal skill without using the /add-portal command?

Yes. You can manually create the directory structure under `.agents/skills/<portal-name>/` following the contract specifications. Ensure you include [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md), [`cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/package.json), [`cli/index.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/index.ts) with the search implementation, and offline tests. The `/scrape` command will automatically detect your skill as long as the `enabled:` flag is set to `true` in the metadata file.

### Why must skills have zero runtime dependencies?

The zero-dependency requirement ensures security and performance. According to the repository's security guidelines, skills should not rely on external packages that could introduce supply-chain vulnerabilities or execute postinstall scripts. The [`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json) should only contain `devDependencies` for type definitions, keeping the runtime footprint minimal and auditable.

### How do I test my new job portal skill without making real API calls?

Write unit tests in [`__tests__/skill.test.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/__tests__/skill.test.ts) that mock the fetch calls or network interfaces. Use the `bun test` runner to validate that your CLI correctly parses arguments (`--query` and `--format`) and produces output matching the expected schema. The test suite must pass entirely offline, validating the contract without hitting the actual job board's API.