关于 freehire-search
freehire-search 是通过 freehire.me 搜索科技、软件、数据和工程职位的 CLI 技能,面向求职者及检索职位的 Agent。它支持结构化筛选,并在搜索结果中提供完整职位描述。
主要功能
- 01搜索实时科技职位
- 02按地点、资历、技能和发布时间筛选
- 03在搜索结果中获取完整描述
- 04按 slug 查询职位
- 05输出 JSON、表格或纯文本
适用场景
- 01查找远程资深工程职位
- 02比较不同地区和国家的职位
- 03按 slug 查看已收藏或已关闭的职位
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ai-job-search-dev/ ai-job-search --skill freehire-search安装后在 Claude Code 中输入/freehire-search确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
url-reference.mdcli/package.jsonREADME.mdtsconfig.jsoncli/src/cli.tshelpers.tscli/src/commands/detail.tssearch.tscli/tests/cli-flag-validation.test.tscommands.test.tsdetail-flags.test.tshelpers.tsparsing.test.tsregion-value-warning.test.tsremote-mode.test.tsrequest-timeout.test.tsrequired-search-values.test.tsretry-backoff.test.ts详细介绍
freehire Search Skill
搜索 freehire.me 上的实时职位——这是一个开源 IT 招聘平台,会将多个国家和地区约 50 个 ATS 平台发布的职位信息标准化为统一格式。无需身份验证、API 密钥或任何运行时依赖——只需 bun 即可运行。可通过筛选标记(--region、--country)为每次查询选择市场,因此这项技能开箱即用,适用于任何市场。
这是本仓库 job-portal-skill 模式的一个不限定国家/地区的完整示例,类似于
linkedin-search。与抓取 HTML 的招聘门户不同,它查询 freehire 的公开 JSON API,因此返回的是结构化结果(技能、资历级别、地区筛选项),而非从标记语言中解析的内容。
⚠️ 范围:以科技类职位为主
freehire 的职位库已包含一些非科技类职位(它会抓取整家公司职业页面),但目前其分面筛选——技能、类别和资历级别字典——主要针对科技类职位优化,因此这项技能的触发范围限定为软件、数据、工程和科技类职位,这些领域的筛选效果较好。它也涵盖非科技类职位,但相关能力仍在完善中;目前请勿依赖这项技能来搜索一般(非技术类)职位。
ℹ️ 依赖托管服务(尽力而为,不提供 SLA)
这项技能依赖第三方托管服务 freehire.me。读取操作公开且无需身份验证——与 linkedin-search 一样,无需注册。
freehire.me 是一个个人项目,但仍在积极维护;服务按尽力而为的方式运行(不提供正式 SLA)。 如果 API 无法访问,CLI 会优雅地失败——以非零退出码退出并提供清晰的错误消息——因此服务中断只会导致此来源不可用,不会破坏周边工作流。
自托管/可更换基础 URL。 freehire 后端是一个独立的 MIT 许可仓库——strelov1/freehire(Go + PostgreSQL + Meilisearch)——可通过 Docker Compose 使用一条命令启动(make up → API 运行于 :8080,路径与 /api/v1/... 相同)。这项技能支持基础 URL 环境变量 FREEHIRE_API_URL(默认为 https://freehire.me),只需一行改动即可将其指向本地实例:
FREEHIRE_API_URL=http://localhost:8080 bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "go"
注意:启动 API 的成本很低,但要维护一个完整且持续更新的镜像(涵盖约 50 个平台上的数百万条职位信息)则需要大量资源——自托管者可以只抓取部分指定来源,或将环境变量指回托管 API。
何时使用这项技能
- 按关键词搜索指定地区/国家的科技类职位,或搜索远程职位——每条结果都会附带完整职位描述,无需再逐条查询
- 按资历级别、类别、技能或新近程度筛选(发布于 N 天内)
- 按 slug 查询一条 freehire 职位(包括已关闭的职位)
命令
搜索职位列表
bun run .agents/skills/freehire-search/cli/src/cli.ts search [-q "<keywords>"] [facet flags]
主要标记:
--query <text>/-q <text>— 关键词搜索(职位名称、技能、角色)。全文搜索;可选。 支持OR和title:(见下文“查询运算符”)。--jobage <days>— 搜索 N 天内发布的职位(映射到posted_within_days)。--page <n>— 从 1 开始计数的页码。默认为 1。--limit <n>/-n <n>— 每页结果数(API 限制)。默认为 25。--format json|table|plain— 默认为json。--description-format markdown|text|html— 指定每条结果的完整职位描述呈现格式。默认为markdown,可保留职位发布内容中的标题和要求列表。仅适用于json输出。
查询运算符。 -q 支持两个运算符;CLI 会原样传递文本,由 API 解析:
- 在备选项之间使用
OR,可返回匹配任一备选项的职位,例如:-q "golang OR rust"。仅大写形式有效:小写的or会被视为普通搜索词。以空格分隔的普通词语不会合并搜索(-q "golang rust"返回的职位少于单独使用-q golang),因此应使用OR搜索同义词和备选技术栈,而不是针对每个关键词分别搜索。最多可有 5 个备选项;第 6 个会被拒绝并报错。双引号内的OR会被视为字面文本。 - 在开头添加
title:,可将该备选项限制为职位标题,例如:-q "title:staff engineer"会跳过只在职位描述中提到这些词的招聘信息。该限制针对每个备选项分别生效,因此每个备选项都要重复添加:-q "title:staff engineer OR title:principal engineer"。只有位于备选项开头时才生效;中间的title:会作为文本搜索。其内部可以使用引号:-q 'title:"staff engineer"'会匹配该词组。
分类筛选标志、--jobage、--page 和 --limit 会应用于合并后的结果:所有备选项都使用相同的筛选条件,匹配多个备选项的职位只会出现一次。
搜索结果已包含完整职位描述。 此技能会查询 freehire 的 Agent 搜索端点,该端点会将索引中被截断的预览替换为每条招聘信息的完整文本,因此搜索 20 个职位只需 1 次请求,而不是 1 + 20 次。不要循环对搜索结果调用 detail 来读取职位描述——只有在需要按 slug 查询某条招聘信息时才使用 detail(例如,从跟踪器中查询,或查询一条已关闭、因此不会出现在搜索结果中的招聘信息)。完整职位描述篇幅较长:请适当降低 --limit,并在阅读正文前按职位标题/公司筛选;或者传入 --no-description,以较低开销进行初步搜索,保留其他所有字段但完全省略正文(可使用 detail 获取入围职位的正文,或不带该标志重新搜索)。
分类筛选(值来自 freehire 的受控词汇表;在同一分类筛选中用逗号分隔表示 OR):
--region <codes>- 大区:global、eu、uk、north_america、latam、apac、mena、africa、cis。--region eu,uk。eu不包括 UK;如需覆盖整个欧洲,请添加uk。单个国家请改用--country-us是国家代码,不是大区,--region us不会匹配任何结果。--region、--country和--city组成一个 OR 组,因此单独使用--country us就会将范围限定为美国。使用none匹配大区无法解析的职位(见下文“部分数据”)。--country <codes>— ISO-3166 alpha-2,例如--country DE,GB--city <names>— 城市名称,例如--city Berlin--seniority <levels>—junior、middle、senior、staff、principal、lead,……--category <cats>—backend、frontend、fullstack、devops、ml_ai、qa,……--skill <names>— 规范技能名称,例如--skill go,kubernetes--company <slug>— 公司 slug(取自搜索结果中的company_slug)--remote <mode>—remote|hybrid|onsite(work_mode分类筛选)。单独使用--remote表示remote。用逗号分隔表示 OR(remote,hybrid;on-site会规范化为onsite)。未知项目会以BAD_ARG退出,代码为 1。--facet <key=value>— 任何其他分类筛选参数(可重复使用),例如--facet salary_min=100000
地点是分类筛选项,不是自由文本。 与
linkedin-search的--location不同, freehire 通过结构化的--region/--country/--city分类筛选来筛选地理位置。可在/api/v1/jobs/facets查询某个市场的实时值 (附加?q=<role>可限定范围)——切勿自行编造分类筛选值。
获取完整职位详情
bun run .agents/skills/freehire-search/cli/src/cli.ts detail <slug|url> [--format json|plain]
slug 是 search 结果中的 id(例如 golang-zensar-2bxu6dxm)。你也可以传入完整的 https://freehire.me/jobs/<slug> URL。任何其他主机上的 URL(包括搜索结果 url 字段中的雇主申请链接)都会在发出任何请求前被拒绝,并返回 BAD_ID。返回完整的(已去除 HTML 的)描述、技能、地区/国家,以及在职位信息已补充完整时提供的资历级别、类别、雇佣类型和薪资。若 freehire 记录了薪资周期,salary 会保留该周期(INR 300000–300000/year、INR 12000/month);比较薪资时,请确保周期相同。
适用于你已经有 slug 的职位,例如已跟踪的申请、分享的链接,或搜索结果不再列出的已关闭职位。重新获取 search 刚刚返回的结果,只会再次读取你已经有的描述。
使用示例
# Senior backend roles, table view
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "backend engineer" --seniority senior --limit 10 --format table
# Go or Rust roles, one search (OR must be uppercase)
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "golang OR rust" --remote remote --format table
# Staff or principal titles only, not postings that mention them in passing
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "title:staff engineer OR title:principal engineer" --format table
# Remote React roles in the EU
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "react" --remote remote --region eu --format table
# DevOps roles in Germany posted in the last 14 days
bun run .agents/skills/freehire-search/cli/src/cli.ts search --category devops --country DE --jobage 14 --format table
# ML/AI roles anywhere, fully remote
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "machine learning" --category ml_ai --remote remote --format table
# Descriptions as plain text instead of Markdown
bun run .agents/skills/freehire-search/cli/src/cli.ts search -q "platform engineer" --limit 5 --description-format text
# Full details for a specific job
bun run .agents/skills/freehire-search/cli/src/cli.ts detail golang-zensar-2bxu6dxm --format plain
输出格式
| 格式 | 适用场景 |
|---|---|
json | 默认格式——供程序使用;这是唯一包含每条结果描述的格式 |
table | 快速浏览,便于阅读 |
plain | 阅读单个职位的完整详情(detail 命令) |
搜索 JSON 的结构为 { "meta": { "count", "page", "total" }, "results": [...] };每条结果至少包含 id(freehire 的 slug)、title、company、location、date、url 和 description(缺失值为 null)。table 和 plain 不包含描述——描述会让便于浏览的列表变得臃肿。所有错误都会以 { "error": "...", "code": "..." } 的形式写入 stderr,并以代码 1 退出进程。
有一类诊断信息不属于错误:如果 --region(或 --facet regions=)的值不在文档列出的词汇范围内(us 是国家代码,不是地区),程序会在结果之后将 { "warning": "...", "code": "REGION_VALUE_UNKNOWN" } 输出到 stderr,进程仍以 0 退出,且 stdout 是有效的 JSON 文档。
部分数据
筛选条件按职位生成,可能不完整,尤其是地理信息。
职位可能已解析出其 work_mode(例如 remote),但由于来源中的地点文本含糊不清,仍无法确定其地区或国家(freehire 的词典不会进行猜测)。因此:
- 缺少地区/国家表示“未解析”,不表示“不适用”——按
--region eu筛选会悄悄排除地区尚未解析的职位,即使它们实际上位于 EU。若需要找回这些职位,请放宽或移除该筛选条件。 - 对应未解析项设有专用筛选值:
--region none可匹配未解析出地区的职位——适合找出从未标明地理位置的远程职位。它可与实际地区进行 OR 匹配,例如--region eu,none。 - 出于同样原因,
result.regions/countries/cities可能为空数组;应将空数组视为未知,而不是“以上皆非”。
注意事项
- 数据来自 freehire.me 的公共 API——无需凭据。只有按用户跟踪(申请/保存)才需要密钥,而此技能会刻意避开这类操作:它仅支持搜索和详情。
- 搜索结果中的
id是 freehire 的public_slug——将其原样传给detail。 date是职位发布日期,格式为YYYY-MM-DD(即 API 的posted_at时间戳中的日期部分,所有招聘平台 CLI 都采用这种格式);未标注日期的职位可能为null。- 筛选值采用受控词汇表。筛选前,可使用
/api/v1/jobs/facets查看某个查询的实时取值(及其数量)。 - API 会通过指数退避重试 429/5xx;如果 API 无法访问,程序会以非零状态退出并显示清晰的消息(服务尽力提供,详见上文依赖项说明)。
search会调用/api/v1/agent/jobs/search(与其他接口一样公开);--no-description会调用较轻量的/api/v1/jobs/search,该接口不提供完整正文。低于该接口版本的可自托管实例会返回 404,CLI 会报告错误并指出对应接口——绝不会将其报告为空结果集。