# How /add-portal Scaffolds New Job Portal Search Skills for Different Markets

> Discover how the `/add-portal` command scaffolds job portal search skills for any market. This CLI generator creates production-ready TypeScript skills by interviewing users and validating against live data.

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

---

**The `/add-portal` command is a guided, country-agnostic CLI generator that creates production-ready portal search skills by interviewing the user, investigating the target site, and scaffolding a TypeScript CLI that validates against live data before registration.**

The `ai-job-search` repository provides a modular framework for automating job discovery across global markets. The `/add-portal` command, specified in [`.claude/commands/add-portal.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/add-portal.md), implements a rigorous seven-step workflow to scaffold new job portal search skills that integrate directly into the existing scraping pipeline. This approach ensures every skill adheres to a strict architectural contract and validates against real-world data before deployment.

## The Seven-Step Scaffold Workflow

The generator follows a strict, sequential process implemented in the command specification, with each step enforced by the repository's architecture.

### Step 0: Argument Parsing and Discovery

When you invoke `/add-portal`, the generator first checks for the `--list` flag or a supplied portal URL. If `--list` is provided, it globs `.agents/skills/*/SKILL.md` to enumerate existing skills (e.g., `jobindex-search`, `linkedin-search`). Otherwise, it initiates the interactive interview process.

### Step 1: Interview and Naming Conventions

The generator queries for the portal URL, desired skill name, target market/language, and a realistic test query. It enforces **kebab-case** naming with the mandatory `-search` suffix and validates against name collisions in `.agents/skills/` to prevent overwriting existing implementations.

### Step 2: Portal Investigation

The tool performs live HTTP fetches (or `curl` via Bash) to capture the portal's search endpoint, result fields, and detail-page patterns. It records these findings in [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md) and checks [`robots.txt`](https://github.com/MadsLorentzen/ai-job-search/blob/main/robots.txt) and terms of service compliance before proceeding.

### Step 3: Skill Scaffolding from Reference

Using `.agents/skills/linkedin-search/` as the canonical reference implementation, the generator creates a new directory at `.agents/skills/<name>/` containing:

- [`SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SKILL.md) — Description, trigger phrases, usage examples, and personal-use warnings
- [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md) — Documented endpoints and field selectors for future maintainers
- `cli/` — Complete TypeScript project with [`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 command implementations
- `tests/` — Smoke-test suite for validation

### Step 4: Mandatory Live Validation

Before the skill is considered complete, the generator installs dev dependencies, type-checks the code, and executes a real search using the user's test query via `bun run src/cli.ts search`. It validates the JSON output shape `{ meta, results }` and tests the `detail` command with `bun run src/cli.ts detail`. This ensures the skill only ships after proven functionality against live data.

### Step 5: Pipeline Registration

Optionally, the generator inserts the new portal into [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md), enabling the `/scrape` command to automatically include results from the new source without additional wiring.

### Step 6: Confirmation and Summary

The command prints a concise confirmation block showing the skill location, test results, and a one-line command to run the new skill manually.

## The Portal Skill Contract

Every generated skill adheres to a strict contract defined in the specification to ensure interoperability across the codebase:

- **Commands**: `search` and `detail <id|url>`
- **Flags**: `--query`/`-q`, `--jobage`, `--page`, `--limit`, `--format` (accepting `json`, `table`, or `plain`), and optional `--location`
- **Output Structure**: JSON containing `meta` and `results` arrays, with each result including `id`, `title`, `company`, `location`, `date`, and `url`
- **Error Handling**: Errors emitted on **stderr** as JSON with exit code 1
- **Dependencies**: Zero runtime dependencies by default, using only `bun` + `fetch` + regex parsing

## Practical Usage Examples

### List existing portal skills

```bash
/add-portal --list

```

This displays a table of installed skills by scanning `.agents/skills/*/SKILL.md`.

### Scaffold a skill for a foreign market

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

```

The generator will interactively request:
- **Skill name**: `seek-search` (enforced kebab-case with `-search` suffix)
- **Market/language**: Australia / English
- **Test query**: Software Engineer

### Run the generated skill manually

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

```

Fetch specific job details:

```bash
bun run .agents/skills/seek-search/cli/src/cli.ts detail <job-id> --format plain

```

### Register for automatic scraping

Add the 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):

```markdown
- https://www.seek.com.au/jobs?q=Software+Engineer

```

Once registered, `/scrape` automatically includes results from your new portal alongside existing sources.

## Summary

- `/add-portal` creates production-ready skills in `.agents/skills/<name>/` using the `linkedin-search` reference implementation as a template
- The workflow requires **mandatory live validation** (Step 4) before considering the skill complete, ensuring robust parsing logic
- Generated skills follow a strict contract with standardized `search` and `detail` commands and a consistent JSON output shape
- Registration in [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md) enables seamless integration with the `/scrape` command
- All markup assumptions are documented in [`url-reference.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/url-reference.md) for future maintenance and debugging

## Frequently Asked Questions

### What file structure does `/add-portal` create?

The generator scaffolds a directory under `.agents/skills/<name>/` containing [`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), a `cli/` directory with TypeScript source files ([`src/cli.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/src/cli.ts), helpers, and command modules), and a `tests/` directory with smoke tests. This structure mirrors the reference implementation at `.agents/skills/linkedin-search/`.

### Why does the generator require a live test query before completion?

Step 4 mandates live validation to ensure the skill correctly parses the portal's HTML structure and returns data in the expected JSON shape `{ meta, results }`. This prevents shipping broken scrapers by verifying the regex selectors and endpoint logic against real responses before the skill is considered production-ready.

### How does the skill integrate with the existing job scraper?

After scaffolding, you can optionally register the portal in [`.claude/skills/job-scraper/search-queries.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-scraper/search-queries.md). Once listed there, the `/scrape` command automatically includes results from your new portal skill alongside existing sources like `linkedin-search` or `jobindex-search` without requiring additional code changes.

### What are the naming requirements for new portal skills?

The generator enforces **kebab-case** naming with the `-search` suffix (e.g., `seek-search` or `indeed-search`). It checks `.agents/skills/` for name collisions to prevent overwriting existing implementations, ensuring each skill has a unique identity in the repository.