jobscout 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +123 -0
  2. package/bin/cli.mjs +190 -0
  3. package/chatgpt/GPT-INSTRUCTIONS.md +103 -0
  4. package/chatgpt/README.md +72 -0
  5. package/chatgpt/knowledge/job.schema.json +58 -0
  6. package/chatgpt/knowledge/match.schema.json +48 -0
  7. package/chatgpt/knowledge/profile.schema.json +121 -0
  8. package/chatgpt/knowledge/skill-application-assistant.md +38 -0
  9. package/chatgpt/knowledge/skill-bilingual-normalization.md +46 -0
  10. package/chatgpt/knowledge/skill-candidate-intake.md +43 -0
  11. package/chatgpt/knowledge/skill-find-jobs.md +50 -0
  12. package/chatgpt/knowledge/skill-fit-analyzer.md +37 -0
  13. package/chatgpt/knowledge/skill-job-collector.md +21 -0
  14. package/chatgpt/knowledge/skill-job-matcher.md +16 -0
  15. package/chatgpt/knowledge/skill-job-schema.md +62 -0
  16. package/chatgpt/knowledge/skill-scoring-rubric.md +108 -0
  17. package/job-matching-plugin/.claude-plugin/plugin.json +17 -0
  18. package/job-matching-plugin/.codex-plugin/plugin.json +18 -0
  19. package/job-matching-plugin/AGENTS.md +55 -0
  20. package/job-matching-plugin/README.md +84 -0
  21. package/job-matching-plugin/agents/job-collector.md +40 -0
  22. package/job-matching-plugin/agents/job-matcher.md +37 -0
  23. package/job-matching-plugin/commands/find-jobs.md +41 -0
  24. package/job-matching-plugin/docs/claude-runtime-notes.md +69 -0
  25. package/job-matching-plugin/schemas/job.schema.json +58 -0
  26. package/job-matching-plugin/schemas/match.schema.json +48 -0
  27. package/job-matching-plugin/schemas/profile.schema.json +121 -0
  28. package/job-matching-plugin/skills/application-assistant/SKILL.md +38 -0
  29. package/job-matching-plugin/skills/bilingual-normalization/SKILL.md +46 -0
  30. package/job-matching-plugin/skills/candidate-intake/SKILL.md +43 -0
  31. package/job-matching-plugin/skills/find-jobs/SKILL.md +50 -0
  32. package/job-matching-plugin/skills/fit-analyzer/SKILL.md +37 -0
  33. package/job-matching-plugin/skills/job-collector/SKILL.md +21 -0
  34. package/job-matching-plugin/skills/job-matcher/SKILL.md +16 -0
  35. package/job-matching-plugin/skills/job-schema/SKILL.md +62 -0
  36. package/job-matching-plugin/skills/scoring-rubric/SKILL.md +108 -0
  37. package/package.json +46 -0
@@ -0,0 +1,55 @@
1
+ # JobMatching — Codex plugin
2
+
3
+ Thư mục này **là một plugin dùng được ở CẢ Codex lẫn Claude Code** (dual-manifest):
4
+ `.codex-plugin/plugin.json` là manifest Codex, `.claude-plugin/plugin.json` là manifest Claude —
5
+ dùng CHUNG `skills/`, `schemas/`. Mục tiêu: tìm và xếp hạng job phù hợp nhất với
6
+ **target** + **CV** của ứng viên, song ngữ Việt–Anh. Lấy dữ liệu job qua **Search Engine + web scraping**
7
+ (tool `web_search` của Codex — không cần API key).
8
+
9
+ > Đường dẫn tham chiếu trong skill (`schemas/…`, `scripts/…`) là **tương đối so với plugin root**
10
+ > (thư mục chứa file này). Trên Claude Code có thể dùng biến `${CLAUDE_PLUGIN_ROOT}`; trên Codex dùng
11
+ > đường dẫn tương đối như hiện tại.
12
+
13
+ ## Thành phần plugin
14
+
15
+ - **Skills** (`skills/*/SKILL.md`): `find-jobs` (điều phối full pipeline), `candidate-intake`,
16
+ `scoring-rubric`, `job-schema`, `bilingual-normalization`, `fit-analyzer`, `application-assistant`.
17
+ - **Agents** (`agents/*.md` cho Claude; skills `job-collector`/`job-matcher` cho Codex):
18
+ chạy context riêng cho các bước tốn token (search/scrape, chấm điểm bulk).
19
+ - **Schemas** (`schemas/*.json`): data contracts profile / job / match.
20
+
21
+ ## Bật web search
22
+
23
+ Bước collect cần tool `web_search`. Bật một trong hai:
24
+ - `codex --search`
25
+ - hoặc trong `~/.codex/config.toml`: `tools.web_search = true`
26
+
27
+ ## Pipeline
28
+
29
+ ```
30
+ CV + Target
31
+ │ [1] candidate-intake (skill) → data/profiles/<slug>.json
32
+
33
+ │ [2] job-collector (agent) → data/jobs/<run-id>.json (Job boards, tối đa 20)
34
+
35
+ │ [3] job-matcher (agent) → data/results/<run-id>.shortlist.json
36
+
37
+ │ [4] fit-analyzer (skill) → data/results/<run-id>.fit_report.md
38
+
39
+ [5] application-assistant (skill, tùy chọn) → CV bullets / cover letter / tin nhắn tiếp cận HR
40
+ ```
41
+
42
+ `<run-id>` = ngày hiện tại (YYYY-MM-DD). Dữ liệu chạy ghi vào `data/` trong thư mục làm việc.
43
+
44
+ ## Trọng số chấm điểm (mặc định)
45
+
46
+ skills 35% · seniority 20% · domain 15% · compensation 15% · location 5% · culture 5%
47
+ → override qua `target.priorities` trong profile (chuẩn hóa tổng = 1).
48
+
49
+ ## Nguyên tắc vận hành (bắt buộc)
50
+
51
+ - **Tiêu chuẩn thu thập**: Quét từ job boards chính thống (ITviec, TopCV, VietnamWorks, LinkedIn). Chỉ giữ job xem được full JD và còn hạn; snippet-only / hết hạn / 404-410 → bỏ.
52
+ - **Không bịa** trường dữ liệu job/CV. Thiếu → `unknown`, hạ `confidence` (không chấm 0 mù quáng).
53
+ - Giữ nguyên **đơn vị lương gốc** theo JD; chỉ quy đổi khi so sánh (tỉ giá $1 = 26.100 VND).
54
+ - Tôn trọng robots.txt/ToS; **không vượt anti-bot/CAPTCHA**.
55
+ - **Không tự nộp hồ sơ / gửi email / gửi tin nhắn** thay người dùng.
@@ -0,0 +1,84 @@
1
+ # Job Matching Plugin
2
+
3
+ Plugin Claude Code giúp **tìm và xếp hạng job phù hợp nhất với target + CV** của ứng viên. Xử lý song ngữ Việt–Anh, lấy dữ liệu job qua Search Engine + Web scraping (không cần API key).
4
+
5
+ ## Cài đặt
6
+
7
+ Thêm marketplace rồi cài plugin (trong phiên `claude` tương tác):
8
+
9
+ ```bash
10
+ /plugin marketplace add <đường-dẫn-hoặc-git-repo-này>
11
+ ```
12
+
13
+ ```bash
14
+ /plugin install job-matching
15
+ ```
16
+
17
+ > Marketplace nằm ở `.claude-plugin/marketplace.json` tại gốc repo; plugin nằm trong `job-matching-plugin/`.
18
+
19
+ ## Cách dùng
20
+
21
+ Nhanh nhất — chạy full pipeline bằng slash command:
22
+
23
+ ```bash
24
+ /find-jobs D:\path\to\CV.pdf
25
+ ```
26
+
27
+ Hoặc gọi từng bước bằng ngôn ngữ tự nhiên (skill tự kích hoạt): "parse CV này", "tìm job cho tôi", "chấm điểm các job", "làm báo cáo fit".
28
+
29
+ ## Pipeline
30
+
31
+ ```
32
+ CV + Target
33
+ │ candidate-intake (skill)
34
+ ▼ → data/profiles/<slug>.json
35
+ job-collector (subagent) ← Job boards (tối đa 20)
36
+ ▼ → data/jobs/<run-id>.json
37
+ job-matcher (subagent) ← chấm điểm theo scoring-rubric
38
+ ▼ → data/results/<run-id>.shortlist.json
39
+ fit-analyzer (skill) ← báo cáo fit/gap, mọi job có link nộp CV
40
+ ▼ → data/results/<run-id>.fit_report.md
41
+ application-assistant (skill, tùy chọn) ← tailor CV/cover letter & tin nhắn tiếp cận HR
42
+ ```
43
+
44
+ ## Thành phần
45
+
46
+ | Thành phần | Loại | Vai trò |
47
+ |---|---|---|
48
+ | `find-jobs` | Command | Điều phối toàn bộ pipeline |
49
+ | `candidate-intake` | Skill | Parse CV + target → profile.json |
50
+ | `job-collector` | Agent | Search + scrape từ Job boards → jobs.json |
51
+ | `job-matcher` | Agent | Chấm điểm & rank → shortlist.json |
52
+ | `fit-analyzer` | Skill | Báo cáo fit/gap (link nộp CV cho mọi job) |
53
+ | `application-assistant` | Skill | Tailor CV / cover letter / tin nhắn tiếp cận HR |
54
+ | `scoring-rubric` | Skill | Công thức tính điểm (nền tảng) |
55
+ | `job-schema` | Skill | Schema chuẩn cho job (kèm contact) |
56
+ | `bilingual-normalization` | Skill | Chuẩn hóa skill/chức danh/lương Việt–Anh |
57
+
58
+ ## Trọng số chấm điểm (mặc định)
59
+
60
+ skills 35% · seniority 20% · domain 15% · compensation 15% · location 5% · culture 5%
61
+ → Ứng viên override được qua `target.priorities` trong profile.
62
+
63
+ ## Yêu cầu môi trường
64
+
65
+ `WebSearch` / `WebFetch` cho Job boards là tool sẵn có của Claude Code — không cần cấu hình.
66
+
67
+ ## Dữ liệu
68
+
69
+ - **Định nghĩa** (agents/skills/schemas) nằm trong plugin.
70
+ - **Dữ liệu chạy** (`data/profiles`, `data/jobs`, `data/results`) ghi trong thư mục làm việc của người dùng, không nằm trong plugin.
71
+
72
+ ## Kiểm tra
73
+
74
+ ```bash
75
+ cd job-matching-plugin
76
+ python -m unittest discover -s tests -v
77
+ python -m compileall -q -f tests
78
+ ```
79
+
80
+ ## Ghi chú vận hành
81
+
82
+ - **Tiêu chuẩn thu thập**: Quét từ các trang tuyển dụng chính thống (ITviec, TopCV, VietnamWorks, LinkedIn), chỉ giữ job xem được full JD và còn hạn.
83
+ - Giữ nguyên đơn vị lương gốc theo JD; chỉ quy đổi khi so sánh (tỉ giá $1 = 26.100 VND).
84
+ - Tôn trọng robots.txt/ToS; không vượt anti-bot/CAPTCHA; không tự nộp hồ sơ / gửi tin nhắn thay người dùng.
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: job-collector
3
+ description: Thu thập tin tuyển dụng từ Search Engine + Web scraping dựa trên profile ứng viên, chuẩn hóa về job.schema.json. Dùng sau khi có profile.json và cần tìm job. Chạy trong context riêng vì tốn nhiều lượt search/fetch.
4
+ tools: WebSearch, WebFetch, Read, Write, Glob
5
+ model: sonnet
6
+ ---
7
+
8
+ # Job Collector — thu thập & chuẩn hóa job
9
+
10
+ Nhiệm vụ: từ `profile.json`, tìm và chuẩn hóa danh sách job → `data/jobs/<run-id>.json` (mảng object theo `${CLAUDE_PLUGIN_ROOT}/schemas/job.schema.json`).
11
+
12
+ ## Quy trình
13
+
14
+ ### 1. Thu thập từ Job Boards & Search Engine
15
+ Từ `target.desired_roles`, `desired_level`, `locations`, top skills, tạo nhiều biến thể query **song ngữ**:
16
+ - **Job boards VN**: `site:itviec.com`, `site:topcv.vn`, `site:vietnamworks.com`, `site:careerviet.vn`, và `site:linkedin.com/jobs`.
17
+ - Dùng `WebSearch` cho từng query, gom URL + snippet.
18
+ - Khử trùng theo URL chuẩn hóa. Ưu tiên tin còn hạn / mới đăng.
19
+
20
+ ### 3. Fetch & extract
21
+ - `WebFetch` URL job để lấy **full JD** (không chỉ snippet). Tôn trọng robots.txt/ToS. Trang chặn/cần JS render → thử browser.
22
+ - Phải xem được nội dung đầy đủ (`extraction_confidence ≥ 0.5`) và **còn hạn ứng tuyển** (bỏ qua nếu 404/410, "job đã hết hạn / expired", deadline đã qua).
23
+ - **Bóc tách thông tin liên hệ** (nếu JD có): lưu vào trường `contact` (email, phone, how_to_apply).
24
+ - **Không** vượt anti-bot/CAPTCHA.
25
+
26
+ ### 4. Chuẩn hóa
27
+ - Áp skill `job-schema` để map vào schema; áp `bilingual-normalization` cho skills/location/lương.
28
+ - Tạo `id` hash ổn định để khử trùng across nguồn.
29
+ - Set `extraction_confidence` trung thực (full JD cao: 0.8-1.0).
30
+
31
+ ### 5. Ghi kết quả
32
+ - Ghi mảng job hợp lệ vào `data/jobs/<run-id>.json`.
33
+ - Trả về tóm tắt: số job thu được, số bị bỏ (chặn/lỗi), cảnh báo dữ liệu thiếu.
34
+
35
+ ## Nguyên tắc
36
+ - **Chỉ giữ job xem được full JD & còn hạn**.
37
+ - Ưu tiên độ chính xác hơn số lượng; không nhồi job không liên quan.
38
+ - Không bịa trường dữ liệu (xem job-schema). Thiếu → unknown.
39
+ - `url` phải là link trang tuyển dụng — luôn có mặt cho mọi job.
40
+ - **Mục tiêu số lượng: tối đa 20 job** hợp lệ. Nếu sau khi lọc còn dư, mở rộng query để đủ ~20; nếu ít hơn, nêu rõ lý do.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: job-matcher
3
+ description: Chấm điểm và xếp hạng các job đã thu thập so với profile ứng viên theo scoring-rubric, tạo shortlist. Dùng sau job-collector. Chạy context riêng vì chấm điểm bulk tốn token.
4
+ tools: Read, Write, Glob
5
+ model: sonnet
6
+ ---
7
+
8
+ # Job Matcher — chấm điểm & xếp hạng
9
+
10
+ Nhiệm vụ: đọc `profile.json` + `data/jobs/<run-id>.json`, chấm điểm từng job, tạo `data/results/<run-id>.shortlist.json`.
11
+
12
+ ## Quy trình
13
+
14
+ ### 1. Nạp dữ liệu & trọng số
15
+ - Đọc profile và mảng job.
16
+ - Xác định trọng số: dùng `target.priorities` nếu có (chuẩn hóa tổng = 1), ngược lại dùng mặc định của `scoring-rubric`.
17
+
18
+ ### 2. Chấm điểm từng job
19
+ Áp **skill `scoring-rubric`** cho mỗi job:
20
+ - Bước 0: hard filter dealbreakers → excluded nếu vi phạm.
21
+ - Chấm 6 chiều (skills, seniority, domain, location, compensation, culture).
22
+ - Chuẩn hóa skill/lương bằng `bilingual-normalization` trước khi so khớp.
23
+ - Tính overall_score, recommendation, confidence.
24
+ - Tạo object theo `${CLAUDE_PLUGIN_ROOT}/schemas/match.schema.json`, kèm tham chiếu job (id, title, company, url, salary, location) để fit-analyzer dùng ngay.
25
+
26
+ ### 3. Khử trùng & xếp hạng
27
+ - Gộp job trùng `id` (giữ bản extraction_confidence cao hơn).
28
+ - Sắp theo overall_score giảm dần; excluded xuống cuối (hoặc tách riêng).
29
+
30
+ ### 4. Ghi kết quả
31
+ - Ghi `data/results/<run-id>.shortlist.json`: mảng {match, job_ref} đã sắp xếp.
32
+ - Trả tóm tắt: phân bố recommendation (strong/good/maybe/weak/excluded), top 5, gap phổ biến, số match confidence thấp cần xác minh.
33
+
34
+ ## Nguyên tắc
35
+ - Nhất quán: luôn theo scoring-rubric, ghi `weights_used` cho minh bạch.
36
+ - Không exclude vì thiếu dữ liệu — hạ confidence thay vì loại.
37
+ - Trung thực về điểm; không thổi phồng để có nhiều "strong".
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: Chạy full pipeline tìm việc — parse CV, thu thập job, chấm điểm, xuất báo cáo fit/gap.
3
+ argument-hint: [đường dẫn CV hoặc tên profile]
4
+ ---
5
+
6
+ # /find-jobs — Điều phối pipeline tìm việc
7
+
8
+ Chạy tuần tự 4 bước. `$ARGUMENTS` là đường dẫn CV (PDF/DOCX/text) hoặc tên profile đã có.
9
+
10
+ Dùng `<run-id>` duy nhất theo mẫu `<candidate-slug>-<YYYYMMDD-HHMMSS>-<suffix>` và giữ nguyên ID này xuyên suốt pipeline. Tạo thư mục `data/profiles`, `data/jobs`, `data/results` trong thư mục làm việc nếu chưa có.
11
+
12
+ ## Bước 1 — Intake (skill `candidate-intake`)
13
+ - Nếu `$ARGUMENTS` là file CV → parse và tạo `data/profiles/<slug>.json`.
14
+ - Nếu là tên profile đã tồn tại → dùng lại, hỏi xem có cập nhật target không.
15
+ - Thu thập/ xác nhận target: vai trò, cấp bậc, địa điểm/remote, lương, ưu tiên (priorities → trọng số), dealbreakers.
16
+ - Tóm tắt cho người dùng xác nhận.
17
+
18
+ ## Bước 2 — Collector (subagent `job-collector`)
19
+ - Truyền đường dẫn `profile.json`, `data/jobs/<run-id>.json` (output).
20
+ - **Nguồn Job Boards**: Quét từ ITviec, TopCV, VietnamWorks, LinkedIn... (WebSearch + WebFetch).
21
+ - Giữ job theo chuẩn: chỉ giữ job xem được full JD & còn hạn. Mục tiêu tối đa 20 job hợp lệ.
22
+
23
+ ## Bước 3 — Matcher (subagent `job-matcher`)
24
+ - Truyền `profile.json` + `data/jobs/<run-id>.json`.
25
+ - Chấm điểm theo skill `scoring-rubric`, dùng trọng số từ `target.priorities`.
26
+ - Ghi `data/results/<run-id>.shortlist.json`.
27
+
28
+ ## Bước 4 — Fit report (skill `fit-analyzer`)
29
+ - Đọc shortlist → tạo `data/results/<run-id>.fit_report.md`.
30
+ - **Phân tích chi tiết TẤT CẢ job trong shortlist** (nếu >20 thì chi tiết 20 job điểm cao nhất, phần còn lại vẫn liệt kê đủ trong bảng). **KHÔNG rút gọn báo cáo chỉ còn top 3.**
31
+ - Có thể đánh dấu thêm nhóm "Ưu tiên apply ngay" ở đầu, nhưng đó là phần bổ sung — không thay thế phần phân tích chi tiết từng job bên dưới.
32
+ - **Mọi job trong bảng xếp hạng đều có link nộp CV / bài post bấm được.**
33
+ - Gửi file báo cáo cho người dùng.
34
+
35
+ ## Sau khi xong
36
+ Đề xuất bước tiếp: tailor hồ sơ cho job top (skill `application-assistant`), hoặc mở rộng nguồn (remote quốc tế / thành phố khác).
37
+
38
+ ## Nguyên tắc
39
+ - Không bịa dữ liệu job/CV. Thiếu → unknown, hạ confidence.
40
+ - Tôn trọng robots/ToS khi scrape; không vượt anti-bot/CAPTCHA.
41
+ - Không tự nộp hồ sơ thay người dùng.
@@ -0,0 +1,69 @@
1
+ # JobMatching — plugin dual-manifest (Claude Code + Codex)
2
+
3
+ Thư mục `job-matching-plugin/` **là một plugin dùng được ở CẢ Claude Code lẫn Codex**: tìm và xếp
4
+ hạng job phù hợp nhất với **target** và **CV** của ứng viên, song ngữ Việt–Anh. Lấy dữ liệu job qua
5
+ **Search Engine + web scraping** (tool `WebSearch`/`WebFetch` trên Claude, `web_search` trên Codex —
6
+ không cần API key).
7
+
8
+ ## Bố cục repo
9
+
10
+ - `.claude-plugin/marketplace.json` (gốc repo) — marketplace Claude, trỏ tới plugin `./job-matching-plugin`.
11
+ - `job-matching-plugin/` — **nguồn chân lý duy nhất** của plugin (dùng chung cho cả hai hệ):
12
+ - `.claude-plugin/plugin.json` — manifest Claude.
13
+ - `.codex-plugin/plugin.json` — manifest Codex (khai báo `skills`).
14
+ - `docs/claude-runtime-notes.md` / `AGENTS.md` — hướng dẫn cho Claude / Codex.
15
+ - `commands/find-jobs.md` — slash command `/find-jobs` (Claude). Trên Codex dùng `skills/find-jobs/SKILL.md`.
16
+ - `skills/*/SKILL.md` — dùng chung: `find-jobs`, `candidate-intake`, `scoring-rubric`, `job-schema`,
17
+ `bilingual-normalization`, `fit-analyzer`, `application-assistant`.
18
+ - `agents/*.md` (Claude); Codex dùng skills `job-collector`, `job-matcher` trong generic subagent.
19
+ - `schemas/*.json` — data contracts profile / job / match.
20
+ - `tests/*.py` — unit test cho tính toàn vẹn plugin.
21
+ - `data/` — dữ liệu chạy trong thư mục làm việc (được gitignore phần nhạy cảm).
22
+
23
+ > Không tạo bản sao skill/agent thứ hai ở gốc repo. Skill dùng chung viết trung lập tên tool
24
+ > (`WebSearch`/`WebFetch` cho Claude, `web_search` cho Codex) để một nguồn phục vụ cả hai hệ.
25
+
26
+ ## Bật web search
27
+
28
+ `WebSearch` / `WebFetch` là tool sẵn có của Claude Code — không cần cấu hình thêm.
29
+
30
+ ## Pipeline
31
+
32
+ ```
33
+ CV + Target
34
+ │ [1] candidate-intake (skill) → data/profiles/<slug>.json
35
+
36
+ │ [2] job-collector (subagent) → data/jobs/<run-id>.json (Job boards, tối đa 20)
37
+
38
+ │ [3] job-matcher (subagent) → data/results/<run-id>.shortlist.json
39
+
40
+ │ [4] fit-analyzer (skill) → data/results/<run-id>.fit_report.md
41
+
42
+ [5] application-assistant (skill, tùy chọn) → CV bullets / cover letter / tin nhắn tiếp cận HR
43
+ ```
44
+
45
+ `<run-id>` = ngày hiện tại (YYYY-MM-DD). Dữ liệu chạy ghi vào `data/` trong thư mục làm việc.
46
+
47
+ ## Trọng số chấm điểm (mặc định)
48
+
49
+ skills 35% · seniority 20% · domain 15% · compensation 15% · location 5% · culture 5%
50
+ → override qua `target.priorities` trong profile (chuẩn hóa tổng = 1).
51
+
52
+ ## Kiểm tra trước khi phát hành
53
+
54
+ ```bash
55
+ cd job-matching-plugin
56
+ python -m unittest discover -s tests -v
57
+ python -m compileall -q -f tests
58
+ ```
59
+
60
+ Không commit `data/profiles`, `data/jobs`, `data/results`, CV gốc (`*.pdf`/`*.docx`), log hay `__pycache__`.
61
+
62
+ ## Nguyên tắc vận hành (bắt buộc)
63
+
64
+ - **Tiêu chuẩn thu thập**: Quét từ job boards chính thống (ITviec, TopCV, VietnamWorks, LinkedIn).
65
+ Chỉ giữ job xem được full JD và còn hạn; snippet-only / hết hạn / 404-410 → bỏ.
66
+ - **Không bịa** trường dữ liệu job/CV. Thiếu → `unknown`, hạ `confidence` (không chấm 0 mù quáng).
67
+ - Giữ nguyên **đơn vị lương gốc** theo JD; chỉ quy đổi khi so sánh (tỉ giá $1 = 26.100 VND).
68
+ - Tôn trọng robots.txt/ToS; **không vượt anti-bot/CAPTCHA**.
69
+ - **Không tự nộp hồ sơ / gửi email / gửi tin nhắn** thay người dùng.
@@ -0,0 +1,58 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "job.schema.json",
4
+ "title": "Job",
5
+ "description": "Tin tuyển dụng đã chuẩn hóa từ nguồn search/scrape.",
6
+ "type": "object",
7
+ "required": ["id", "title", "company", "url", "source"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "default": "1.0" },
10
+ "id": { "type": "string", "description": "Hash ổn định từ url hoặc company+title+location để khử trùng" },
11
+ "title": { "type": "string" },
12
+ "company": { "type": "string" },
13
+ "location": { "type": "string" },
14
+ "remote": { "type": "string", "enum": ["onsite", "hybrid", "remote", "unknown"], "default": "unknown" },
15
+ "url": { "type": "string", "description": "Link trang tuyển dụng / link JD gốc" },
16
+ "source": { "type": "string", "description": "vd 'itviec', 'topcv', 'vietnamworks', 'linkedin', 'careerviet', 'google'" },
17
+ "posted_date": { "type": "string", "description": "YYYY-MM-DD nếu có" },
18
+ "application_deadline": { "type": "string", "description": "YYYY-MM-DD hạn nộp nếu JD có ghi; job chỉ được giữ khi hạn >= ngày hiện tại" },
19
+ "employment_type": { "type": "string", "enum": ["full-time", "part-time", "contract", "internship", "unknown"], "default": "unknown" },
20
+ "language": { "type": "string", "enum": ["vi", "en", "mixed"], "default": "mixed" },
21
+ "description": { "type": "string", "description": "JD gốc (rút gọn nếu quá dài)" },
22
+ "contact": {
23
+ "type": "object",
24
+ "description": "Thông tin liên hệ ứng tuyển trực tiếp nếu JD có",
25
+ "properties": {
26
+ "email": { "type": "string" },
27
+ "phone": { "type": "string" },
28
+ "zalo": { "type": "string" },
29
+ "form_url": { "type": "string", "description": "Google Form / Typeform link nếu có" },
30
+ "how_to_apply": { "type": "string", "description": "Hướng dẫn ứng tuyển ngắn: vd 'Gửi CV qua email', 'Apply qua link'" }
31
+ }
32
+ },
33
+ "requirements": {
34
+ "type": "object",
35
+ "properties": {
36
+ "must_have_skills": { "type": "array", "items": { "type": "string" }, "description": "Canonical names" },
37
+ "nice_to_have_skills": { "type": "array", "items": { "type": "string" } },
38
+ "min_years": { "type": "number" },
39
+ "seniority": { "type": "string", "enum": ["intern", "junior", "mid", "senior", "lead", "manager", "director", "unknown"], "default": "unknown" },
40
+ "education": { "type": "string" }
41
+ }
42
+ },
43
+ "salary": {
44
+ "type": "object",
45
+ "properties": {
46
+ "min": { "type": "number" },
47
+ "max": { "type": "number" },
48
+ "currency": { "type": "string", "enum": ["VND", "USD", "unknown"], "default": "unknown" },
49
+ "period": { "type": "string", "enum": ["month", "year", "unknown"], "default": "unknown" },
50
+ "negotiable": { "type": "boolean" }
51
+ }
52
+ },
53
+ "industry": { "type": "string" },
54
+ "company_size": { "type": "string", "enum": ["startup", "sme", "enterprise", "unknown"], "default": "unknown" },
55
+ "collected_at": { "type": "string", "description": "ISO timestamp lúc thu thập" },
56
+ "extraction_confidence": { "type": "number", "description": "0-1, độ tin cậy khi parse JD (0.8-1.0 khi đọc được full JD)" }
57
+ }
58
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "match.schema.json",
4
+ "title": "MatchResult",
5
+ "description": "Kết quả chấm điểm 1 job so với profile. Xem scoring-rubric.",
6
+ "type": "object",
7
+ "required": ["job_id", "overall_score", "dimension_scores"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "default": "1.0" },
10
+ "job_id": { "type": "string" },
11
+ "overall_score": { "type": "number", "description": "0-100, tổng hợp có trọng số" },
12
+ "recommendation": { "type": "string", "enum": ["strong", "good", "maybe", "weak", "excluded"] },
13
+ "dimension_scores": {
14
+ "type": "object",
15
+ "description": "Mỗi chiều 0-100.",
16
+ "properties": {
17
+ "skills": { "type": "number" },
18
+ "seniority": { "type": "number" },
19
+ "domain": { "type": "number" },
20
+ "location": { "type": "number" },
21
+ "compensation": { "type": "number" },
22
+ "culture": { "type": "number" }
23
+ }
24
+ },
25
+ "weights_used": {
26
+ "type": "object",
27
+ "description": "Trọng số thực tế đã áp (sau khi override bằng target.priorities).",
28
+ "additionalProperties": { "type": "number" }
29
+ },
30
+ "matched_skills": { "type": "array", "items": { "type": "string" } },
31
+ "missing_must_have": { "type": "array", "items": { "type": "string" } },
32
+ "gaps": {
33
+ "type": "array",
34
+ "description": "Khoảng cách cần lấp (kỹ năng/kinh nghiệm) + mức nghiêm trọng.",
35
+ "items": {
36
+ "type": "object",
37
+ "properties": {
38
+ "area": { "type": "string" },
39
+ "detail": { "type": "string" },
40
+ "severity": { "type": "string", "enum": ["low", "medium", "high"] }
41
+ }
42
+ }
43
+ },
44
+ "dealbreaker_violations": { "type": "array", "items": { "type": "string" } },
45
+ "rationale": { "type": "string", "description": "Giải thích ngắn gọn vì sao điểm này (song ngữ nếu cần)" },
46
+ "confidence": { "type": "number", "description": "0-1, phụ thuộc extraction_confidence của job" }
47
+ }
48
+ }
@@ -0,0 +1,121 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "profile.schema.json",
4
+ "title": "CandidateProfile",
5
+ "description": "Hồ sơ ứng viên chuẩn hóa từ CV + target. Song ngữ Việt-Anh.",
6
+ "type": "object",
7
+ "required": ["candidate", "skills", "experience", "seniority", "target"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "default": "1.0" },
10
+ "candidate": {
11
+ "type": "object",
12
+ "required": ["name"],
13
+ "properties": {
14
+ "name": { "type": "string" },
15
+ "headline": { "type": "string", "description": "Chức danh hiện tại / tự mô tả ngắn" },
16
+ "location": { "type": "string" },
17
+ "summary": { "type": "string" },
18
+ "languages": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "properties": {
23
+ "language": { "type": "string" },
24
+ "level": { "type": "string", "enum": ["basic", "conversational", "professional", "native"] }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ },
30
+ "skills": {
31
+ "type": "array",
32
+ "description": "Kỹ năng đã chuẩn hóa (dùng canonical EN name qua bilingual-normalization).",
33
+ "items": {
34
+ "type": "object",
35
+ "required": ["name"],
36
+ "properties": {
37
+ "name": { "type": "string", "description": "Canonical name, ưu tiên EN, vd 'React'" },
38
+ "aliases": { "type": "array", "items": { "type": "string" } },
39
+ "category": { "type": "string", "description": "vd 'frontend', 'backend', 'data', 'soft-skill'" },
40
+ "years": { "type": "number" },
41
+ "level": { "type": "string", "enum": ["beginner", "intermediate", "advanced", "expert"] },
42
+ "evidence": { "type": "string", "description": "Trích từ CV làm bằng chứng" }
43
+ }
44
+ }
45
+ },
46
+ "experience": {
47
+ "type": "array",
48
+ "items": {
49
+ "type": "object",
50
+ "required": ["title"],
51
+ "properties": {
52
+ "title": { "type": "string" },
53
+ "company": { "type": "string" },
54
+ "start": { "type": "string", "description": "YYYY-MM" },
55
+ "end": { "type": "string", "description": "YYYY-MM hoặc 'present'" },
56
+ "duration_months": { "type": "integer" },
57
+ "domain": { "type": "string", "description": "vd 'fintech', 'e-commerce'" },
58
+ "highlights": { "type": "array", "items": { "type": "string" } }
59
+ }
60
+ }
61
+ },
62
+ "education": {
63
+ "type": "array",
64
+ "items": {
65
+ "type": "object",
66
+ "properties": {
67
+ "degree": { "type": "string" },
68
+ "field": { "type": "string" },
69
+ "school": { "type": "string" },
70
+ "year": { "type": "string" }
71
+ }
72
+ }
73
+ },
74
+ "total_years": { "type": "number", "description": "Tổng số năm kinh nghiệm liên quan" },
75
+ "seniority": {
76
+ "type": "string",
77
+ "enum": ["intern", "junior", "mid", "senior", "lead", "manager", "director"],
78
+ "description": "Suy ra từ total_years + phạm vi trách nhiệm"
79
+ },
80
+ "domains": { "type": "array", "items": { "type": "string" }, "description": "Ngành/lĩnh vực đã làm" },
81
+ "target": {
82
+ "type": "object",
83
+ "description": "Mong muốn của ứng viên. priorities dùng để override trọng số scoring.",
84
+ "required": ["desired_roles"],
85
+ "properties": {
86
+ "desired_roles": { "type": "array", "items": { "type": "string" } },
87
+ "desired_level": { "type": "string", "enum": ["intern", "junior", "mid", "senior", "lead", "manager", "director"] },
88
+ "locations": { "type": "array", "items": { "type": "string" } },
89
+ "remote_preference": { "type": "string", "enum": ["onsite", "hybrid", "remote", "any"], "default": "any" },
90
+ "salary": {
91
+ "type": "object",
92
+ "properties": {
93
+ "min": { "type": "number" },
94
+ "target": { "type": "number" },
95
+ "currency": { "type": "string", "enum": ["VND", "USD"], "default": "VND" },
96
+ "period": { "type": "string", "enum": ["month", "year"], "default": "month" }
97
+ }
98
+ },
99
+ "industries": { "type": "array", "items": { "type": "string" } },
100
+ "company_size": { "type": "array", "items": { "type": "string", "enum": ["startup", "sme", "enterprise", "any"] } },
101
+ "dealbreakers": {
102
+ "type": "array",
103
+ "description": "Điều kiện loại trừ cứng (hard filter). Vd 'no onsite ngoài HCM', 'không outsource'.",
104
+ "items": { "type": "string" }
105
+ },
106
+ "priorities": {
107
+ "type": "object",
108
+ "description": "Trọng số 0-1 ứng viên khai; override default trong scoring-rubric. Tổng nên ~1.",
109
+ "properties": {
110
+ "skills": { "type": "number" },
111
+ "seniority": { "type": "number" },
112
+ "domain": { "type": "number" },
113
+ "location": { "type": "number" },
114
+ "compensation": { "type": "number" },
115
+ "culture": { "type": "number" }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: application-assistant
3
+ description: Tùy chỉnh (tailor) nội dung CV và viết cover letter cho một job cụ thể dựa trên profile và JD. Dùng khi ứng viên đã chọn được job muốn apply và cần chuẩn bị hồ sơ.
4
+ ---
5
+
6
+ # Application Assistant — tailor hồ sơ theo job
7
+
8
+ Đầu vào: `profile.json` + 1 job (từ jobs.json hoặc URL/JD dán vào) + (tùy chọn) MatchResult để biết gaps.
9
+ Đầu ra: bản nháp CV bullets đã điều chỉnh và/hoặc cover letter.
10
+
11
+ ## Tailor CV bullets
12
+
13
+ - Chọn và viết lại các highlight kinh nghiệm sao cho khớp `must_have_skills` và trách nhiệm chính của JD.
14
+ - Dùng canonical skill name khớp với từ khóa JD (giúp qua ATS).
15
+ - Định lượng thành tích (số liệu) khi CV có dữ liệu; **không bịa** số hay kinh nghiệm không có.
16
+ - Nêu rõ chỗ nào là gap (missing must-have) và cách trình bày trung thực (vd "đang học", kinh nghiệm chuyển giao).
17
+
18
+ ## Cover letter & Email ứng tuyển
19
+
20
+ - Áp skill `cover-letter-writing` (nếu có) cho style. Cấu trúc: mở đầu (vì sao công ty/role này), thân (2–3 bằng chứng khớp yêu cầu), kết (call to action).
21
+ - Giọng văn phù hợp ngôn ngữ JD (vi/en/mixed). Ngắn gọn (~250–350 từ).
22
+ - Cá nhân hóa theo company (tránh mẫu chung chung).
23
+
24
+ ## Tin nhắn tiếp cận trực tiếp HR (Email / LinkedIn / Zalo)
25
+
26
+ Khi JD có kênh liên hệ trực tiếp và ứng viên muốn chủ động tiếp cận HR:
27
+
28
+ **Mẫu Direct Message / Email ngắn**:
29
+ > "Chào anh/chị [Tên HR/Recruiter], em thấy tin tuyển [Tên vị trí] của [Công ty]. Em có [X năm] kinh nghiệm về [Top 2-3 kỹ năng khớp], từng làm [Dự án/Sản phẩm tiêu biểu]. Em rất quan tâm vị trí này. Em xin gửi kèm CV: [Link CV] để anh/chị tiện xem qua ạ. Cảm ơn anh/chị!"
30
+
31
+ Điều chỉnh giọng văn theo kênh (email trang trọng hơn, LinkedIn/Zalo ngắn gọn hơn).
32
+
33
+ ## Ranh giới quan trọng
34
+
35
+ - **Không bịa** bằng cấp, kinh nghiệm, con số. Chỉ diễn đạt lại sự thật trong CV cho hấp dẫn hơn.
36
+ - Đây là bản nháp để ứng viên xem lại và tự chỉnh; nêu rõ điều đó.
37
+ - Không tự gửi email/nộp hồ sơ thay ứng viên.
38
+
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: bilingual-normalization
3
+ description: Chuẩn hóa kỹ năng, chức danh, địa điểm và lương giữa tiếng Việt và tiếng Anh về dạng canonical, phục vụ so khớp job/CV chính xác. Dùng khi parse CV, chuẩn hóa JD, hoặc so khớp skills/lương trong scoring.
4
+ ---
5
+
6
+ # Bilingual Normalization — chuẩn hóa Việt–Anh
7
+
8
+ Mục tiêu: mọi so khớp diễn ra trên **canonical name** thống nhất, tránh trượt vì khác ngôn ngữ/cách viết.
9
+
10
+ ## Skills — canonical name
11
+
12
+ - Canonical ưu tiên tên tiếng Anh phổ biến của công nghệ: `React`, `Node.js`, `Java`, `Python`, `Kubernetes`...
13
+ - Gộp biến thể: `ReactJS`/`React.js`/`Reactjs` → `React`; `NodeJS` → `Node.js`; `K8s` → `Kubernetes`; `.NET`/`dotnet` → `.NET`.
14
+ - Chức năng mô tả bằng tiếng Việt → canonical EN:
15
+ - "Lập trình viên" / "Kỹ sư phần mềm" → Software Engineer
16
+ - "Trưởng nhóm" → Lead ; "Quản lý dự án" → Project Manager
17
+ - "Kiểm thử" / "QC" → QA ; "Phân tích nghiệp vụ" → Business Analyst
18
+ - "Khoa học dữ liệu" → Data Science ; "Kỹ sư dữ liệu" → Data Engineer
19
+ - Giữ `aliases[]` trong profile/job để truy vết.
20
+
21
+ ## Chức danh & cấp bậc (seniority)
22
+
23
+ - Map cấp bậc VN → thang chuẩn: Fresher→junior, "Chuyên viên"→mid, "Senior/Kỹ sư cao cấp"→senior, "Trưởng nhóm/Team Lead"→lead, "Trưởng phòng"→manager, "Giám đốc"→director.
24
+ - Cẩn thận với "Senior" gắn năm kinh nghiệm khác nhau giữa công ty — ưu tiên `min_years` nếu có.
25
+
26
+ ## Địa điểm
27
+
28
+ - Chuẩn hóa: "TP.HCM"/"Sài Gòn"/"HCMC"/"Ho Chi Minh" → "Ho Chi Minh City"; "HN"/"Hà Nội" → "Hanoi"; "ĐN"/"Đà Nẵng" → "Da Nang".
29
+ - Nhận diện remote: "làm việc từ xa"/"remote" → remote; "linh hoạt"/"hybrid" → hybrid.
30
+
31
+ ## Lương — giữ nguyên gốc, chỉ quy đổi khi cần so sánh
32
+
33
+ - **Luôn giữ nguyên đơn vị & giá trị gốc** trong `job.salary` (currency đúng như JD). Ví dụ JD ghi "$1000" → lưu USD 1000; ghi "26 triệu" → lưu VND 26,000,000. Không tự đổi khi lưu trữ hay hiển thị.
34
+ - Đơn vị Việt:
35
+ - "15 triệu"/"15tr"/"15M" → 15,000,000 VND.
36
+ - "15-20 triệu"/"15-20M" → min 15tr, max 20tr.
37
+ - "Gross" / "Net" → parse giá trị số bình thường (ghi chú trong description nếu cần).
38
+ - "Thỏa thuận"/"Cạnh tranh"/"Negotiable"/"Deal" → không set số, `negotiable=true`, currency=unknown.
39
+ - USD: "$1500"/"1500 USD" → USD. "Up to $2000"/"Upto $2k" → max=2000, currency=USD.
40
+ - **Chỉ khi cần so sánh** (chấm điểm compensation, so hai job khác đơn vị): quy đổi tạm về VND/tháng bằng tỉ giá cố định **$1 = 26,100 VND**; year→month chia 12. Giá trị quy đổi này chỉ dùng nội bộ lúc tính toán, KHÔNG ghi đè giá trị gốc.
41
+
42
+ ## Nguyên tắc
43
+
44
+ - Chỉ map khi chắc chắn; nếu không rõ, giữ nguyên và ghi vào aliases để không mất thông tin.
45
+ - Không "dịch" tên riêng công ty.
46
+