Install
$ agentstack add skill-ua-job-search-job-search-skill-job-search-skill ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Job Search Automation
Automated job search across Ukrainian platforms (Djinni, Work.ua, Robota.ua). Scans vacancies, scores match against your profile, generates personalized cover letters, auto-applies.
Commands
/job-searchor/job-search setup— First-time onboarding (required before first use)/job-search scan— Scan all enabled platforms, score vacancies, show results/job-search apply— Scan + auto-apply to matches above threshold/job-search apply djinni— Apply only on Djinni/job-search apply work.ua— Apply only on Work.ua/job-search apply robota.ua— Apply only on Robota.ua/job-search status— Show run history, stats, applied count
Data Directory
All data is stored in {project_root}/job-search-data/:
job-search-data/
├── profile.md # Candidate profile (created during onboarding)
├── config.json # Search config, scoring, blacklist, credentials
├── cv.pdf # Copy of the user's CV
├── tone-samples.md # Approved cover letter tone/examples
├── tracking/
│ ├── applications.xlsx # Tracking spreadsheet
│ ├── state.json # Run history, applied URLs, stats
│ └── results-*.json # Per-platform scan results
If job-search-data/config.json does not exist, run onboarding automatically.
PHASE 0: DISCLAIMER
ALWAYS show this disclaimer on the very first run (before onboarding). Check if job-search-data/.disclaimer-accepted exists. If not — show the disclaimer and wait for acceptance.
Display this text to the user:
Перед початком роботи, будь ласка, прочитай:
1. Підготовка профілів
Перед використанням цього скіла переконайся, що твої профілі на платформах пошуку роботи (Djinni, Work.ua, Robota.ua тощо) повністю заповнені та готові до відгуків:
- Актуальне резюме завантажене на платформу
- Контактні дані (email, телефон) вказані коректно
- Фото профілю додане (якщо платформа підтримує)
- Досвід роботи, навички, освіта — заповнені
- Профіль опублікований і активний
Це необхідно, щоб при автоматичному відгуку платформа не блокувала заявки через незаповнений профіль і AI не стикався з помилками при відправці.
2. Відповідальність
- Автор цього скіла (Stanislav Marynovych) не несе жодної відповідальності за збереження, передачу чи втрату ваших персональних даних, паролів та будь-якої іншої інформації, яку ви надаєте під час використання скіла.
- Паролі зберігаються локально в macOS Keychain на вашому пристрої. Автор не має доступу до ваших даних.
- AI-моделі можуть галюцинувати — вигадувати або спотворювати факти у супровідних листах, відповідях на питання, та іншому згенерованому контенті. Скіл має механізми захисту від цього, але повна гарантія неможлива.
- Вся відповідальність за зміст відгуків, супровідних листів, відповідей на питання роботодавців та за те, на які вакансії відгукується скіл — лежить виключно на користувачеві.
- Рекомендуємо переглядати згенеровані супровідні листи перед першими відгуками (режим
/job-search scan), щоб переконатися у якості та точності контенту.
Use AskUserQuestion: > Чи приймаєш ці умови?
Options:
- "Так, приймаю — продовжуємо" — proceed to onboarding
- "Ні, не приймаю" — say "Зрозуміло. Скілл не може працювати без прийняття цих умов." and stop.
If accepted, create the marker file:
touch job-search-data/.disclaimer-accepted
PHASE 1: ONBOARDING
Run this when /job-search or /job-search setup is invoked, OR when config.json doesn't exist. Disclaimer (Phase 0) must be accepted before onboarding starts.
Onboarding is a conversation. Ask questions one at a time using AskUserQuestion where appropriate, but prefer open-ended questions as plain text for richer answers.
Step 1: LinkedIn Profile
Say: > Для початку надішли, будь ласка, посилання на свій LinkedIn-профіль. > Якщо немає LinkedIn — просто скажи, перейдемо до наступного кроку.
If the user provides a LinkedIn URL:
- Use WebFetch to scrape the public profile (or ask the user to copy-paste the text)
- Extract: name, headline, experience, education, skills, certifications
- Save raw text to
job-search-data/linkedin-raw.txt
If no LinkedIn — skip, rely on CV and self-description.
Step 2: Resume / CV
Say: > Тепер надішли своє резюме (PDF, DOCX або HTML). Якщо є версія з фото — краще її.
- Read the file, extract all text
- Copy the file to
job-search-data/cv.pdf(or cv.docx/cv.html) - Parse: experience, education, skills, contact info
Step 3: Tell Me About Yourself
Say: > Розкажи про себе своїми словами. Що реально робиш на поточній роботі? Який у тебе рівень англійської — чесно? Що вважаєш своїми сильними сторонами? Де відчуваєш, що досвіду ще не вистачає? > > Мені важливо мати реальну картину, а не маркетингову версію з CV. Це допоможе точніше підбирати вакансії і не відгукуватися на те, де тебе відсіють.
Listen to the user's response. This is critical for honest assessment.
Step 4: Realistic Level Assessment
Based on LinkedIn + CV + self-description, write an honest assessment:
- For each skill area: Junior / Junior+ / Middle / Senior
- Note gaps between CV claims and reality
- Identify real competitive advantages
- Determine honest English level
- Estimate market value range (USD/month)
Show the assessment to the user and ask: > Ось моя оцінка твого рівня. Згоден? Щось хочеш скоригувати?
Step 5: Target Roles
Ask: > На які позиції хочеш шукати роботу?
Use AskUserQuestion with common options based on the profile, allow multi-select.
Then automatically research adjacent roles. For example:
- "Product Manager" → also search: Product Owner, Product Marketing Manager, Growth PM, AI Product Manager
- "Project Manager" → also search: Project Coordinator, Delivery Manager, Program Manager, Scrum Master
- "Business Analyst" → also search: Systems Analyst, Requirements Engineer, Product Analyst
- "Developer" → also search: Tech Lead, Full Stack, relevant framework-specific titles
Tell the user: > На основі твоїх ролей я також буду шукати суміжні позиції: {list}. Додати чи прибрати щось?
Step 6: Target Industries / Domains
Say: > В яких сферах/доменах ти хочеш працювати? Вибери зі списку або напиши свої.
Use AskUserQuestion with multi-select. Suggest domains based on the user's experience from Steps 1-4, but allow any choice:
Common IT domains:
- SaaS / B2B Software
- FinTech / Banking
- MedTech / HealthTech
- EdTech
- Legal Tech
- E-commerce / Marketplace
- MarTech / AdTech
- AgriTech
- Logistics / Supply Chain
- IoT / Hardware
- AI / ML
- Cybersecurity
- Gaming (non-gambling)
- Media / Content
- HR Tech / Recruiting
Non-IT domains:
- Consulting / Professional Services
- Manufacturing / Industrial
- Real Estate
- FMCG / Retail
- Telecom
- Energy / Utilities
- Non-profit / Social Impact
- Government / Public Sector
Also ask: > Чи є домени, які точно НЕ цікавлять? (окрім тих, що вже в blacklist)
Add unwanted domains to the blacklist in config.
How this affects scoring: The Industry Match scoring criterion (10% weight) now uses the user's selected domains:
- Selected domain = 100
- Adjacent/related domain = 60
- Not selected but not blacklisted = 30
- Blacklisted = 0 (filtered out entirely)
Save to config:
{
"search": {
"preferred_domains": ["SaaS", "Legal Tech", "Agency", "Startup"],
"excluded_domains": ["AdTech", "Telecom", "Banking"]
}
}
Step 7: Search Parameters
Use AskUserQuestion for:
- Salary range (min-max in USD or UAH)
- Work format (Remote / Hybrid / Office + city)
- Auto-apply threshold — explain what different percentages mean practically:
- 60% = wide net, some weak matches
- 70% = balanced, good quality
- 80% = only strong matches, fewer applications
- Blacklist — industries to never apply to (suggest: iGaming, gambling, crypto, betting, forex, adult; let user add/remove)
Step 8: Job Platforms
Ask: > Які платформи використовувати для пошуку?
Options (multi-select):
- Djinni (основна IT-платформа України)
- Work.ua (найбільший job board)
- Robota.ua (другий за розміром)
- LinkedIn (тільки скан, без автовідгуку — ризик бану)
For each selected platform, ask for credentials:
- Djinni: email + password → save password to macOS Keychain via
security add-generic-password -s "job-search-djinni" -a "{email}" -w '{password}' -U - Work.ua: email only (auth via email code, user provides code during apply)
- Robota.ua: email + password → save to Keychain
- LinkedIn: no credentials needed (scan only, uses Chrome session)
NEVER store passwords in files. Only in macOS Keychain.
Step 9: Cover Letter Tone
Generate 3 sample cover letters for a hypothetical vacancy matching the user's profile. Show all 3 with different tones:
- Formal and concise
- Professional but warmer
- Casual-professional
Ask: > Який тон тобі підходить? Або скажи що змінити.
Iterate until the user approves. Save approved examples to job-search-data/tone-samples.md.
Step 10: Build Profile
Compile everything into job-search-data/profile.md:
- Contact info
- Professional summary (from their own words, not CV marketing)
- Realistic level assessment
- Current and previous experience (with honest descriptions)
- Key skills for matching
- Achievements
- Anti-preferences
Save config to job-search-data/config.json:
{
"candidate": {
"name": "...",
"location": "...",
"cv_path": "job-search-data/cv.pdf",
"profile_path": "job-search-data/profile.md"
},
"search": {
"roles": ["Product Manager", "Project Manager"],
"adjacent_roles": ["Product Owner", "Product Marketing Manager", "Project Coordinator"],
"experience_years_real": 1,
"location_preferences": ["Remote", "Lviv"],
"format": ["remote", "hybrid", "office"],
"salary": { "min_usd": 1000, "max_usd": 2500 },
"preferred_domains": ["SaaS", "Legal Tech", "Agency", "Startup", "MedTech"],
"excluded_domains": ["AdTech", "Telecom"]
},
"scoring": { "auto_apply_threshold": 70 },
"blacklist": { "industries": ["iGaming", "gambling", "crypto"], "companies": [] },
"platforms": {
"djinni": { "enabled": true, "auto_apply": true, "login_email": "..." },
"work_ua": { "enabled": true, "auto_apply": true, "login_email": "...", "auth_method": "email_code" },
"robota_ua": { "enabled": false },
"linkedin": { "enabled": false, "auto_apply": false }
},
"cover_letter": {
"max_words_djinni": 150,
"max_words_work_ua": 200,
"language": "auto",
"linkedin_link": true
}
}
Create empty tracking files:
job-search-data/tracking/state.jsonjob-search-data/tracking/applications.xlsx(with headers)
Step 11: Onboarding Summary & Confirmation
Before finishing, show the user a full summary of everything collected. Format as a structured analysis:
=== ПІДСУМОК ОНБОРДИНГУ ===
ПРОФІЛЬ:
Ім'я: {name}
Локація: {location}
Англійська: {level}
CV: {filename}
ОЦІНКА РІВНЯ:
{skill_area_1}: {Junior/Middle/Senior}
{skill_area_2}: {Junior/Middle/Senior}
...
Ринкова вартість: ${min}-${max}/міс
ЦІЛЬОВІ ПОЗИЦІЇ:
Основні: {roles}
Суміжні: {adjacent_roles}
ДОМЕНИ:
Бажані: {preferred_domains}
Виключені: {excluded_domains}
ПАРАМЕТРИ ПОШУКУ:
Зарплата: ${min}-${max}
Формат: {formats}
Поріг автовідгуку: {threshold}%
Blacklist: {industries}
ПЛАТФОРМИ:
{platform_1}: {enabled/disabled} — {auth_method}
{platform_2}: {enabled/disabled} — {auth_method}
...
ТОН ЛИСТІВ: {description of approved tone}
COVER LETTER ПРИКЛАД:
{one approved sample}
Ask: > Ось повний підсумок. Все правильно? Що хочеш змінити?
Iterate until user confirms. Only then save files and finish.
Say: > Онбординг завершено. Тепер можеш запускати /job-search scan для пошуку або /job-search apply для автовідгуку.
PHASE 2: SCANNING
Step 1: Load Data
Read:
job-search-data/config.jsonjob-search-data/profile.mdjob-search-data/tracking/state.json→ getapplied_urlsto skip duplicates
Step 2: Spawn Parallel Agents
For each enabled platform, spawn a sub-agent using the Agent tool. Send ALL agents in a SINGLE message for parallel execution.
Each agent's prompt must include:
- Path to this skill file (for platform-specific instructions and scoring rubric)
- Path to profile.md
- Path to config.json
- List of applied URLs to skip
- Instruction to write results to
job-search-data/tracking/results-{platform}.json
The agent must:
- Search for ALL roles from
search.rolesANDsearch.adjacent_roles - Use WebSearch and WebFetch to find and analyze listings
- Score each vacancy using the scoring rubric (see "Scoring" section)
- Generate cover letters for matches above threshold
- Write results as JSON
Step 3: Merge Results
After all agents complete:
- Read all
results-*.jsonfiles - Merge, deduplicate (same company + similar title)
- Remove applied URLs
- Remove blacklisted
- Sort by match_percent descending
- Show summary to user
PHASE 3: APPLYING
Browser Setup
Chrome MUST be closed. Ask the user to close it.
Use Playwright with the user's Chrome profile:
const { chromium } = require(PLAYWRIGHT_PATH);
const browser = await chromium.launchPersistentContext(
'/Users/{user}/Library/Application Support/Google/Chrome/Default',
{ headless: false, channel: 'chrome',
args: ['--disable-blink-features=AutomationControlled'],
viewport: { width: 1280, height: 900 } }
);
Detect Playwright path: check ~/.npm/_npx/*/node_modules/playwright or node_modules/playwright.
Platform Login
Djinni:
- Get password:
security find-generic-password -s "job-search-djinni" -w - Navigate to
https://djinni.co/login - Use
page.fill()for email and password (not JS evaluate) - Click submit via
page.locator('button:has-text("Увійти")').click() - Verify login by checking URL doesn't contain "login"
Work.ua:
- Navigate to
https://www.work.ua/jobseeker/login/ - Click "Увійти за допомогою ел. пошти" via
page.locator() - Fill email via
page.fill() - Click submit
- Ask user for email code: "Work.ua надіслав код на пошту. Введи код:"
- Wait for user, enter code, submit
Apply Flow — Djinni
For each vacancy:
- Navigate to URL
- Click
page.locator('button:has-text("Відгукнутися")').first().click() - Wait 2s
Check for special form (IMPORTANT): After clicking, check what appeared:
const hasTextarea = await page.$('textarea');
const formInputs = await page.$$('input:not([type="hidden"]), select, textarea');
- If ONLY textarea → simple flow: fill cover letter, submit
- If MULTIPLE form inputs → special form flow:
- Take screenshot
- Read ALL visible questions/labels/placeholders
- Answer each based on profile.md (ONLY real facts)
- Fill cover letter textarea if present
- Submit
Simple flow:
- Fill textarea with cover letter via
page.locator('textarea').fill() - Verify text wasn't truncated: compare
await textarea.inputValue()length with original - If truncated → shorten cover letter to 2 bullet points, re-fill
- Click submit:
page.locator('button:has-text("Надіслати відгук")').click() - Wait 3s, screenshot
Between applications: random delay 3-7 seconds to avoid rate limiting.
Apply Flow — Work.ua
For each vacancy:
- Navigate to URL
- Click
page.locator('button:has-text("Відгукнутися")').first().click() - Wait 2s
- Click cover letter checkbox:
page.locator('label:has-text("супровідний")').click() - Wait 1.5s
- Fill textarea:
page.locator('textarea').first().fill(coverLetter) - Verify text wasn't truncated
- Click submit:
page.locator('button[type="submit"]:has-text("Надіслати")').click() - If Step 2 appears → click
page.locator('button:has-text("Відгукнутися")').first().click() - Wait 3s, screenshot
Work.ua custom questionnaires (IMPORTANT): Work.ua vacancies can also have custom
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ua-job-search
- Source: ua-job-search/job-search-skill
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.