AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Job Search

skill-ua-job-search-job-search-skill-job-search-skill · by ua-job-search

Automated job search and application across Ukrainian platforms with AI-powered cover letters

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-ua-job-search-job-search-skill-job-search-skill

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-ua-job-search-job-search-skill-job-search-skill)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Job Search? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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-search or /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:

  1. Salary range (min-max in USD or UAH)
  2. Work format (Remote / Hybrid / Office + city)
  3. 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
  1. 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:

  1. Formal and concise
  2. Professional but warmer
  3. 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.json
  • job-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.json
  • job-search-data/profile.md
  • job-search-data/tracking/state.json → get applied_urls to 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:

  1. Path to this skill file (for platform-specific instructions and scoring rubric)
  2. Path to profile.md
  3. Path to config.json
  4. List of applied URLs to skip
  5. Instruction to write results to job-search-data/tracking/results-{platform}.json

The agent must:

  • Search for ALL roles from search.roles AND search.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:

  1. Read all results-*.json files
  2. Merge, deduplicate (same company + similar title)
  3. Remove applied URLs
  4. Remove blacklisted
  5. Sort by match_percent descending
  6. 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:

  1. Navigate to URL
  2. Click page.locator('button:has-text("Відгукнутися")').first().click()
  3. 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:
  1. Take screenshot
  2. Read ALL visible questions/labels/placeholders
  3. Answer each based on profile.md (ONLY real facts)
  4. Fill cover letter textarea if present
  5. 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:

  1. Navigate to URL
  2. Click page.locator('button:has-text("Відгукнутися")').first().click()
  3. Wait 2s
  4. Click cover letter checkbox: page.locator('label:has-text("супровідний")').click()
  5. Wait 1.5s
  6. Fill textarea: page.locator('textarea').first().fill(coverLetter)
  7. Verify text wasn't truncated
  8. Click submit: page.locator('button[type="submit"]:has-text("Надіслати")').click()
  9. If Step 2 appears → click page.locator('button:has-text("Відгукнутися")').first().click()
  10. 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.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.