Targets logo

Targets

Community
galiprandi
targets

Active direct sourcing. Registers and creates standout profiles on the 40 target companies' career sites, then applies to matching positions.

Overview

Publishergaliprandi
Repositoryjob-seeker
Skill nametargets
Stars
68
Forks
11
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by galiprandi on GitHub. Read the source before you install it.

Installation

Install the Targets AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/galiprandi/job-seeker/tree/main/.agents/skills/targets
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/galiprandi/job-seeker.git /tmp/job-seeker
mkdir -p .claude/skills
cp -r /tmp/job-seeker/.agents/skills/targets .claude/skills/targets
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Targets in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Targets on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Targets is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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 target companies (loaded from users.data.target_companies in DB), registers the user, creates a standout profile, and applies to matching positions.

Pre-flight

  • Browser: always use node .agents/skills/browser-automation/scripts/browser.js for open/close/goto. See AGENTS.md "Browser session" and "Parallel execution" for details. Never call agent-browser open directly
  • Parallel execution: if running alongside other flows (e.g: apply or news), attach a session with node .agents/skills/browser-automation/scripts/browser.js attach --session targets-1 and pass --session targets-1 to all browser commands and scripts. Use detach when done (never close — it's ref-counted)
  • Load active preferences (see memory skill):
    bash
    node scripts/db.js "SELECT category, key, value, confidence, source FROM preferences WHERE user_id = <user_id> 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 = <user_id>"
    Respect: targets_batch_size (max companies per session, 0 = don't run, "all" = no limit), match_threshold, relax_must_haves. If targets is not in sources_active, 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 = <user_id>"
  • 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 = <user_id> ORDER BY registration_status, region, company"
  • Load existing applications for dedup:
    bash
    node scripts/db.js "SELECT company, url FROM applications WHERE user_id = <user_id>"

Must-haves filter (from job_preferences)

Before applying to any job, verify against Must-haves from users.data.job_preferences. The filter is dynamic, not hardcoded. For each Must-weighted preference, discard if the job doesn't match. Common examples:

  • Work mode mismatch (if work_mode.weight = Must, role must match the user's preferred mode. If relax_must_haves resolves to include work_mode, accept partial matches)
  • Sector/specialization mismatch (if sector.weight = Must, role must be in the user's preferred sector)
  • Role level in deal_breakers (the user defines their own deal-breakers, never hardcoded. If the user listed "junior" as a deal-breaker, discard junior roles. If they didn't, don't)
  • In industries_avoid list (per industries_avoid.weight)
  • Salary below job_preferences.salary.value.min (if mentioned and salary.weight = Must)
  • Not in user's accepted locations (per location and timezones preferences)

Gold Rule 4: The user's career goal and what's sacrificable live in users.data.profile.career_goal and users.data.job_preferences. Read them from DB. Don't discard roles based on hardcoded assumptions about role type or seniority. Check relax_must_haves (resolved from users.data.job_preferences) to know which Must-haves are relaxed for the current strategy level.

Flow

Phase 1 — Registration & profile creation

For each company with registration_status = 'pending' (or profile_completed = false):

  1. Navigate to careers URL using node .agents/skills/browser-automation/scripts/browser.js goto <url> (or open if no session)
  2. 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)
  3. Find the registration / sign up / create account page
  4. 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.data as {email, password})
    • If no account creation possible → mark registration_status = 'manual_login_needed', notify user (Gold Rule 5)
  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: " | ")
    • Summary/bio: use profile.summary, adapted to the platform's character limit
    • Location: from users.data.personal_info (city, country) or form_answers.location
    • Upload CV: use profile.cv_path or personal_info.cv_pdf_path (from DB, never hardcoded)
    • Upload photo: use users.data.photo_path if the platform accepts it
    • Skills: add all from profile.skills
    • Experience: fill from profile.experience if 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)
  6. Update DB after each company:
    bash
    node scripts/db.js "UPDATE company_registrations SET registration_status = 'registered', profile_completed = true, ats_platform = '<ats>', login_method = '<google|linkedin|email>', profile_url = '<url if available>', last_visit_at = NOW(), updated_at = NOW(), data = '<json with credentials if email login>'::jsonb WHERE id = <id>" --write
  7. 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, and job_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 = <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 = <id>" --write

If a company has no matching roles or no remote options:

  • Mark registration_status = 'no_fit' with reason in notes
  • 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):

  1. Navigate to the company's job board
  2. Search with filters:
    • Keywords: derived from profile.title, profile.skills, and job_preferences (the user's primary role + key skills + seniority level)
    • Location: from job_preferences.location and job_preferences.timezones
    • Seniority: from job_preferences.seniority
  3. Filter by Must-haves (see above). For each matching job:
    • Check dedup against applications table
    • If already applied → skip
  4. Warm Sourcing & Referral Pre-Check (Strategy #1 & #4):
    • Gate: only run if referrals is in strategy.sources_active. If not, skip to step 5.
    • Run node scripts/linkedin-warm-sourcing.js --company "<Company>" --role "<Role>" --json
    • If internal contact/alumni/ex-colleague found: stage referral request in messages and set card status to discovered (Strategy #1).
    • If NO internal contact found: if strategy.cold_outreach = true, extract recruiter info and stage recruiter outreach DM in messages (Strategy #4). If cold_outreach = false, skip outreach. Either way, micro-align CV keywords to JD via scripts/generate-cv.js and proceed to ATS application.
  5. Apply following the ATS-specific flow (see ATS guide below)
  6. Register each application in DB:
    bash
    node scripts/db.js "INSERT INTO applications (user_id, platform, company, role, url, status, data) VALUES (<user_id>, '<company_lowercase>', '<company>', '<role>', '<url>', 'applied', '<json with match_reason, ats_type, location, salary_if_known>'::jsonb)" --write
  7. Update company registration:
    bash
    node scripts/db.js "UPDATE company_registrations SET applied_jobs_count = applied_jobs_count + <N>, last_applied_at = NOW(), updated_at = NOW() WHERE id = <id>" --write

Phase 3 — Report

Present to user:

## Targets report

### Registration summary
- Registered: X/40 companies
- Profile completed: X/40
- No fit (no remote/<country>/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/<company> or boards.greenhouse.io/<company>
  • "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/<company>
  • "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/<company>
  • "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.<company>.com or <company>.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/<company>
  • "Apply now" → form with personal info, resume, questions
  • Resume: upload CV file
  • May offer Google/LinkedIn prefill
  • Submit → confirmation

Teamtailor

  • URL pattern: <company>.teamtailor.com or <company>.na.teamtailor.com
  • "Apply" → form with personal info, CV, cover letter (optional)
  • Resume: upload CV file
  • Submit → confirmation

Eightfold

  • URL pattern: <company>.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_system in 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 visited
  • registered → account created, profile completed
  • no_fit → no matching roles (with reason in notes)
  • manual_login_needed → requires user intervention
  • manual_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:

bash
node scripts/db.js "SELECT * FROM company_registrations WHERE user_id = <user_id> 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

ColumnTypePurpose
idSERIAL PK
user_idINTEGER FK → usersAlways 1
companyTEXTCompany name (unique per user)
regionTEXTlatam or argentina
sectorTEXTIndustry sector
careers_urlTEXTDirect URL to careers page
ats_platformTEXTDetected ATS type
registration_statusTEXTpending, registered, no_fit, manual_login_needed, manual_apply_needed
profile_completedBOOLEANWhether profile was fully filled
profile_urlTEXTURL to user's profile on the platform (if available)
login_methodTEXTgoogle, linkedin, email
applied_jobs_countINTEGERHow many jobs applied to via this flow
last_visit_atTIMESTAMPTZLast time the careers page was visited
last_applied_atTIMESTAMPTZLast time an application was submitted
notesTEXTFree-form notes (ATS details, role observations, no_fit reasons)
dataJSONBCredentials (if email login), extra metadata
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ

Typical queries:

bash
# Pending companies
node scripts/db.js "SELECT id, company, region, careers_url, ats_platform, notes FROM company_registrations WHERE user_id = <user_id> 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 = <user_id> 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 = '<ats>', login_method = '<method>', profile_url = '<url>', last_visit_at = NOW(), updated_at = NOW() WHERE id = <id>" --write

# Mark as no_fit
node scripts/db.js "UPDATE company_registrations SET registration_status = 'no_fit', notes = '<reason>', last_visit_at = NOW(), updated_at = NOW() WHERE id = <id>" --write

# Increment applied count
node scripts/db.js "UPDATE company_registrations SET applied_jobs_count = applied_jobs_count + <N>, last_applied_at = NOW(), updated_at = NOW() WHERE id = <id>" --write

Rules

  • Browser: always use node .agents/skills/browser-automation/scripts/browser.js for open/close/goto. Never agent-browser open directly
  • Resumable. Every action updates company_registrations. If interrupted, next run continues from where it left off
  • Must-haves are non-negotiable. Don't register or apply to companies that don't match the user's job_preferences (remote, location, AI focus, salary, industries_avoid)
  • Gold Rule 4. Manager is preferred but sacrificable for interesting IC roles with AI focus and good compensation
  • Gold Rule 5. If manual login needed → open headed browser, notify user, wait
  • Gold Rule 6. If applying via email (not ATS form) → show draft to user before sending
  • Anti-ban. 3-5 second delays, max 5 applications per company per session
  • Dedup. Check applications table before applying. Don't apply to the same job twice
  • Persist everything. Registration status, profile completion, applications, credentials (in data JSONB)
  • CV and photo paths come from users.data.cv_path and users.data.photo_path. Never hardcode paths
  • Single user (repo owner)

Dependencies

  • Depends on onboarding (DB, browser profile, Gmail + LinkedIn sessions)
  • Depends on profile (Must-haves to filter, profile data to fill forms)
  • Consume by daily (can be added as a third pillar alongside news + apply)

Frequently asked questions

What does the Targets AI skill do?

Active direct sourcing. Registers and creates standout profiles on the 40 target companies' career sites, then applies to matching positions.

Why use Targets on TypingMind?

Because you install it once and use it with any model. Targets is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Targets in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/galiprandi/job-seeker/tree/main/.agents/skills/targets. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Targets?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Targets?

As many as you like. As long as a model supports skills, you can use Targets with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Targets AI skill free?

Yes. It is published on GitHub by galiprandi under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇