Install
$ agentstack add skill-galiprandi-job-seeker-targets ✓ 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 No
- ✓ 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
Targets — Active direct sourcing
Trigger
Keyword: targets
The user says targets (or variants: "register on companies", "apply to target companies", "company direct", "direct sourcing") and the full registration + application flow is triggered.
Purpose
The third sourcing pillar alongside radar (passive alerts) and apply (LinkedIn Easy Apply). This flow goes directly to the career sites of the 40 target companies (19 LATAM + 21 Argentina), registers the user, creates a standout profile, and applies to matching positions.
Pre-flight
- [ ] Browser: always use
node scripts/browser.jsfor open/close/goto. See AGENTS.md "Browser session" and "Parallel execution" for details. Never callplaywright-cli opendirectly - [ ] Parallel execution: if running alongside other flows (e.g:
applyornews), attach a session withnode scripts/browser.js attach --session targets-1and pass--session targets-1to all browser commands and scripts. Usedetachwhen done (neverclose— it's ref-counted) - [ ] Load active preferences (see
memoryskill):
``bash node scripts/db.js "SELECT category, key, value, confidence, source FROM preferences WHERE user_id = 1 AND status = 'active' ORDER BY category, key" ``
- [ ] Load strategy (see AGENTS.md "Strategy levels"):
``bash node scripts/db.js "SELECT data->'strategy' AS strategy FROM users WHERE id = 1" ` Respect: targetsbatchsize (max companies per session, 0 = don't run, "all" = no limit), matchthreshold, relaxmusthaves. If targets is not in sourcesactive`, skip this flow entirely
- [ ] Load profile, job preferences, CV and photo paths:
``bash node scripts/db.js "SELECT data->'profile' AS profile, data->'job_preferences' AS prefs, data->'cv_path' AS cv_path, data->'photo_path' AS photo_path, data->'style_profile' AS style_profile FROM users WHERE id = 1" ``
- [ ] Load company registrations to see current state:
``bash node scripts/db.js "SELECT id, company, region, sector, careers_url, ats_platform, registration_status, profile_completed, applied_jobs_count, notes FROM company_registrations WHERE user_id = 1 ORDER BY registration_status, region, company" ``
- [ ] Load existing applications for dedup:
``bash node scripts/db.js "SELECT company, url FROM applications WHERE user_id = 1" ``
Must-haves filter (from job_preferences)
Before applying to any job, verify against Must-haves from users.data.job_preferences. Discard if:
- Not remote (hybrid with mandatory office days = discard, unless
relax_must_havesincludesremote) - Doesn't match
ai_focuspreference (ifai_focus.weight = Must, role must involve the user's AI role types) - Junior/Intern level (per
deal_breakers) - In
industries_avoidlist (perindustries_avoid.weight) - Salary below
job_preferences.salary.value.min(if mentioned andsalary.weight = Must) - Not in user's accepted locations (per
locationandtimezonespreferences)
Gold Rule 4: Manager role is highly valued but sacrificable if the pay and project are interesting enough. Don't discard IC roles automatically if the AI focus and compensation are strong. Check relax_must_haves for manager.
Flow
Phase 1 — Registration & profile creation
For each company with registration_status = 'pending' (or profile_completed = false):
- Navigate to careers URL using
node scripts/browser.js goto(oropenif no session) - Detect ATS if not already identified. Common signals:
- URL contains
greenhouse.io→ Greenhouse - URL contains
lever.co→ Lever - URL contains
ashbyhq.com→ Ashby - URL contains
workday→ Workday - URL contains
smartrecruiters.com→ SmartRecruiters - URL contains
teamtailor.com→ Teamtailor - URL contains
eightfold.ai→ Eightfold - URL contains
successfactors→ SAP SuccessFactors - URL contains
workable.com→ Workable - URL contains
phenom→ Phenom - URL contains
attrax→ Attrax - URL contains
gupy→ Gupy - None of the above → Custom (manual inspection needed)
- Find the registration / sign up / create account page
- Login method priority:
- Google login (preferred, reuses Gmail session from onboarding)
- LinkedIn login (reuses LinkedIn session from onboarding)
- Email + password (create account, save credentials to
company_registrations.dataas{email, password}) - If no account creation possible → mark
registration_status = 'manual_login_needed', notify user (Gold Rule 5)
- Complete profile to make it stand out:
- Full name (from
users.data.profile.full_name) - Title/headline: use
profile.title+ top skills (e.g: "Software Engineer | AI Strategy & Agent-First Workflows") - Summary/bio: use
profile.summary, adapted to the platform's character limit - Location: from
users.data.personal_info(city, country) orform_answers.location - Upload CV: use
profile.cv_pathorpersonal_info.cv_pdf_path(from DB, never hardcoded) - Upload photo: use
users.data.photo_pathif the platform accepts it - Skills: add all from
profile.skills - Experience: fill from
profile.experienceif the platform has structured fields - Languages: from
profile.languages - Work preferences: from
job_preferences.modalities,job_preferences.role_types - Salary expectation: from
job_preferences.salary.value(only if field is required)
- Update DB after each company:
``bash node scripts/db.js "UPDATE company_registrations SET registration_status = 'registered', profile_completed = true, ats_platform = '', login_method = '', profile_url = '', last_visit_at = NOW(), updated_at = NOW(), data = ''::jsonb WHERE id = " --write ``
- Subscribe to job alerts if the ATS or career site supports it. This is critical for passive monitoring of new openings:
- Greenhouse: look for "Subscribe to alerts" or "Email me new jobs" link on the job board. Enter email, select job categories or keywords (AI, Engineering, Remote)
- Lever: look for "Subscribe" or "Alerts" button. Enter email, select departments or locations
- Ashby: look for "Subscribe to alerts" link. Enter email, select teams or locations
- Workable: look for "Subscribe" or "Job Alerts" link. Enter email, select job categories
- SmartRecruiters: look for "Job Alerts" or "Subscribe" link. Enter email, select filters
- Teamtailor: look for "Subscribe" or "Alerts" link. Enter email, select categories
- Eightfold: AI matching may auto-suggest relevant jobs. Look for "Save search" or "Alerts" option
- Workday: often has " Save Search" or "Job Alerts" after searching. Set up with filters
- Custom ATS: look for any "Subscribe", "Alerts", "Notify me", or RSS feed icon. If none exists, skip this step and note it in
company_registrations.notes - Keywords for alerts: derived from
profile.skills,profile.title, andjob_preferences(e.g: AI, the user's primary role, seniority level, key tech skills) - Frequency: daily if available, weekly otherwise
- Email: use the user's email (from
users.data.profile.email) so alerts route through the Gmail filter - Update DB with alert status:
``bash node scripts/db.js "UPDATE company_registrations SET data = jsonb_set(COALESCE(data, '{}'::jsonb), '{alerts_subscribed}', 'true'::jsonb), notes = COALESCE(notes, '') || ' | Alerts subscribed' WHERE id = " --write ``
- If alerts not available on the platform, note it:
``bash node scripts/db.js "UPDATE company_registrations SET notes = COALESCE(notes, '') || ' | No native alerts' WHERE id = " --write ``
If a company has no matching roles or no remote options:
- Mark
registration_status = 'no_fit'with reason innotes - Do not register
If a company requires manual login (no Google/LinkedIn, no email signup):
- Mark
registration_status = 'manual_login_needed' - Open headed browser (Gold Rule 5), notify user, wait for confirmation
Phase 2 — Search & apply
For each company with registration_status = 'registered' and applied_jobs_count = 0 (or user requests more):
- Navigate to the company's job board
- Search with filters:
- Keywords: derived from
profile.title,profile.skills, andjob_preferences(the user's primary role + key skills + seniority level) - Location: from
job_preferences.locationandjob_preferences.timezones - Seniority: from
job_preferences.seniority
- Filter by Must-haves (see above). For each matching job:
- Check dedup against
applicationstable - If already applied → skip
- Warm Sourcing & Referral Pre-Check (Strategy #1 & #4):
- Gate: only run if
referralsis instrategy.sources_active. If not, skip to step 5. - Run
node scripts/linkedin-warm-sourcing.js --company "" --role "" --json - If internal contact/alumni/ex-colleague found: stage referral request in
messagesand set card status todiscovered(Strategy #1). - If NO internal contact found: if
strategy.cold_outreach = true, extract recruiter info and stage recruiter outreach DM inmessages(Strategy #4). Ifcold_outreach = false, skip outreach. Either way, micro-align CV keywords to JD viascripts/generate-cv.jsand proceed to ATS application.
- Apply following the ATS-specific flow (see ATS guide below)
- Register each application in DB:
``bash node scripts/db.js "INSERT INTO applications (user_id, platform, company, role, url, status, data) VALUES (1, '', '', '', '', 'applied', ''::jsonb)" --write ``
- Update company registration:
``bash node scripts/db.js "UPDATE company_registrations SET applied_jobs_count = applied_jobs_count + , last_applied_at = NOW(), updated_at = NOW() WHERE id = " --write ``
Phase 3 — Report
Present to user:
## Targets report
### Registration summary
- Registered: X/40 companies
- Profile completed: X/40
- No fit (no remote/Argentina/tech): X
- Manual login needed: X
### Applications summary
- Total applications via targets: X
- By company:
| Company | Applied | Roles |
|---|---|---|
| Mercado Libre | 3 | Sr EM AI, AI Architect, Platform Eng |
| Bitso | 2 | Sr EM, AI Engineer |
...
### Pending (need attention)
- [manual_login_needed] Santander Tecnología — requires manual login
- [no_fit] Nubank — all roles hybrid, no 100% remote
ATS-specific application guide
Greenhouse
- URL pattern:
job-boards.greenhouse.io/orboards.greenhouse.io/ - "Apply" button → form with personal info, resume upload, custom questions
- Resume: upload CV file directly
- Questions: answer based on profile (years of experience, salary, location, work authorization)
- Submit → confirmation page
Lever
- URL pattern:
jobs.lever.co/ - "Apply for this job" → form with name, email, phone, resume, links (GitHub, LinkedIn, portfolio)
- Resume: upload CV file
- Custom questions: answer from profile
- Submit → email confirmation
Ashby
- URL pattern:
jobs.ashbyhq.com/ - "Apply" → multi-step form (personal info, experience, resume, questions)
- Resume: upload CV file
- May require cover letter — use a brief, tailored version from profile summary
- Submit → confirmation
Workday
- URL pattern:
mywd..comor.wd1.myworkdayjobs.com - Notoriously difficult to automate. Dynamic forms, anti-bot measures
- If automation fails after 3 attempts → mark company as
manual_apply_needed, notify user - Create account first, then search and apply
SmartRecruiters
- URL pattern:
careers.smartrecruiters.com/ - "Apply now" → form with personal info, resume, questions
- Resume: upload CV file
- May offer Google/LinkedIn prefill
- Submit → confirmation
Teamtailor
- URL pattern:
.teamtailor.comor.na.teamtailor.com - "Apply" → form with personal info, CV, cover letter (optional)
- Resume: upload CV file
- Submit → confirmation
Eightfold
- URL pattern:
.eightfold.ai/careers - AI-powered matching. May suggest roles based on profile
- "Apply" → form, may offer LinkedIn import
- Resume: upload CV file
- Submit → confirmation
Custom / Unknown ATS
- Navigate to the careers page
- Look for "Apply" or "Postulate" or "Send CV" button
- If it redirects to LinkedIn → apply via LinkedIn flow instead
- If it's an email submission → draft email with CV attached, show to user (Gold Rule 6)
- If no apply button → mark as
no_application_systemin notes
Anti-ban
- Wait 3-5 seconds between actions (don't spam clicks)
- Don't apply to more than 5 jobs per company per session
- If a captcha or block appears → pause and notify user
- Vary navigation order when browsing job lists
- Take snapshots before interacting (LinkedIn-style tiptap editors are not the only ones with dynamic refs)
Resumability
The flow is fully resumable. The company_registrations table tracks state per company:
pending→ not yet visitedregistered→ account created, profile completedno_fit→ no matching roles (with reason in notes)manual_login_needed→ requires user interventionmanual_apply_needed→ Workday or similar that can't be automated
If the session is interrupted, the next run picks up where it left off by querying:
node scripts/db.js "SELECT * FROM company_registrations WHERE user_id = 1 AND registration_status = 'pending' ORDER BY region, company"
DB access
All DB access via scripts/db.js (see db skill). Read-only by default, --write for inserts/updates.
company_registrations table
| Column | Type | Purpose | |---|---|---| | id | SERIAL PK | | | user_id | INTEGER FK → users | Always 1 | | company | TEXT | Company name (unique per user) | | region | TEXT | latam or argentina | | sector | TEXT | Industry sector | | careers_url | TEXT | Direct URL to careers page | | ats_platform | TEXT | Detected ATS type | | registration_status | TEXT | pending, registered, no_fit, manual_login_needed, manual_apply_needed | | profile_completed | BOOLEAN | Whether profile was fully filled | | profile_url | TEXT | URL to user's profile on the platform (if available) | | login_method | TEXT | google, linkedin, email | | applied_jobs_count | INTEGER | How many jobs applied to via this flow | | last_visit_at | TIMESTAMPTZ | Last time the careers page was visited | | last_applied_at | TIMESTAMPTZ | Last time an application was submitted | | notes | TEXT | Free-form notes (ATS details, role observations, no_fit reasons) | | data | JSONB | Credentials (if email login), extra metadata | | created_at | TIMESTAMPTZ | | | updated_at | TIMESTAMPTZ | |
Typical queries:
# Pending companies
node scripts/db.js "SELECT id, company, region, careers_url, ats_platform, notes FROM company_registrations WHERE user_id = 1 AND registration_status = 'pending' ORDER BY region, company"
# Registered but no applications yet
node scripts/db.js "SELECT id, company, careers_url, ats_platform FROM company_registrations WHERE user_id = 1 AND registration_status = 'registered' AND applied_jobs_count = 0 ORDER BY company"
# Update after registration
node scripts/db.js "UPDATE company_registrations SET registration_status = 'registered', profile_completed = true, ats_platform = '', login_method = '', profile_url = '', last_visit_at = NOW(), updated_at = NOW() WHERE id = " --write
# Mark as no_fit
node scripts/db.js "UPDATE company_registrations SET registration_status = 'no_fit', notes = '', last_visit_at = NOW(), updated_at = NOW() WHERE id = " --write
# Increment applied count
node scripts/db.js "UPDATE company_registrations SET applied_jobs_count = applied_jobs_count + , last_applied_at = NOW(), updated_at = NOW() WHERE id = " --write
Rules
- Browser: always use
node scripts/browser.jsfor open/close/goto. Ne
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: galiprandi
- Source: galiprandi/job-seeker
- License: MIT
- Homepage: https://galiprandi.github.io/job-seeker/
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.