jobscout 1.5.2 → 1.6.3

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.
package/bin/cli.mjs CHANGED
@@ -9,7 +9,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
  const PKG_ROOT = resolve(__dirname, "..");
10
10
  const PLUGIN_SRC = join(PKG_ROOT, "job-matching-plugin");
11
11
  const CWD = process.cwd();
12
- const REPO_URL = "https://github.com/hieutrong512/JobMatching";
12
+ const REPO_URL = "https://github.com/hieutrong512/JobScout";
13
13
 
14
14
  const RESET = "\x1b[0m";
15
15
  const GREEN = "\x1b[32m";
@@ -1,40 +1,61 @@
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 profile.json cần tìm job. Chạy trong context riêng 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 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`, `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 link trang tuyển dụng luôn 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.
1
+ ---
2
+ name: job-collector
3
+ description: Thu thập tin tuyển dụng từ MỘT nền tảng (job board). Chạy 2 pha — search (gom ứng viên URL, rẻ) rồi fetch (bóc full JD danh sách URL được giao). Điều phối viên spawn nhiều collector song song, mỗi nền tảng một cái.
4
+ tools: WebSearch, WebFetch, Read, Write, Glob
5
+ model: sonnet
6
+ ---
7
+
8
+ # Job Collector — thu thập & chuẩn hóa job cho MỘT nền tảng
9
+
10
+ Mỗi lần chạy, collector này chỉ phụ trách **một nền tảng** chạy **một trong hai chế độ** (`mode`).
11
+ Điều phối viên (`/find-jobs`) spawn song song nhiều collector, mỗi nền tảng một cái, và chèn một bước
12
+ **chọn toàn cục** ở giữa để phân bổ ngân sách fetch cho các URL tốt nhất — bất kể nền tảng nào.
13
+
14
+ ## Tham số nhận từ điều phối viên
15
+ - `mode` `search` hoặc `fetch`.
16
+ - `platform` domain nền tảng phụ trách (vd: `itviec.com`, `topcv.vn`, `vietnamworks.com`, `careerviet.vn`, `linkedin.com/jobs`).
17
+ - `profile_path` đường dẫn `profile.json`.
18
+ - (mode=search) `fetch_count` cận trên số ứng viên trả về của nền tảng này (dùng làm K khi cap top-K).
19
+ - (mode=search) `out_path` — file ứng viên: `data/jobs/<run-id>.<platform-slug>.candidates.json`.
20
+ - (mode=fetch) `urls` danh sách URL cụ thể cần fetch (đã được chọn toàn cục).
21
+ - (mode=fetch) `out_path` file job: `data/jobs/<run-id>.<platform-slug>.json`.
22
+
23
+ ---
24
+
25
+ ## Chế độ `search` — gom ứng viên (KHÔNG fetch)
26
+ Mục tiêu: trả về danh sách **đã lọc gọn** của nền tảng này (không phải toàn bộ) để điều phối viên
27
+ xếp hạng rẻ. **Không** `WebFetch` chế độ này. Snippet chỉ dùng để tính relevance/ước tuổi tin
28
+ **trong context của collector** rồi bỏ đi **KHÔNG ghi snippet vào file** (tiết kiệm token).
29
+
30
+ 1. Từ `target.desired_roles`, `desired_level`, `locations`, top skills → tạo vài biến thể query **song ngữ**, tất cả `site:<platform>`.
31
+ 2. `WebSearch` từng query, gom URL + snippet + tiêu đề. Khử trùng theo URL chuẩn hóa.
32
+ 3. **Lọc ngay tại nguồn** (bỏ trước khi trả về):
33
+ - **Tuổi tin 1 tháng (≥ 30 ngày) loại.** Ước `posted_days` từ snippet ("đăng X ngày trước", ngày đăng, "posted N days ago"). Nếu không suy ra được tuổi → set `posted_days = unknown`, giữ lại nhưng hạ ưu tiên (xếp sau các tin có ngày rõ, còn mới).
34
+ - Link không phải trang JD / snippet có dấu hiệu hết hạn / rõ ràng không liên quan → loại.
35
+ - `relevance` dưới ngưỡng sàn (vd < 0.3) → loại.
36
+ 4. Với mỗi ứng viên còn lại, ước lượng `relevance` (0-1) từ snippet so với target.
37
+ 5. **Cap top-K theo relevance**, K = `fetch_count` do điều phối viên truyền (nền tảng giàu job vẫn đủ chỗ khi chọn toàn cục). Chỉ giữ K ứng viên tốt nhất.
38
+ 6. Ghi mảng ứng viên **gọn** vào `out_path` (`data/jobs/<run-id>.<platform-slug>.candidates.json`), mỗi item chỉ gồm:
39
+ `{ url, title, platform, relevance, posted_days }` (KHÔNG snippet). Trả về số ứng viên giữ lại + số bị loại vì quá cũ.
40
+
41
+ ## Chế độ `fetch` — bóc full JD cho danh sách URL được giao
42
+ Chỉ fetch đúng các URL trong `urls` (đã được chọn toàn cục theo chất lượng), không tự tìm thêm.
43
+
44
+ 1. `WebFetch` từng URL để lấy **full JD**. Tôn trọng robots.txt/ToS. **Không** vượt anti-bot/CAPTCHA.
45
+ 2. Chỉ giữ job xem được nội dung đầy đủ (`extraction_confidence ≥ 0.5`) và **còn hạn ứng tuyển**
46
+ (bỏ nếu 404/410, "đã hết hạn / expired", deadline đã qua). Nếu JD ghi ngày đăng **≥ 1 tháng** → cũng loại.
47
+ 3. Bóc liên hệ nếu JD có → trường `contact` (email, phone, how_to_apply).
48
+ 4. Chuẩn hóa: áp skill `job-schema` để map schema; áp `bilingual-normalization` cho skills/location/lương;
49
+ tạo `id` hash ổn định; set `extraction_confidence` trung thực (full JD: 0.8-1.0);
50
+ thêm `source_platform = <platform>`.
51
+ 5. Ghi mảng job hợp lệ vào `out_path` (`data/jobs/<run-id>.<platform-slug>.json`).
52
+ Trả về: số job lấy được, số bị bỏ (chặn/lỗi/hết hạn) kèm URL để điều phối viên có thể bù.
53
+
54
+ ---
55
+
56
+ ## Nguyên tắc
57
+ - **Chỉ làm việc trong nền tảng được giao** — không lan sang site khác.
58
+ - **Chỉ giữ job xem được full JD & còn hạn**. Ưu tiên độ chính xác hơn số lượng.
59
+ - Không bịa trường dữ liệu (xem job-schema). Thiếu → unknown.
60
+ - `url` phải là link trang tuyển dụng — luôn có mặt cho mọi job.
61
+ - Không tự nộp hồ sơ / gửi tin nhắn thay người dùng.
@@ -13,12 +13,21 @@ Dùng `<run-id>` duy nhất theo mẫu `<candidate-slug>-<YYYYMMDD-HHMMSS>-<suff
13
13
  - Nếu `$ARGUMENTS` là file CV → parse và tạo `data/profiles/<slug>.json`.
14
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
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.
16
+ - **Hỏi số lượng job muốn thu thập** (`fetch_count`): người dùng nhập một số. Mặc định 20 nếu bỏ trống. **Giới hạn tối đa 20** — nếu nhập lớn hơn thì kẹp về 20; nếu ≤ 0 thì hỏi lại.
17
+ - **Hỏi danh sách nền tảng** (`platforms`): mặc định 5 nền tảng (`itviec.com`, `topcv.vn`, `vietnamworks.com`, `careerviet.vn`, `linkedin.com/jobs`). Người dùng có thể **thay bằng cách gửi link/domain nền tảng muốn crawl**; khi đó dùng đúng danh sách người dùng đưa (chuẩn hóa mỗi link về domain, khử trùng). **Giới hạn tối đa 7 nền tảng** — nếu đưa nhiều hơn thì giữ 7 cái đầu và báo cho người dùng.
18
+ - Tóm tắt cho người dùng xác nhận (kèm `fetch_count` và danh sách `platforms`).
17
19
 
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ệ.
20
+ ## Bước 2 — Collector (2 pha: search song song → chọn toàn cục → fetch song song)
21
+ Mục tiêu: **chỉ tốn tối đa `fetch_count` (≤20) lần fetch** nhưng ngân sách fetch được dồn cho các URL **tốt nhất trên toàn bộ nền tảng**, không chia đều — để không bỏ sót nền tảng giàu job.
22
+
23
+ **Pha 2a — Search (rẻ, song song).** Với mỗi nền tảng trong `platforms` (đã chốt ở Bước 1), **spawn một `job-collector` `mode=search` song song trong CÙNG một message**, truyền `fetch_count`. Mỗi cái chỉ `WebSearch` (không fetch) **lọc gọn ngay tại nguồn**: bỏ tin đăng **≥ 1 tháng**, bỏ link không phải JD / hết hạn / relevance thấp, cap top-`fetch_count` theo relevance, rồi ghi bản **gọn (KHÔNG snippet)** ra `data/jobs/<run-id>.<platform-slug>.candidates.json` (`{url, title, platform, relevance, posted_days}`).
24
+
25
+ **Pha 2b — Chọn toàn cục (điều phối viên, không spawn).** Đọc tất cả file `*.candidates.json` (chỉ là các dòng gọn, nhẹ token), gộp, khử trùng theo URL chuẩn hóa, **xếp hạng toàn cục theo `relevance` + `posted_days` (tin mới ưu tiên)** — KHÔNG giới hạn theo nền tảng. Chọn **top `fetch_count` URL** làm danh sách fetch (một nền tảng giàu job có thể chiếm phần lớn slot). Lấy dư một ít (buffer ~30%, nhưng tổng ≤ 20 + buffer) để bù URL hỏng/hết hạn.
26
+
27
+ **Pha 2c — Fetch (song song).** Nhóm danh sách URL đã chọn **theo nền tảng**; với mỗi nhóm không rỗng, **spawn một `job-collector` `mode=fetch` song song**, truyền `urls` của nhóm đó, ghi `data/jobs/<run-id>.<platform-slug>.json`. Mỗi cái chỉ fetch đúng URL được giao.
28
+
29
+ **Pha 2d — Gộp pool.** Đọc mọi file `data/jobs/<run-id>.<platform-slug>.json`, gộp, **khử trùng theo `id`/URL**, ghi `data/jobs/<run-id>.json`. Nếu sau khi bỏ job hỏng/hết hạn mà còn thiếu nhiều so với `fetch_count`, chọn thêm URL từ danh sách dự phòng (pha 2b) và fetch bù trước khi sang Bước 3. Xếp hạng/chọn 20 cuối cùng do Bước 3 (matcher) lo.
30
+ - Chuẩn thu thập không đổi: chỉ giữ job xem được full JD & còn hạn.
22
31
 
23
32
  ## Bước 3 — Matcher (subagent `job-matcher`)
24
33
  - Truyền `profile.json` + `data/jobs/<run-id>.json`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobscout",
3
- "version": "1.5.2",
3
+ "version": "1.6.3",
4
4
  "description": "AI job matching — tìm và xếp hạng việc làm phù hợp CV, song ngữ Việt–Anh. Chạy trên Claude Code và Codex CLI.",
5
5
  "bin": {
6
6
  "jobscout": "bin/cli.mjs"
@@ -22,6 +22,7 @@
22
22
  "email": "hieutrong512@gmail.com"
23
23
  },
24
24
  "license": "MIT",
25
+ "homepage": "https://github.com/hieutrong512/JobScout",
25
26
  "repository": {
26
27
  "type": "git",
27
28
  "url": "git+https://github.com/hieutrong512/JobScout.git"