cv-2026/.claude/skills/get-tg-jobs/SKILL.md

255 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: get-tg-jobs
description: Full autonomous end-to-end job pipeline. Fetches fresh vacancies from Oleg's Telegram "Jobs" folder, handles new/pending channels, runs the full triage cycle (triage-jobs), pushes APPLY verdicts to Trello, then reports results in the chat session AND sends a Telegram notification to Oleg via telegram-helper. Trigger on "забери из Jobs", "что нового в каналах", "запусти pipeline", or similar.
---
# get-tg-jobs
Autonomous end-to-end pipeline: fetch → curate new channels → triage → Trello → report. Does everything without stopping for confirmation. Only halts if a hard error occurs (auth failure, script crash, etc.).
**Trigger only when Oleg explicitly asks** — e.g. "забери свежее из Jobs", "что нового в каналах", "запусти pipeline". No background polling.
---
## Notification targets
All pipeline reports go to the **"New cool job"** Telegram group. The `telegram-helper` account (samuishechka) is a member of that group and sends messages there.
| Target | chat_id | When |
|---|---|---|
| "New cool job" group | `-4783500394` | All reports — success and errors |
Tool to use: `mcp__telegram-helper__send_message(chat_id=-4783500394, message="...", parse_mode="html")`
This section is the canonical reference for notification targets. Update here if the group changes.
---
## Step 1 — Run the pipeline
```bash
~/.local/bin/uv run scripts/list_telegram_channels.py \
| ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
```
Run from the project root. The script prints a per-channel summary to stdout — echo it to the session so Oleg can see raw progress.
**If the script fails:**
- `TELEGRAM_SESSION_STRING not set` or auth error → `.env` is missing or the session is expired. Regenerate with `scripts/regen_telegram_session.sh` and tell Oleg.
- `TELEGRAM_API_ID / TELEGRAM_API_HASH not set` → same `.env` issue.
- `Could not find folder "Jobs"` → the folder was renamed or the wrong session is in `.env`. Must be the **usulsu** session, not samuishechka.
- Any other exception → paste the error and stop. Don't retry blindly.
---
## Step 2 — Check for pending (new) channels
```bash
test -f tracking/telegram_pending_channels.json && echo PENDING || echo OK
```
**If OK**: skip to Step 3.
**If PENDING**: curate each new channel using the procedure below, then rerun Step 1.
---
### New-channel curation procedure
For each channel in `telegram_pending_channels.json`:
#### 2a — Read keyword frequency scan
```bash
jq -r 'to_entries[] | "\n=== \(.key) ===\nmessages_scanned: \(.value.messages_scanned)\ntruncated: \(.value.truncated)\ntop keywords:\n\(.value.keyword_counts_from_other_channels | to_entries | sort_by(-.value) | .[:20] | .[] | " \(.key): \(.value)")"' \
tracking/telegram_pending_channels.json
```
#### 2b — Sample messages
```bash
jq -r '.channels["<channel>"].messages[:6] | .[] | "── \(.date[0:16])\n\(.text[0:400])\n"' \
tracking/telegram_inbox.json
```
Look for: hashtag patterns, language, post structure (single role / digest / chat), recurring noise.
#### 2c — Decide lang, priority, filter shape
**Priority:**
- `p1` — strong stack hits (JS/TS/React/Node/AI/LLM) AND global-remote culture
- `p2` — stack OK but CIS/RUB market, or occasional gems; recruiter/market-intel content
- `p3` — wrong stack, off-market, noise, dead channels
Base priority on the **best** vacancy in the sample, not the average.
**Filter shape:**
| Channel pattern | Filter |
|---|---|
| Consistent `#vacancy` + `#remote` hashtags | AND-of-OR hashtag filter |
| Vacancy text without consistent hashtags | Positive stack include + Oleg-stack excludes |
| Low-volume personal/curated digest | Trust-all (no `include`/`exclude`) |
| Mixes resumes and vacancies | Trust-all |
| Mostly noise but worth keeping | Strict positive filter |
Standard Oleg-stack excludes: `["kafka", "golang", "kotlin", "android", "swift"]`
For `*_jobs` channels add resume excludes: `["#резюме", "#resume", "#cv", "#ищуработу"]`
Positive stack include: `["javascript", "typescript", " react", "node.js", "nodejs", "fullstack", "full-stack", "tech lead", "techlead", "ai engineer", " llm"]`
#### 2d — Add entry to telegram_channels.json
Insert in correct priority block (p1 → p2 → p3, alphabetically within each group):
```jsonc
"<channel_id>": {
"lang": "ru" | "en" | "mixed",
"priority": "p1" | "p2" | "p3",
"include": [["kw1","kw2"], ["kw3"]], // optional
"exclude": ["kw4", "kw5"] // optional
}
```
#### 2e — Rerun and confirm clean
```bash
~/.local/bin/uv run scripts/list_telegram_channels.py \
| ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
test -f tracking/telegram_pending_channels.json && echo "STILL PENDING" || echo "Clean"
```
#### 2f — Validate the filter
```bash
jq -r '.channels["<channel>"] | "kept \(.kept)/\(.seen)" + (.messages | map("\n── \(.date[0:16])\n\(.text[0:300])") | join(""))' \
tracking/telegram_inbox.json
```
If `kept == 0` and a filter is set — verify the filter isn't too strict before accepting zero as correct.
---
## Step 3 — Run full triage
Follow the complete `triage-jobs` skill procedure on `tracking/telegram_inbox.json`.
Key points from that skill:
1. **Pre-step**: build the `known_applied` list from **Trello** (the source of truth) before spawning Haiku. Fetch the vacancy-bearing lists and keep **only card titles**:
```
mcp__trello__set_active_board(boardId="6a1a9a5af082cb0526b22704")
mcp__trello__get_cards_by_list_id(listId="6a1aa59555aab72a261c42aa") # TODO
mcp__trello__get_cards_by_list_id(listId="6a1aa59a5e7e651b1352895b") # In Progress
mcp__trello__get_cards_by_list_id(listId="6a267e2e9ed02737346dc047") # In touch
mcp__trello__get_cards_by_list_id(listId="6a1aa5a1d8bcb0ed7234987b") # Applyed
mcp__trello__get_cards_by_list_id(listId="6a245a328452976c0d1fcca1") # Rejected
```
Distill to titles only, then pass that list into every Haiku subagent prompt. See the triage-jobs batch pre-step for the context-safety rules and the REST fallback when the board grows. `tracking/applications.md` is frozen/legacy — don't grep it.
2. **Stratify by priority**: all p1, p2, p3 go through Haiku first pass. Main session reviews only APPLY and VERIFY verdicts from Haiku. SKIP verdicts from Haiku accepted without re-review.
3. **VERIFY verdicts**: run a fast `WebSearch "{company} remote hiring countries"` to resolve geo, then apply Gate 1.
4. **APPLY verdicts**: for every confirmed APPLY — create a Trello card in the TODO column (`6a1aa59555aab72a261c42aa`) on board `6a1a9a5af082cb0526b22704`. The card **is** the application record (replaces the old applications.md row): title `Company — Role (Track X)`, description = verdict summary + a `Tracking` footer (date added, channel, CV used, JD link, status). See the triage-jobs skill ("After triage — update tracking") for the exact card schema. Do not write to `tracking/applications.md` (frozen/legacy).
5. **QUICK-APPLY verdicts**: note them explicitly in the session report; do not auto-add to Trello. Ask Oleg if he wants to proceed.
Full triage playbook: `base/reference/vacancy-filter-and-triage-2026.md`. Full batch instructions: `.claude/skills/triage-jobs/SKILL.md`.
---
## Step 4 — Report in session
After triage is complete, output a consolidated summary to the chat:
```
Pipeline done — <date>
Fetch: N messages from X channels (Y with results)
[⚠️ Truncated: channel_name (kept K/500, p2/ru)]
Triage results:
✅ APPLY: N roles → added to Trello
🔍 VERIFY: N roles → resolved / still open
⚡ QUICK-APPLY: N roles (ask Oleg before adding)
❌ SKIP: N roles
APPLY roles:
- Company A — Role Title (Track A) → [Trello card]
- Company B — Role Title (Track B) → [Trello card]
New channels curated: N [or: none]
```
---
## Step 5 — Send Telegram notification
After the session report, send a summary to the **"New cool job"** group via telegram-helper.
### Success message format
```
mcp__telegram-helper__send_message(
chat_id=-4783500394,
parse_mode="html",
message="""<b>Jobs pipeline done</b> · 2026-06-07
✅ APPLY: N роли → Trello
🔍 VERIFY: N (нужен ресёрч)
⚡ Quick-apply: N (нужно ок)
❌ SKIP: N
📥 Всего: N сообщений из X каналов
<b>APPLY:</b>
<a href="https://trello.com/c/...">Company A — Role (Track A)</a>
<a href="https://trello.com/c/...">Company B — Role (Track B)</a>"""
)
```
Rules:
- Always include Trello links for APPLY roles — это главная ценность уведомления.
- If QUICK-APPLY roles exist, list them with direct JD links (not Trello — для быстрого решения):
```
⚡ Quick-apply (нужно ок):
Company X — Role → wantapply.com/...
Company Y — Role → linkedin.com/jobs/...
```
- Keep VERIFY as a count only (no list) — they need more research, not immediate action.
- If zero APPLY and zero QUICK-APPLY: still send the message — "всё просмотрено, ничего релевантного в этот раз".
### Error message format
If the pipeline failed at any step and cannot complete — send a short error notification to the **same group**:
```
mcp__telegram-helper__send_message(
chat_id=-4783500394,
parse_mode="html",
message="""<b>⚠️ Pipeline error</b> · 2026-06-07
Упало на шаге: <step name>
Причина: <one-line error summary>
Что сделано до ошибки: <brief — e.g. "fetch OK, 12 msgs, triage не запустился">
Что нужно: <action — e.g. "обнови TELEGRAM_SESSION_STRING в .env">"""
)
```
Send the error notification **even if only partial work was done**. Better to have an incomplete notification than silence when something breaks.
---
## Sanity notes
- `telegram_state.json` is not in git — per-machine cursor. If lost, next run fetches 30 days back per channel (slow but correct).
- To reset a single channel's cursor for filter re-validation:
```bash
jq 'del(.["<channel>"])' tracking/telegram_state.json > /tmp/s.json && mv /tmp/s.json tracking/telegram_state.json
```
- `telegram_inbox.json` is overwritten every run — it's the current snapshot only.
- The pipeline reads the live "Jobs" folder — new channels Oleg added in Telegram are automatically included in the next run.