Requirements for a Custom Job Portal Skill to Be Integrated in ai-job-search

A custom job portal skill must reside in /.agents/skills/<name>/ with a SKILL.md containing YAML front-matter, implement search and detail CLI commands via a Bun-based TypeScript executable, use only public APIs with rate-limiting, and declare zero runtime dependencies to be auto-discovered by the framework.

The ai-job-search repository provides a modular framework for aggregating job listings from multiple sources. To extend this system with support for additional job boards, developers must follow strict architectural conventions that ensure seamless integration with the orchestration layer. Understanding the requirements for a custom job portal skill to be integrated ensures your contribution is automatically detected and executed during scraping workflows.

Mandatory Directory Layout and Metadata

The framework discovers skills through a strict directory convention. Each skill must live under /.agents/skills/<skill-name>/ and contain a SKILL.md file alongside a cli/ subdirectory housing the implementation. As documented in AGENTS.md, this structure supports the thin-pointer design pattern used by the orchestration layer.

The SKILL.md file must begin with a YAML front-matter block declaring metadata fields that the framework reads during discovery:

  • name: Unique identifier for the skill
  • version: Semantic version string
  • description: Human-readable summary
  • context: Execution context (typically fork)
  • enabled: Boolean flag controlling automatic participation in /scrape workflows
  • allowed-tools: Command declaration specifying the executable path

Set enabled: true to have the skill automatically participate in the scraping workflow; set it to false to keep the code present but skip execution.

CLI Implementation Standards

The allowed-tools entry in SKILL.md must point to an executable Bash command that runs a TypeScript/Bun script. According to the reference implementation in /.agents/skills/linkedin-search/cli/src/cli.ts, the CLI must implement two mandatory sub-commands:

search: Fetches a paginated list of job postings. Accept options for location (-l, --location), query (-q, --query), limit (-n, --limit), and format (-f, --format).

detail: Retrieves the full description for a single posting using an identifier or URL argument (detail <idOrUrl>).

Both commands must support three output formats: json, table, and plain. Error handling must write to stderr using a structured JSON format: { "error": "...", "code": "…" }.

The implementation should use a CLI parser like commander or yargs and reside at cli/src/cli.ts within the skill directory.

Runtime and API Constraints

Zero Runtime Dependencies: The skill must run with plain bun (or Node) without requiring additional npm packages, as the framework loads it in a sandboxed environment. The LinkedIn skill declares "zero runtime dependencies" as a reference standard.

Public-API Usage Only: Skills may scrape only public endpoints that do not require authentication or API keys. Any required authentication must be documented as a personal-use-only warning. The reference implementation uses LinkedIn's jobs-guest endpoints.

Rate-Limit Friendliness: Implementations must respect the source site's rate limits using exponential back-off on 429 or 5xx responses. The LinkedIn skill demonstrates this pattern with retry logic that includes exponential back-off.

Documentation Structure Requirements

The SKILL.md file must contain specific documentation sections following the pattern established in /.agents/skills/linkedin-search/SKILL.md and /.agents/skills/jobbank-search/SKILL.md:

  • When to use this skill
  • Commands (with example invocations)
  • Usage examples
  • Output formats
  • Notes (including legal and Terms of Service considerations)

These sections ensure users understand the skill's capabilities and limitations before execution.

Implementation Skeleton

Below is a minimal implementation template that satisfies all framework requirements.

SKILL.md template:

---
name: myportal-search
version: 1.0.0
description: >
  Search live job listings from MyPortal's public job board. No authentication,
  zero runtime dependencies – runs with bun.
context: fork
enabled: true
allowed-tools: Bash(bun run .agents/skills/myportal-search/cli/src/cli.ts *)
---

When to use this skill
Use this skill to search the MyPortal public job board without authentication.

Commands
- `search`: Query job listings by location and keywords
- `detail`: Retrieve full job description by ID

Usage examples
Search for frontend positions:
Bash(bun run .agents/skills/myportal-search/cli/src/cli.ts search -q "frontend" -l "Copenhagen")

Output formats
Supports `json`, `table`, and `plain` output formats.

Notes
This skill uses public endpoints only. Respect rate limits and MyPortal's Terms of Service.

Minimal CLI implementation (cli/src/cli.ts):

#!/usr/bin/env bun
import { Command } from "commander";

const program = new Command();

program
  .name("myportal-search")
  .description("CLI wrapper for the MyPortal job-portal skill");

program
  .command("search")
  .requiredOption("-l, --location <text>", "Location string")
  .option("-q, --query <text>", "Keyword query")
  .option("-n, --limit <number>", "Maximum results", "10")
  .option("-f, --format <type>", "json|table|plain", "json")
  .action(async (opts) => {
    // Implement HTTP request to public endpoint
    console.log(JSON.stringify({ results: [] }));
  });

program
  .command("detail <idOrUrl>")
  .option("-f, --format <type>", "json|plain", "json")
  .action(async (idOrUrl, opts) => {
    // Fetch full job description
    console.log(JSON.stringify({ id: idOrUrl, description: "" }));
  });

program.parseAsync(process.argv);

Invocation examples:


# Search for frontend engineer positions in Copenhagen

bun run .agents/skills/myportal-search/cli/src/cli.ts search -q "frontend engineer" -l "Copenhagen, Denmark" --format table

# Retrieve job details

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

Summary

Integrating a custom job portal skill into the ai-job-search framework requires adherence to strict architectural conventions:

  • Place the skill in /.agents/skills/<skill-name>/ with SKILL.md and cli/ subdirectory
  • Include YAML front-matter in SKILL.md with name, version, enabled, and allowed-tools fields
  • Implement search and detail commands supporting json, table, and plain output formats
  • Ensure zero runtime dependencies and executable via bun without additional packages
  • Use only public APIs with rate-limiting and exponential back-off for 429/5xx responses
  • Document the skill with required sections including usage examples and legal notes

When all requirements are satisfied, the framework automatically discovers the skill via SKILL.md front-matter and can invoke the declared CLI during job-search workflows.

Frequently Asked Questions

What directory structure is required for a new skill?

The skill must reside under /.agents/skills/<skill-name>/ containing a SKILL.md file at the root and a cli/src/cli.ts TypeScript file for the implementation. This structure, as seen in /.agents/skills/linkedin-search/, allows the framework to locate and load the skill automatically during the discovery phase.

Which CLI commands must be implemented?

Every skill must implement two sub-commands: search for fetching paginated job listings and detail for retrieving full descriptions of specific postings. Both commands must accept a --format option supporting json, table, and plain outputs, and write errors to stderr as structured JSON containing error and code fields.

Can skills require external npm packages or authentication?

No, skills must declare zero runtime dependencies and run with plain bun or Node without additional packages. They must use only public endpoints that do not require API keys; any authentication must be documented as personal-use-only. This constraint ensures safe execution within the framework's sandboxed environment.

How does the framework discover new skills?

The orchestration layer scans /.agents/skills/ for directories containing a SKILL.md file with valid YAML front-matter. The name, enabled, and allowed-tools fields determine whether the skill is active and how it should be invoked during the /scrape workflow, as implemented in the repository's skill loader.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →