mcpField
J
job-hunt

job-hunt

github.com/jpcwhj生産性79339 回インストール8 週間前に更新
リポジトリ

job-hunt について

求職活動を管理する Claude スキルです。履歴書の取り込み、求人収集、分析、求人別の調整から shortlist の作成までを扱い、段階実行や状態確認、クリーンアップにも対応します。

主な機能

  1. 01履歴書と求人情報の取り込み
  2. 02履歴書の品質と求人適合度の分析
  3. 03応募先に合わせた履歴書の調整
  4. 04HTML shortlist ビューの生成
  5. 05段階実行と状態確認

活用シーン

  1. 01求職開始前の履歴書評価
  2. 02スクリーンショットや JSON ファイルからの求人取り込み
  3. 03求人票に合わせた履歴書の比較と調整
  4. 04HTML ビューで shortlist を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jpcwhj/job-hunt --skill job-hunt

インストール後、Claude Code で/job-huntを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

build_html.py
build-font-subset.sh
import_jobs.py
vendor/
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:询问用户选择

关键约束:

  1. 必须输出用户粘贴的完整简历原文(一字不少,不是预览、不是摘要、不是前 N 字截断)
  2. 简历原文之后才是 A/B 问题
  3. A/B 问题必须是整条消息的最后一段,让用户视线最后落点就是 👉 回复 A 或 B
  4. 简历原文之前不要写任何说明文字(如"以下是你的简历""收到了"等开场白)
  5. 简历原文用代码块(``` 包裹)显示,避免 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 に進む

ユーザーが後から元に戻したい場合(すべての段階)

ユーザーが後続の手順で「原版に戻して」「原版を使って」「貼り付けた方を使って」「クリーニング後のものは使わないで」など、同等の意味を持つ指示をした場合:

  1. <data_dir>/.work/resume.raw.md が存在するか確認する
    • 存在しない → 「元の保存バージョンが見つかりません(今回の履歴書はクリーニングされていない可能性があります)。変更する場合は、履歴書を再度貼り付けるか、新しい履歴書を送信してください。」
    • 存在する → Bash を使って resume.raw.md を resume.md にコピーし、上書きする
  2. ユーザーに「✅ 元のバージョンに切り替えました。」と伝える
  3. 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.md
  • jd_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.md
  • jd_ids:<完整排序后的 JD ID 列表>
  • run_id:<当前 run_id>

tailor 返回后,用 Bash 工具更新 state.json phase 为 "tailored"。

【全流程】⚠️ 不得回显或复述 tailor 的返回内容,state.json 更新完毕后立即执行 Step 7,不得有任何文字输出。


Step 7:生成 shortlist.html

(全流程最后一步,只产 HTML,不产 MD 文件)

执行顺序(严格按此顺序):

  1. 用 Bash 工具:探测 Python,调用 build_html.py 生成 shortlist.html
  2. 用 Bash 工具:更新 state.json 的 phase 为 "done"
  3. 最后一步:在聊天里输出一条简短的完成消息(只含 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(若存在)。 统计并告知用户清理了多少文件/目录。

コメント