# How to Create a New Job Portal Search Skill for Non‑Danish Boards Using `/add-portal`

> Easily create custom job portal search skills for non Danish boards using the /add-portal command. Scaffold a new skill in minutes from any public job board URL with zero dependencies.

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

---

**Use the `/add-portal` command-line generator to scaffold a complete job-portal search skill from any public job board URL in under five minutes, following the zero-dependency contract established in the MadsLorentzen/ai-job-search repository.**

The MadsLorentzen/ai-job-search repository provides a command-line generator called `/add-portal` that transforms any public job board—such as Seek, StepStone, or Indeed—into a fully-featured search skill compatible with the AI job search workflow. This tool is specifically designed for non-Danish boards, following a strict seven-step interview and scaffolding process that ensures consistency with existing skills like `linkedin-search`.

## The Seven-Step `/add-portal` Workflow

The generator, implemented in [`.claude/commands/add-portal.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.ts) and specified in [`.claude/commands/add-portal.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.md), enforces a repeatable process from URL to working CLI.

### Step 1: Parse Arguments

The command accepts an optional `--list` flag to display existing portal skills or a portal URL to skip the initial prompt.

```bash

# List existing skills

bun run .claude/commands/add-portal.ts --list

# Start with a specific URL

bun run .claude/commands/add-portal.ts https://www.seek.com.au

```

If no URL is provided, the generator starts an interactive interview.

### Step 2: Interview for Portal Basics

The generator prompts for four critical pieces of information:

- **Portal URL**: The base domain of the job board (e.g., `https://www.stepstone.de`)
- **Skill name**: A kebab-case identifier (e.g., `seek-search`, `stepstone-search`)
- **Market & language**: The geographic region and language code (e.g., "Australia – English")
- **Test query**: A realistic search term for live validation (e.g., "Data Engineer")

### Step 3: Investigate the Portal Structure

The tool analyzes the target site to determine:
- Search URL patterns and query parameters
- HTML result fields and pagination logic
- Detail-page URL construction
- Access rules (robots.txt, rate limits, API availability)

This investigation is documented in [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md) within the generated skill folder.

### Step 4: Scaffold the Skill Files

The generator copies the canonical structure from the reference skill at `.agents/skills/linkedin-search/` to create:

```

.agents/skills/<skill-name>/
├── SKILL.md                 # Trigger phrases and description

├── url-reference.md         # Endpoint documentation from Step 3

└── cli/
    ├── package.json
    ├── tsconfig.json
    ├── README.md
    ├── src/
    │   ├── cli.ts           # Argument parsing and help

    │   ├── helpers.ts       # Fetch logic, backoff, and parsers

    │   └── commands/
    │       ├── search.ts    # Main search implementation

    │       └── detail.ts    # Job detail fetcher

    └── tests/
        └── helpers.ts       # CLI test utilities

```

### Step 5: Enforce the Portal-Skill Contract

Every generated skill must satisfy the **portal-skill contract** defined in the source specification:

- **Zero-runtime-dependency policy**: Only `bun`/`node` standard library; no external npm packages in production code
- **Standardized search flags**: `-q` (query), `--limit`, `--format` (json|table|plain)
- **JSON output shape**: Consistent field names (`id`, `title`, `company`, `location`, `url`, `posted_at`)
- **Error handling**: Graceful degradation with exit codes 0 (success) and 1 (failure)

### Step 6: Test-Run a Live Query

Before registration, you must validate the skill against the real site:

```bash
cd .agents/skills/<skill-name>/cli

# Install dev dependencies (types only)

bun install

# Type check the TypeScript

bun run typecheck

# Execute live search

bun run src/cli.ts search -q "Data Engineer" --limit 5 --format table

# Fetch a specific job detail

bun run src/cli.ts detail <job-id> --format plain

# Run unit tests

bun run test

```

### Step 7: Register the Skill

Optionally, the generator can append the new portal to the global scrape strategy in [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md). This registration allows the `/scrape` command to include your new board in multi-site search workflows.

## Reference Implementation Structure

When examining the generated `.agents/skills/<skill-name>/cli/src/` directory, you will find:

- **[`cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli.ts)**: Handles argument parsing using `process.argv` and routes to `search` or `detail` commands
- **[`helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/helpers.ts)**: Contains `fetchWithBackoff()` for HTTP requests and HTML parsing utilities using standard `Bun` APIs
- **[`commands/search.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/commands/search.ts)**: Implements the search logic, normalizing the portal's HTML structure into the required JSON contract
- **[`commands/detail.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/commands/detail.ts)**: Fetches individual job pages and extracts full descriptions

This structure mirrors the canonical `linkedin-search` skill, ensuring that all skills behave identically despite targeting different job boards.

## Adding Non-Danish Boards to Global Search

To integrate your new portal into the assistant's global search capabilities, the generator updates [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md) with site-specific query templates. This file contains the site-specific search strategies used by the `/scrape` command when running multi-portal job searches.

If you decline automatic registration during the interview phase, you can manually add an entry following the markdown format established in that file, specifying the skill name and base URL pattern.

## Summary

- **Use `/add-portal`** to scaffold job-portal skills for any non-Danish public job board through a guided seven-step process
- **Follow the zero-dependency contract** by keeping runtime code free of external npm packages and adhering to standardized JSON output shapes
- **Test live queries** before registration using `bun run src/cli.ts search` with `--format table` for immediate visual validation
- **Register globally** by allowing the generator to append your portal to [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md)
- **Reference `linkedin-search`** in `.agents/skills/` as the canonical example of a compliant portal skill implementation

## Frequently Asked Questions

### What job boards work with `/add-portal`?

Any publicly accessible job board that does not require authentication for search results works with `/add-portal`. The generator specifically targets non-Danish boards such as Seek (Australia), StepStone (Germany), or Indeed (global), but the scaffold works for any site with crawlable HTML job listings.

### Why must generated skills have zero runtime dependencies?

The **zero-runtime-dependency policy** ensures that every portal skill remains lightweight and secure, relying only on the `bun` JavaScript runtime standard library. This design prevents dependency rot, reduces security vulnerabilities, and guarantees that skills continue functioning months after creation without maintenance updates for third-party packages.

### How do I update a skill after the portal changes its HTML structure?

Navigate to `.agents/skills/<skill-name>/cli/src/helpers.ts` and modify the CSS selectors or parsing logic in the `extractJobs()` function. After editing, rerun `bun run typecheck` and execute the live test query (`bun run src/cli.ts search -q "test"`) to verify the fix before committing changes to your fork.

### Can I use `/add-portal` for private APIs or authenticated job boards?

No. The `/add-portal` generator and the resulting skills are designed for **public HTML scraping** only. The scaffold does not include authentication handlers, API key management, or OAuth flows. For authenticated sources, you would need to manually extend the generated [`helpers.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/helpers.ts) with authentication logic, though this violates the zero-dependency contract maintained by the repository.