# How to Use the /add-portal Command to Add a New Job Portal to AI Job Search

> Learn to use the /add-portal command in MadsLorentzen/ai-job-search. This command easily adds new job portals to your AI job search, scaffolding a TypeScript CLI and registering it for the /scrape workflow.

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

---

**The `/add-portal` command is a guided wizard that turns any public job board into a fully-featured search skill by scaffolding a TypeScript CLI, validating it with live tests, and registering it for the `/scrape` workflow.**

The `MadsLorentzen/ai-job-search` repository provides a Claude-hosted command system for automating job search workflows. The `/add-portal` command, defined in [`.claude/commands/add-portal.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.md), implements a six-step pipeline that transforms raw job board URLs into maintainable, version-controlled search skills compatible with the repository's job scraping architecture.

## Understanding the /add-portal Architecture

The command follows a strict six-step pipeline designed to ensure every generated skill meets the *portal-skill contract*—a standard requiring specific commands, JSON output shapes, and zero-runtime-dependency policies.

### Step 1: Argument Parsing and Initial Routing

When you invoke the command, it first detects execution context. Supplying `--list` displays all installed portal skills, while providing a URL immediately initiates the wizard with that endpoint. If invoked without arguments, the system enters interactive mode to collect the portal URL and other required metadata.

### Step 2: Interactive Interview for Portal Metadata

The wizard conducts an interview to gather essential scaffolding data:

- **Portal URL**: The base address of the job board
- **Skill name**: A kebab-case identifier ending in `-search` (e.g., `seek-search`)
- **Market and language**: Geographic region and localization settings
- **Test query**: A realistic search term used for validation (e.g., "Software Engineer")

This metadata seeds the directory structure and configuration files created in subsequent steps.

### Step 3: Portal Investigation with WebFetch

The system performs automated reconnaissance on the target job board using **WebFetch** or `curl` via Bash scripts. This investigation phase retrieves:

- Search endpoint patterns and query parameters
- Result field mappings (HTML selectors or API responses)
- Detail-page URL structures
- [`robots.txt`](https://github.com/MadsLorentzen/ai-job-search/blob/main/robots.txt) compliance checks and terms-of-service validation

When restrictive terms are detected, the system enforces a personal-use-only warning before proceeding. All technical findings are recorded for reference during scaffolding.

### Step 4: Scaffold the Skill from the linkedin-search Template

The command clones the canonical structure from `.agents/skills/linkedin-search/`—the reference implementation—and creates a new directory at `.agents/skills/<skill-name>/`. This scaffold includes:

- [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md): Complete definition, trigger phrases, command reference, and usage examples
- [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md): Technical documentation derived from the investigation phase
- `cli/`: A minimal TypeScript CLI containing [`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json), [`tsconfig.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tsconfig.json), [`src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/src/cli.ts), helper modules, and a test suite

The generated code implements the mandatory `search` and `detail` commands with standardized flags (`--limit`, `--format`) and JSON output shapes containing required fields: `id`, `title`, `company`, `location`, `date`, and `url`.

### Step 5: Live Testing and Type Validation

Before registration, the command executes a mandatory live test using your provided query. The validation pipeline:

1. Runs a real search and verifies the response contains all required JSON fields
2. Executes a `detail` fetch on one result to confirm deep-linking works
3. Type-checks the TypeScript source with `bun run typecheck`
4. Executes the test suite via `bun run test`

The wizard blocks progression until all tests pass, ensuring the skill is production-ready before integration.

### Step 6: Registration with the /scrape Workflow

Upon successful validation, the command optionally registers the new portal by adding its domain to [`.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 enables the `/scrape` command to include your new portal in multi-source job searches. The system reminds you to commit all generated files, though no additional wiring is required—the [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) description auto-triggers the skill when relevant queries are detected.

## Command Usage Examples

List all currently installed portal skills:

```bash
/add-portal --list

```

Start the wizard with a specific job board URL:

```bash
/add-portal https://www.seek.com.au

```

Enter interactive mode to manually specify parameters:

```bash
/add-portal

# → Prompt: Portal URL?

# → Prompt: Skill name? (e.g. seek-search)

# → Prompt: Market & language? (Australia, English)

# → Prompt: Test query? (Software Engineer)

```

After generation, test the new skill manually:

```bash

# Execute a search

bun run .agents/skills/seek-search/cli/src/cli.ts search -q "Software Engineer" --limit 5 --format table

# Fetch specific posting details

bun run .agents/skills/seek-search/cli/src/cli.ts detail 123456 --format plain

```

Run the generated test suite to verify compliance:

```bash
cd .agents/skills/seek-search/cli && bun install && bun run test

```

## Generated Skill Structure

Each new portal skill creates a version-controlled directory structure under `.agents/skills/<skill-name>/`:

| File | Purpose |
|------|---------|
| [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) | Auto-triggers the skill; contains trigger phrases and API documentation |
| [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md) | Technical reference for endpoints and selectors |
| [`cli/src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/src/cli.ts) | Main TypeScript entry point implementing `search` and `detail` commands |
| [`cli/package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/cli/package.json) | Bun-based runtime configuration with zero external dependencies |
| `cli/tests/` | Unit and integration tests validating the portal contract |

## Summary

- The `/add-portal` command provides a **six-step wizard** that converts job board URLs into TypeScript search skills
- It enforces a **strict portal-skill contract** requiring standardized commands, JSON output fields, and error handling
- Skills scaffold from the **canonical `linkedin-search` template** located in `.agents/skills/linkedin-search/`
- **Mandatory live testing** validates that results contain `id`, `title`, `company`, `location`, `date`, and `url` fields
- Generated skills auto-integrate with the `/scrape` workflow when registered in [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md)
- All artifacts are **country-agnostic** and remain in your fork per upstream policy

## Frequently Asked Questions

### What job portals are compatible with the /add-portal command?

Any public job board with accessible search endpoints works with the command. The wizard uses WebFetch or `curl` to investigate HTML-based portals and APIs, automatically extracting query parameters and result selectors. The system is country-agnostic and works for regional boards (like Seek in Australia) or global platforms, provided they don't block automated access in [`robots.txt`](https://github.com/MadsLorentzen/ai-job-search/blob/main/robots.txt).

### Why does the skill name need to end with "-search"?

The kebab-case `-search` suffix identifies the directory as a search-capable skill within the `.agents/skills/` namespace. This naming convention allows the Claude system to distinguish portal skills from other agent capabilities and ensures consistency with the `linkedin-search` reference implementation used for scaffolding.

### How does the command validate that a new portal skill works correctly?

The validation pipeline performs a **mandatory live test** using your provided query. It executes a real search, verifies the response JSON contains all required fields (`id`, `title`, `company`, `location`, `date`, `url`), fetches details for one result, runs TypeScript type checking with `bun run typecheck`, and executes the full test suite with `bun run test`. The wizard blocks completion until all validations pass.

### Where are the generated skill files stored?

The command creates a new directory at `.agents/skills/<skill-name>/` relative to the repository root. This directory contains [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md), [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md), and a `cli/` subdirectory with TypeScript source code, configuration files, and tests. Optionally, the portal domain is appended to [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md) to enable `/scrape` integration.