job-hunt について
求職活動を管理する Claude スキルです。履歴書の取り込み、求人収集、分析、求人別の調整から shortlist の作成までを扱い、段階実行や状態確認、クリーンアップにも対応します。
主な機能
- 01履歴書と求人情報の取り込み
- 02履歴書の品質と求人適合度の分析
- 03応募先に合わせた履歴書の調整
- 04HTML shortlist ビューの生成
- 05段階実行と状態確認
活用シーン
- 01求職開始前の履歴書評価
- 02スクリーンショットや JSON ファイルからの求人取り込み
- 03求人票に合わせた履歴書の比較と調整
- 04HTML ビューで shortlist を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jpcwhj/ job-hunt --skill job-huntインストール後、Claude Code で/job-huntを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
build_html.pybuild-font-subset.shimport_jobs.pyvendor/marked.min.js詳細
job-hunt 求职猎手
⚡ 全流程自动执行约束(最高优先级,全程有效)
仅适用于 /job-hunt 无参数的全流程模式。
用户提供简历 + 截图确认后,剩余所有步骤全部自动连续执行,直到输出 shortlist。
- Step 3 fetcher 返回 → Step 7 之间:严禁输出任何文字。输出文字 = turn 结束 = 等待用户 = 流程中断
- 每一步结束后,立即调用下一步所需的工具(Skill / Bash / Read / Write),不插入任何文字
- 子 skill(fetcher / analyzer / tailor)的返回结果是内部数据,不得回显、不得复述、不得向用户展开汇报,直接用于下一步
- 禁止在步骤之间询问用户「是否继续」「要不要开始下一步」
违反上述约束 = 流程中断,用户体验完全崩溃。
子命令路由
根据用户输入判断执行路径:
| 用户输入 | 执行 |
|---|---|
/job-hunt 无参数 | 全流程(Step 1→7) |
/job-hunt fetch | 只执行截图导入(Step 1+2+3) |
/job-hunt analyze | 只执行分析(Step 1+4) |
/job-hunt tailor | 执行排序(Step 5)+ 定制(Step 6) |
/job-hunt status | 输出当前 run 状态(Step 7b) |
/job-hunt clean | 强制清理所有缓存和产物(Step 8) |
Step 1:确定工作目录与初始化
work_dir = Claude 启动时所在的当前目录(./ 的绝对路径)。
用 Bash 获取:pwd,将结果作为 work_dir。
data_dir = <data_dir>/jobHuntSkillData(所有数据文件统一放在这一层目录下,不直接写入 work_dir)。
确保以下目录存在(用 Bash mkdir -p 创建):
<data_dir>/.work/jd-pool/
<data_dir>/output/
生成 run_id(格式:YYYY-MM-DD-HHMM,使用当前本地时间)。
创建目录 <data_dir>/output/<run_id>/。
初始化 <data_dir>/output/<run_id>/state.json:
{
"run_id": "<run_id>",
"phase": "init",
"stages": {
"fetched": [],
"analyzed": [],
"analysis_errors": [],
"tailored": []
},
"last_error": null,
"checkpoint_at": "<当前 ISO 8601 时间>"
}
后续所有步骤中凡涉及路径的地方,一律使用 data_dir 代替 work_dir。传给子 skill 的 work_dir 参数也传入 data_dir 的值。
子命令特殊处理:若当前子命令为 analyze、tailor 或 status,在生成新 run_id 之前,先扫描 <data_dir>/output/ 下已有的 run 目录(格式 YYYY-MM-DD-HHMM),若存在则复用最新一个 run_id(不创建新目录,读取已有 state.json 继续使用);若不存在,则按上述流程创建新 run_id。
Step 2:获取简历
(全流程、fetch、analyze 子命令时执行)
检查 <data_dir>/.work/resume.md 是否存在:
若已存在:告知用户:
「已检测到上次保存的简历,继续使用。 如需更换简历,直接把新简历发给我(支持文件 / 路径 / 粘贴文本),我会帮你替换并重新评估。 如果继续用当前简历,告诉我「继续」。」
停止执行,等待用户回复:
- 用户发来新简历 → 按下方「若不存在」的方式处理,覆盖保存
resume.md,进入 Step 2.5 重新评估 - 用户说「继续」或其他非简历内容 → 跳过本步骤,跳过 Step 2.5,直接进入 Step 3
若不存在:提示用户提供简历:
「请提供你的简历,支持以下两种方式:
① 发送 .md 格式简历文件,或告诉我文件的本地路径(如 /Users/xxx/resume.md)
② 直接将简历文字粘贴到消息框发送(最快最稳,从任意 PDF/Word 阅读器全选复制即可)
不支持直接上传 PDF/Word 等版式文件——这类格式跨环境解析容易失真或失败。请用方式 ② 直接贴文本。」
根据用户提供方式处理:
-
发文件(.md)/ 文件路径(.md):用 Read 工具或 Bash 读取文件内容
-
发文件 / 文件路径(其他格式,如 .pdf / .docx / .doc / .txt / .rtf 等):不要尝试任何形式的解析,直接告知用户:
「暂不支持 PDF / Word 等版式文件上传(跨环境解析容易失真)。请改用: ① 用任意 PDF/Word 阅读器打开简历,全选(Cmd/Ctrl+A)→ 复制(Cmd/Ctrl+C)→ 粘贴到这里发给我 ② 或将简历另存为
.md格式后重发」停止执行,等待用户重新操作
-
粘贴文本:按下方「粘贴文本处理流程」执行
⛔ 伦理硬约束:无论用户提供什么格式,绝对禁止凭空编造、想象、推断简历内容。识别不了的格式就停止流程让用户重传,绝不生成假简历。
.md 文件分支处理:读取内容后直接写入 <data_dir>/.work/resume.md,告知用户「✅ 简历已保存。」,进入 Step 2.5。
粘贴文本处理流程
仅当用户走「粘贴文本」分支时执行:
步骤 1:保存原始留底
将用户粘贴的原始内容(一字不改)写入 <data_dir>/.work/resume.raw.md。这是任何后续清洗的回滚备份。
步骤 2:启发式检测格式是否已 OK
判断粘贴文本是否「已是结构化 Markdown」,必须同时满足以下条件:
- 包含 ≥ 2 处章节标记(行首是
#、##或1./2.这类有序列表标号) - 包含 ≥ 2 处列表标记(行首是
-或*或+后接空格) - 平均非空行长度 ≥ 15 个字符(说明没被一行一词强行断开)
- 不存在大量"单字孤立行"(连续 5 行以上每行只有 1-3 字符的情况)
全部满足:直接把原始内容写入 <data_dir>/.work/resume.md,告知用户「✅ 简历已保存(格式良好,直接使用)。」,进入 Step 2.5。不询问、不清洗。
任一不满足:进入步骤 3。
步骤 3:询问用户选择
关键约束:
- 必须输出用户粘贴的完整简历原文(一字不少,不是预览、不是摘要、不是前 N 字截断)
- 简历原文之后才是 A/B 问题
- A/B 问题必须是整条消息的最后一段,让用户视线最后落点就是
👉 回复 A 或 B - 简历原文之前不要写任何说明文字(如"以下是你的简历""收到了"等开场白)
- 简历原文用代码块(``` 包裹)显示,避免 Markdown 渲染干扰原始格式
输出格式严格如下:
```
<用户粘贴的简历完整原文,一字不动>
```
---
📝 上面是你粘贴的简历内容。看起来格式有些问题——可能存在断行、缺章节标记等,常见于从 PDF/Word 复制带过来的。
两种处理方式:
A. **帮我整理一下** —— 只动格式:合并被断开的同段、还原项目符号、加章节标题;**不动一个字**
B. **保留我的原始格式** —— 我就要这样,不要改
👉 回复 A 或 B
停止执行,等待用户回复。
禁止:
- 截断简历内容(即使简历很长也要全部贴出)
- 在 A/B 问题之后再追加任何文字
- 在简历原文上方加任何标题/说明(直接代码块开始)
步骤 4:按用户选择处理
- 用户回 A(或语义等价:「整理」「清洗」「帮我弄」「随便」等)→ 进入步骤 5 做结构归一化
- 用户回 B(或语义等价:「不动」「原样」「保留」「不要改」等)→ 把原始内容写入
<data_dir>/.work/resume.md,告知用户「✅ 已按你的原始格式保存。」,进入 Step 2.5 - 用户回模糊内容(如「?」「啥意思」)→ 简短重新解释一次 A/B 含义,再等回复
- 用户直接发了新简历→ 当作重新粘贴处理,回到步骤 1
ステップ 5:構造の正規化を実行(A パスのみ)
書式だけを厳密に変更し、文字は一字も変更しません。許可される操作と絶対に禁止される操作は以下のとおりです。
✅ 許可される操作:
| 操作 | 説明 |
|---|---|
| 強制的に分断された同一文の行を結合 | 负责抖音\n小视频\n前端开发 → 负责抖音小视频前端开发。判断基準:前の行が文末の句読点(。!?.!?)で終わっておらず、次の行が新しい章やリスト項目の開始に見えないこと |
| 分割された見出し/氏名を結合 | 张\n三 → 张三;工作经\n历 → 工作经历 |
| 連続する空行を削除 | 3 行以上連続する空行 → 1 行だけ残す |
| 箇条書き記号を復元 | 行頭に単独である · ● ▪ ◦ ▶ → - に置換 |
| 章見出しの階層を復元 | 「个人信息」「工作经历」「项目经历」「项目经验」「教育背景」「专业技能」「自我评价」などの一般的な章のキーワードを識別し、前に ## を追加 |
| インデントを整える | タブ文字 → 半角スペース 2 個;行頭の過剰なスペースは 2 個単位に統一 |
| 文字化けを修正 | ゼロ幅文字 などの不可視文字を削除 |
| 複数列の混在による明らかな順序の乱れを削除 | 複数列の問題だと 100%確信でき、正しく復元できる場合のみ。不確かな場合は変更しない |
❌ 絶対に禁止される操作:
- 文字内容を追加、削除、書き換えること(たとえ一字でも)
- 誤字を修正すること(ユーザーが「杭洲」と書いた場合は「杭洲」のまま残す)
- 翻訳、要約、簡略化、加筆
- 文脈から欠落情報を推測して補完すること
- ユーザーが記載していない章、スキル、経歴を追加すること
- 出来事の順序、日時、数字を変更すること
❌ のいずれかに違反すると、倫理上の厳格な制約に関する警告が発生します。必ず作業を停止してユーザーに伝えてください。
クリーニング完了後:
- クリーニング結果を
<data_dir>/.work/resume.mdに書き込む - ユーザーにクリーニングの内容を簡潔に伝える:「📝 履歴書の書式を整理しました:改行を N か所結合/箇条書き記号を M 個復元/章見出しを K 個追加しました。元のバージョンは保存済みです(気に入らない場合は「原版に戻して」とお伝えください)。」
- Step 2.5 に進む
ユーザーが後から元に戻したい場合(すべての段階)
ユーザーが後続の手順で「原版に戻して」「原版を使って」「貼り付けた方を使って」「クリーニング後のものは使わないで」など、同等の意味を持つ指示をした場合:
<data_dir>/.work/resume.raw.mdが存在するか確認する- 存在しない → 「元の保存バージョンが見つかりません(今回の履歴書はクリーニングされていない可能性があります)。変更する場合は、履歴書を再度貼り付けるか、新しい履歴書を送信してください。」
- 存在する → Bash を使って
resume.raw.mdをresume.mdにコピーし、上書きする
- ユーザーに「✅ 元のバージョンに切り替えました。」と伝える
- Step 2.5 に戻って評価する
Step 2.5:履歴書の品質評価
(今回の会話でユーザーが新しい履歴書を提供した場合のみ実行。キャッシュを再利用する場合は Step 3 に進む)
<data_dir>/.work/resume.md を読み込み、すべての評価可能な単位を見つけて、個別に採点し、結果を出力します。
⛔ 必ず先にフィルタリングしてから評価してください。以下の 3 つのルールは必須の前提条件であり、省略または回避できません。
- 評価に関する作業を行う前に、ルール一→二→三の完全な選別プロセスを必ず完了すること
- 会社名、職名、部署名からその経歴の業務内容を推測して評価してはなりません——評価対象にできるのは、履歴書に明記された行動動詞と結果の記述のみです
- ルール三を通過したテキストだけを「段落ごとの分析」に含めます。選別を通過しなかったブロック/行は一切含めません
3 つの判定ルールを順番に実行します:
ルール一:ブロック全体をスキップ(内容を見ずにそのまま除外)
以下のブロックは、何が書かれていても全体をスキップし、評価しません:
- 個人情報/基本情報/連絡先
- 専門スキル/スキルの特長/技術力/ツールの使用経験/技術スタック
- 学歴/教育経歴/学歴
- 自己評価/プロフィール/希望職種
- 資格/賞/栄誉
ブロック名は上記の文字列に限りません。意味が上記の種類に該当する場合(「私のスキル」「習得しているツール」「受賞歴」など)も、同様に全体をスキップします。
⚠️ 複合ブロックの扱い:ブロック名に仕事と教育の両方が含まれる場合(「職歴および学歴」「教育と職務の経歴」など)、全体をスキップせず、ルール二に進んで行ごとに判断します。そのうち教育項目に該当する行(学校名、専攻名、学位、在学期間を含む)は、ルール二に従って同様にスキップします。
その他すべてのブロック(職務経歴、プロジェクト経験、インターン経験、起業経験、アルバイト経験、または任意の名称の経験に関するブロック)はルール二に進みます。
ルール二:ブロック内の見出し行をスキップ
経験に関するブロック内では、各行を個別に判断します。その行が以下の 2 点を両方満たす場合、スキップします:
- 行動動詞(担当、主導、設計、推進、構築、最適化、完了、実現、率いるなど)がない
- 結果の記述(向上、低下、成長、削減、達成、超過、節約、または具体的な数字/パーセンテージ)がない
⚠️ 重要:職名や部署名に含まれる動詞的な語句は、行動動詞とは見なしません。 たとえば「運営グループ」「ブランド部」「設計課」「管理職」などは、組織単位の名称ラベルであり、個人の行動の記述ではないため、評価対象にはなりません。判断の基準は、その語句が「この人が何をしたか」を表しているのか、それとも「この部署/職務の名称」を示しているのかです。
例:
- 「字节跳动 · 产品经理 · 2021.03—2023.06」→ ❌ スキップ(動詞も結果もない)
- 「某文化传媒公司 2023.7~2025.11 新媒体-运营组」→ ❌ スキップ(「运营」は部署名であり、行動の記述ではない)
- 「某互联网科技公司 2022.6~2023.6 市场部-品牌组」→ ❌ スキップ(「品牌」は部署名であり、行動の記述ではない)
- 「xx大学 2018.9~2022.6 新闻传播专业-本科」→ ❌ スキップ(教育項目であり、行動も結果もない)
- 「项目名称:xxx 平台 | 角色:负责人 | 2022.01—2022.06」→ ❌ スキップ(動詞も結果もない)
⚠️ 空ブロックのルール(ルール二の実行後、すぐに確認)
ある経験に関するブロックでルール二を適用した結果、すべての行がスキップされた場合(つまり、ブロック内に行動動詞または結果の記述を含む行がない場合)、そのブロックは以下の 3 つの評価軸による採点に進みません。そのブロックについて評価項目を生成してはならず、他のブロックから内容を借りて補ってもなりません。
典型的な例:「職務経歴および学歴」ブロックに会社名+期間+部署名の 3 行のヘッダー情報しかない場合、ルール二ですべてスキップされるため、そのブロックは段落ごとの採点に含めません。
📛 ただし、内容が空の職務/インターン経験は、何も言わずに消してはなりません。必ず明確に警告してください:
このようなブロックをスキップする際、職務/インターン経験に該当する空の経歴項目(会社名+期間。例:「某文化传媒公司 2023.7~2025.11」)を記録します。該当する項目が 1 つでもある場合、評価出力の最上部に「内容が空の経歴に関する警告」ブロックを出力しなければなりません(下記の出力形式を参照)。
理由:内容が空の職務経歴は履歴書の最も致命的な弱点であり、tailor の段階でも書き換えによって対応できません(倫理上の一線により、内容の捏造が禁止されています)。こうした空の経歴がユーザーの主な職務年数を占める場合、それらを補完することは履歴書のカスタマイズよりも重要です。ユーザーがはっきり認識できるようにする必要があり、黙って消してはなりません。教育項目の空の経歴は、教育経歴にはそもそも行動や結果が必要ないため、この警告の対象外です。
ルール三:残りの内容だけを評価
最初の 2 つのルールで選別した後に残った文/項目を評価可能な単位とし、以下の 3 つの評価軸で採点します。
⚠️ 見出し行と説明文が同じ行に混在している場合は、ルール二に従って判断します。行動動詞または結果の記述を含んでいれば、評価対象となります。
- 「在字节跳动担任产品经理期间主导了 xx 项目落地,DAU 提升 40%」→ ✅ 評価対象(動詞と結果を含む)
評価軸
⛔ 上記のルール三を通過した内容だけが、このステップに進めます。 あるブロック(「職務経歴および学歴」など)のすべての行がルール二でスキップされた場合、そのブロックについて評価項目を一切生成しません——それが職務経歴だと判断できたとしても、履歴書に行動や結果の記述がないため、評価しません。
各記述内容を、以下の 3 つの軸で採点します(✅ 合格/⚠️ 弱い/❌ 欠落):
| 評価軸 | ✅ 合格 | ⚠️ 弱い | ❌ 欠落 |
|---|---|---|---|
| 状況/課題(S/P) | 明確な業務背景、または解決すべき課題がある | 背景が曖昧で、簡単に触れているだけ | 状況の説明がまったくない |
| 行動(A) | 「自分が何をしたか」を具体的に説明し、方法/手段がある | 責任についてのみ記載し、行動の詳細がない | 欠落 |
| 結果(R) | 数値による定量化、または明確な業務上の価値がある | 結果が曖昧(「効果が良かった」など) | 結果の記述がない |
追加の確認事項:
- 「担当」「参加」「支援」などの受け身の動詞を多用していないか(減点対象。能動的な動詞への変更を推奨)
- 明らかに定量化できるのに空欄になっている指標がないか(tailor の段階で
[请填写:xxx]のプレースホルダーを挿入します)
出力形式
📋 简历质量评估
<【仅当检测到空壳工作/实习经历时,在最顶部输出此块;否则整块省略】
⚠️ 重要提醒:你有 <N> 段工作经历是空的
· <公司名> · <时间段>
· <公司名> · <时间段>
这几段只有公司名和时间,没写任何工作内容和成果。这是你简历最大的短板:
- 系统算匹配度时,这些经历因为没内容会被整体跳过,匹配分会明显偏低
- 定制简历时,AI 不会替你编造内容(伦理红线),这几段只能原样保留、无法优化
👉 补全这几段的「做了什么 + 拿到什么结果」,比任何简历定制都重要。强烈建议先选 A 补这部分。
>
整体:<一句话总结,如"行动描述较充分,但结果量化普遍缺失,建议优先补充">
逐段分析:
【公司名 · 职位名】
场景/问题 ✅ 行动 ⚠️ 结果 ❌
问题:<具体说明薄弱点>
建议:<一句话改写方向>
【项目名 · 角色】
场景/问题 ⚠️ 行动 ✅ 结果 ⚠️
问题:<说明>
建议:<建议>
(…其他段落)
⚡ 最值得优先改的 1-2 件事:
1. <最高优先级>
2. <次优先级>
🔧 专业技能板块诊断:
<独立于上方 STAR 评估,专项检查技能板块(语义识别板块,任意名称均适用;若无标题,按内容特征识别:连续排列的技能词/工具名/框架名)。按以下四类问题逐一检查,有问题输出具体条目,无问题省略该类;若整个技能板块无任何问题,省略「🔧 专业技能板块诊断」整节:>
- **虚浮词/空泛声称**:「精通」「熟练掌握」后面没有具体场景或工具,或「精通所有前端技术」这类大而无当的表述。
针对每条有问题的条目给出**改写示范**,例:
「精通 Python」→「熟练使用 Python(Flask/pandas,用于 xx 数据处理)」
- **缺工具名/过于宽泛**:只写了能力方向,没有写具体工具/平台/框架。例「熟悉数据分析」「了解前端开发」。
针对每条给出建议补全方向,例:
「熟悉数据分析」→「熟悉数据分析(SQL / Tableau / Power BI)」
- **无程度分层**:全部条目都是同一程度词(全是「熟悉」或全是「精通」),无法体现能力深浅。
如有此问题,提示按「熟练 / 熟悉 / 了解」或用户实际情况重新分级。
- **与目标岗位明显偏差**:<此项留空,tailor 阶段结合具体 JD 再给建议;Step 2.5 无 JD 上下文,不评估此项>
<仅当上述四类至少有一类有实质问题时,才输出「🔧 专业技能板块诊断」节;若技能板块写得合理(程度词有分层、有工具名、无大量空泛声称),整节省略,不输出任何占位文字。>
---
如何继续?
A. 我自己去改简历,改完发给你重新评估
B. 先不改,用当前简历继续
ユーザーの選択への対応
A を選択(履歴書を修正):
- ユーザーに「わかりました。修正後、新しい履歴書をそのまま送信してください(ファイル/パス/テキストの貼り付けに対応しています)。」と伝える
- 実行を停止し、ユーザーが再送信するのを待つ
- 受信後、Step 2 と同じ方法で処理し、
resume.mdを上書き保存する - Step 2.5 を再実行する(ユーザーが B を選ぶまで繰り返す)
B を選択(続行):
- ユーザーに「わかりました。続行します。」と伝える
- Step 3 を実行する
Step 3:岗位收集与导入
(全流程、fetch 子命令时执行)
提示用户:
「请上传你感兴趣的岗位,支持以下方式:
【方式一】直接发岗位详情页截图(任意招聘平台,Boss/智联/猎聘/拉勾等) 一次可发多张,📌 一张截图 = 一个岗位 岗位信息长请用长截图,截图里至少要有岗位名(公司名可选)
【方式二】告诉我截图所在的目录路径(如 /Users/xxx/Desktop/jobs) 我会自动读取该目录下的所有图片,以及 .jobs.json 文件(不递归子目录)
【方式三】用浏览器插件导出 .jobs.json 文件后一键拖进来(支持 Boss直聘 / 前程无忧 / 智联招聘 / 猎聘) 👉 没装插件?查看 README 安装说明」
发出上方提示后,停止执行,等待用户发送截图或消息。不得提前进入循环。
判断用户输入类型:
| 输入 | 处理分支 |
|---|---|
| 图片附件(一张或多张) | 分支 A:截图解析 |
文件路径,以 .jobs.json 结尾 | 分支 C:JSON 导入 |
| 目录路径 | 分支 B:扫描目录(图片 + .jobs.json 混合) |
分支 A:截图解析
调用 Skill 工具,加载 job-hunt-fetcher skill,传入:
work_dir:<data_dir 的值>run_id:<当前 run_id>screenshots:<本批次截图>
fetcher 内部处理(含分组确认交互),完成后写入 jd-pool。
分支 B:扫描目录
用 Bash 验证目录存在:
ls "<目录路径>"
若不存在,告知用户「目录不存在,请确认路径后重新发送。」并重新等待。
用 Bash 列出该目录下的图片文件和 .jobs.json 文件(只读当前目录,不递归子目录):
find "<目录>" -maxdepth 1 -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.webp" -o -iname "*.jobs.json" \) | sort
若结果为空,告知用户「该目录下没有找到图片或 .jobs.json 文件,请确认后重新发送。」并重新等待。
告知用户找到了哪些文件:「找到 张截图 + 个 .jobs.json 文件,开始处理。」
按文件类型分别处理:
- 图片 → 分支 A(调 fetcher)
- 每个 .jobs.json → 分支 C(调 import_jobs.py)
分支 C:JSON 导入
用 Bash 调用 import_jobs.py:
if command -v python3 >/dev/null 2>&1; then PYBIN=python3; \
elif command -v python >/dev/null 2>&1; then PYBIN=python; \
else echo "PYTHON_MISSING"; exit 1; fi
"$PYBIN" "$HOME/.claude/skills/job-hunt/import_jobs.py" "<data_dir>" "<json_path>"
预期 stdout:
OK: 已导入 N 个岗位 -> ...→ 成功,继续ERROR: ...→ 告知用户「.jobs.json 处理失败:<原因>。请检查文件后重发。」并重新等待PYTHON_MISSING→ 告知用户「需要 Python 3 才能处理 .jobs.json,请安装后重试。」并停止
公共后续
不论分支 A/B/C,全部处理完成后:
用 Bash 工具扫描 <data_dir>/.work/jd-pool/ 下所有 .md 文件(排除 .analysis.md),读取 frontmatter 中 run_id 等于当前 run_id 的文件(extension-import 也包含),提取其 id 字段,作为本批次 ID 列表;将 ID 列表追加到 state.json 的 stages.fetched,更新 checkpoint_at 和 phase 为 "fetched"。
若子命令为 fetch:告知用户「✅ JD 导入完成,共 个岗位。运行 /job-hunt analyze 开始分析。」并停止。
【全流程】⚠️ 本步骤完成后到 Step 7 之间,严禁输出任何文字(包括不得回显或复述子 skill 的输出内容)。state.json 更新完毕后,下一个动作必须是 Skill 工具调用(执行 Step 4),不得有任何文字夹在中间。
Step 4:分析(job-hunt-analyzer)
(全流程、analyze 子命令时执行)
确定待分析 JD 列表:
- 扫描
<data_dir>/.work/jd-pool/下所有.md文件(排除.analysis.md结尾的文件) - 读取每个文件的 frontmatter,筛选
status.analyzed: false的文件,提取其id字段 - 排除
state.json.stages.analysis_errors中已记录失败的 ID
若列表为空,告知用户「jd-pool 中没有待分析的 JD,请先上传截图。」并停止。
调用 Skill 工具,加载 job-hunt-analyzer skill,传入:
work_dir:<绝对路径>resume_path:<data_dir>/.work/resume.mdjd_ids:<待分析 JD ID 列表>preferences:{"soft_preferences": {"prefer_industries": [], "avoid_industries": [], "prefer_company_size": []}, "ranking": {"match_weight": 1.0, "preference_weight": 0.0}}run_id:<当前 run_id>
analyzer 返回后,用 Bash 工具更新 state.json phase 为 "analyzed"。
【全流程】⚠️ 不得回显或复述 analyzer 的返回内容,state.json 更新完毕后立即执行 Step 5,不得有任何文字输出。
Step 5:排序
(全流程或 tailor 子命令前执行,不调用 LLM)
⚠️ 本步骤全程使用 Bash / Read 工具操作,严禁输出任何文字(包括排序过程、中间结果、排名列表)。
用 Bash 读取 <data_dir>/.work/jd-pool/*.analysis.md,提取每个文件中的 scores.total 字段,按降序排列,得到 JD ID 有序列表。所有已分析 JD 全部参与排序,不截断。
用 Bash 将排序结果写入 state.json 的 stages.sorted_ids 字段,同时保留在内存中供 Step 6 直接使用。Step 6 断点续跑时,若内存中无排序结果,从 state.json.stages.sorted_ids 读取。
【全流程】⚠️ state.json 更新完毕后立即调用 Skill 工具执行 Step 6,不得有任何文字输出。
Step 6:定制简历(job-hunt-tailor)
(全流程或 tailor 子命令时执行)
检查 <data_dir>/.work/resume.md 是否存在,若不存在则执行 Step 2 获取简历流程后再继续。
取 Step 5 排序后的完整 JD ID 列表。
排除 state.json 中已在 stages.tailored 的(断点续跑时跳过)。
调用 Skill 工具,加载 job-hunt-tailor skill,传入:
work_dir:<绝对路径>resume_path:<data_dir>/.work/resume.mdjd_ids:<完整排序后的 JD ID 列表>run_id:<当前 run_id>
tailor 返回后,用 Bash 工具更新 state.json phase 为 "tailored"。
【全流程】⚠️ 不得回显或复述 tailor 的返回内容,state.json 更新完毕后立即执行 Step 7,不得有任何文字输出。
Step 7:生成 shortlist.html
(全流程最后一步,只产 HTML,不产 MD 文件)
执行顺序(严格按此顺序):
- 用 Bash 工具:探测 Python,调用
build_html.py生成 shortlist.html - 用 Bash 工具:更新
state.json的phase为"done" - 最后一步:在聊天里输出一条简短的完成消息(只含 HTML 链接 + 操作提示,不再输出排名 MD 概览,因为 HTML 视图就是用来看这些内容的)
执行命令:
# 1. 探测可用的 Python 命令
if command -v python3 >/dev/null 2>&1; then
PYBIN=python3
elif command -v python >/dev/null 2>&1; then
# 兜底(Windows 上常见只有 python 没有 python3)
PYBIN=python
else
PYBIN=""
fi
# 2. 调用脚本,或在没有 Python 时输出降级标记
if [ -n "$PYBIN" ]; then
"$PYBIN" "$HOME/.claude/skills/job-hunt/build_html.py" "<data_dir>" "<run_id>"
else
echo "PYTHON_MISSING"
fi
Bash 输出解析规则(决定最终向用户发的完成消息):
| 脚本 stdout 内容 | 状态 |
|---|---|
OK: ...(无论后续 OPENED 或 OPEN_FAILED) | HTML 生成成功,提示用户复制链接到浏览器打开 |
SKIP: template not found | 模板文件缺失(极少发生,提示重装) |
PYTHON_MISSING | 系统没装 Python 3,引导用户安装(流程失败,因为现在只产 HTML) |
ERROR: ...(stderr) | 其他错误,告知用户排查 |
脚本设计原则:
- LLM 完全不构造 JSON,由脚本自己扫描 jd-pool / tailored 目录读取所有字段
- 空文件优雅处理:tailored 文件不存在 → 该字段为空字符串
- 模板缺失优雅降级:template.html 不存在 → 打印
SKIP:退出 0,主流程不中断
更新 state.json phase 为 "done" 之后,必须在聊天里输出最终消息——根据 Bash 输出的标记选对应文案。
最终消息文案(4 种分支):
-
HTML 生成成功(Python 脚本输出
OK: ...):✅ 全部完成!共生成 <N> 份定制简历。 🌐 HTML 视图:file://<data_dir 绝对路径>/output/<run_id>/shortlist.html 💡 复制上面的 file:// 链接到浏览器地址栏打开,即可查看完整简历视图。页面支持: · 按匹配度排序查看所有岗位 · 在网页上直接编辑简历(自动保存到浏览器本地) · 一键导出为 PDF(中文字体跨平台一致) · 切换查看每个岗位的简历 / 改动 / 开场白其中
<N>用state.json.stages.tailored数组长度填充。 -
HTML 模板缺失(Python 脚本输出
SKIP: ...):⚠️ 流程已完成,但 HTML 视图未生成。 原因:未找到 ~/.claude/skills/job-hunt/template.html。 解决:请重新安装 skill(运行 install.sh 或 npx skills add)。 各岗位定制简历已写入:<data_dir 绝对路径>/output/<run_id>/tailored/ -
Python 不可用(Bash 输出
PYTHON_MISSING):⚠️ 流程已完成,但 HTML 视图未生成。 原因:你的系统没有 Python 3。 📦 装上 Python 3 之后即可补生成 HTML 视图: • Mac:终端跑 `xcode-select --install`(一次性,几分钟) • Windows:访问 https://python.org/downloads/ 下载安装 ⚠️ 安装时务必勾选「Add Python to PATH」 • Linux:通常已自带;若没有 `sudo apt install python3` 或 `sudo yum install python3` 装完之后,跑这条命令补生成 HTML: python3 ~/.claude/skills/job-hunt/build_html.py "<data_dir 绝对路径>" "<run_id>" 各岗位定制简历已写入:<data_dir 绝对路径>/output/<run_id>/tailored/ -
其他错误(脚本 stderr 输出
ERROR: ...):⚠️ HTML 生成失败。错误信息:<stderr 内容> 各岗位定制简历已写入:<data_dir 绝对路径>/output/<run_id>/tailored/ 请检查文件或重新运行。
⚠️ 关键约束:
- file:// 链接不要用反引号包——纯文本链接更容易让用户三击全选复制到浏览器
- 完成消息必须是整条回复的最后一段,不得放在中间
- 不再生成 shortlist.md,也不在聊天里输出 MD 内容概览——HTML 视图就是给用户看的,重复输出冗余且占屏
Step 7b(status 子命令):输出运行状态
扫描 <data_dir>/output/ 下所有子目录,找名字格式为 YYYY-MM-DD-HHMM 的目录,读取最新一个的 state.json,按以下格式输出:
Job-Hunt 状态报告
==================
Run ID:<run_id>
当前阶段:<phase>
工作目录:<data_dir>
进度统计:
已导入 JD:<stages.fetched 数量> 个
完成分析:<stages.analyzed 数量> 个
分析失败:<stages.analysis_errors 数量> 个
生成三件套:<stages.tailored 数量> 个
最后更新:<checkpoint_at>
若无任何 run 记录,告知用户「尚未运行过 /job-hunt,请先运行完整流程。」
Step 8(clean 子命令):强制清理
删除 <data_dir>/.work/jd-pool/ 下所有文件(含 .analysis.md)。
删除 <data_dir>/output/ 下所有 run 目录。
删除 <data_dir>/.work/resume.md(若存在)。
统计并告知用户清理了多少文件/目录。