Three modes:
FilePurposeworkspace/resume.pdfResume PDF. User replaces to update CV.workspace/roles.txtTarget job titles, one per line.workspace/config.jsonSearch, email, and cron settings.data/seen_jobs.dbSQLite — deduplication across runs. Auto-created.scripts/scrape.pyFetches jobs from LinkedIn, Infostud, HelloWorld.rs.scripts/extract_resume.pyExtracts text from resume.pdf. Prints to stdout.scripts/mark_seen.pyMarks shown jobs as seen. Run after output.scripts/send_email.pySends HTML email digest. Only if email.enabled=true.
bash
python scripts/scrape.py
Writes new unseen jobs to data/raw_jobs.json.
bash
python scripts/extract_resume.py
Read the printed resume text carefully — you need it for scoring.
Read data/raw_jobs.json. For each job score the fit against the resume 1–10.
Scoring guide:
Be specific. Reference actual skills/experience from the resume against actual
requirements in the job. Use the tags field where descriptions are absent.
Only include jobs at or above config.json → search.min_score_to_include.
Sort by score descending, grouped by source. Format each job as:
text
🟢 [9/10] Strong fit
Senior .NET Developer
Luxoft Serbia · Belgrade
Tags: .NET, Azure, SQL
🔗 https://...
↳ [your 2-3 sentence justification]
🟢 = score 8–10 · 🟡 = 6–7 · 🔴 = below 6
Top summary line: "Found X jobs — Y 🟢 strong · Z 🟡 good · W 🔴 moderate"
Always run after outputting results:
bash
python scripts/mark_seen.py
Then check config for email:
bash
python scripts/send_email.py
If email.enabled is false in config.json, the email script prints "Email disabled"
and exits cleanly — no error, nothing to do.
If email is enabled, it sends the HTML digest automatically.
When the user says "set up daily job search" or "schedule this" or "run it every day at [time]":
Read workspace/config.json to get the preferred time and timezone.
Default is 09:00 Europe/Belgrade unless the user specifies otherwise.
Update config.json to set cron.enabled = true and the user's preferred time.
Register the cron job with OpenClaw:
bash
openclaw cron add \
--name "Daily Job Search" \
--cron "0 9 * * *" \
--tz "Europe/Belgrade" \
--session isolated \
--message "Run the job search"
Adjust the hour (0 9 = 9am) to match the user's chosen time.
To cancel: openclaw cron list to find the job ID, then openclaw cron delete <id>
When the user says "enable email" or "also send results by email":
Ask for: Gmail address, Gmail App Password (16-char, from myaccount.google.com/apppasswords), and recipient email (can be the same address).
Update workspace/config.json:
email.enabled to truesmtp_user, smtp_pass, recipientConfirm: "Email enabled. Results will be sent to [address] after each job search."
Note: Gmail requires an App Password (not the regular login password). Generate one at: myaccount.google.com/apppasswords
When the user asks for a cover letter for a specific job:
python scripts/extract_resume.py and read the output.Rules — non-negotiable:
Output the letter in chat. The user copies and edits it themselves.
workspace/resume.pdf is missing: stop and tell the user.workspace/roles.txt is empty: stop and tell the user.