18 Commits (7086dcb0ea47616c2fa7cab38f2d9fbb8ef29113)

Author SHA1 Message Date
I Luk Kim 7086dcb0ea tribalhealth: add adapter for tribal staffing job board
Tribal Health (tribalhealth.com) is a clinician staffing agency placing providers at tribal/IHS sites — the one non-subscribed source carrying net-new tribal dental roles not on ihs.gov/usajobs. Elementor WordPress loop; collect-all + keyword-filter (mirrors nativehealth). collect_cards walks all numbered board pages internally so sparse roles on later pages aren't dropped by the orchestrator's empty-page break; paginate() returns False.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2 months ago
I Luk Kim e13a25b3d5 runtime: disable trace/screenshot/DOM snapshot saving
Tracing was accumulating 45GB+ of data (traces) + 5.6GB (dom) + 1.3GB
(screenshots) on every run. These artifacts are only useful during
development; remove them from the production run pipeline entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim 37e5b59126 googlejobs: traverse <template> content to find apply URLs
Apply links (Indeed, Glassdoor, Monster, etc.) are pre-rendered inside
a <template> tag within each card's share_el. <template> content is
inert in the rendered DOM — a regular querySelectorAll from outside
returns nothing, which is why the previous fix produced empty URLs.

Iterate share_el's <template> elements and search their .content for
links matching utm_campaign=google_jobs_apply. Verified on the live
Google Jobs page that each card's template contains the correct
external apply URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim fa748bebf9 googlejobs: filter out U.S. Navy company postings
Add exclude_company_keywords post-filter (case-insensitive substring
match against company field). Configure googlejobs.yaml to exclude
"U.S. Navy" / "US Navy" — these recurring military recruitment ads
aren't relevant orthodontist openings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim 2f33ea890d googlejobs: link to external apply URL instead of Google deep-link
The htidocid-based Google Jobs deep-links require the job to be in the
current search context (location, session) to open the detail panel.
When clicked from Telegram in a different context, the link only opens
the search list page without the specific job detail.

Each card's share_el ancestor has external apply URLs (Indeed,
Glassdoor, Monster, BeBee, AAO Career Center, etc.) pre-loaded in the
DOM. These are direct, stable links to the source job posting that work
regardless of session or location.

Switch jobUrl to use the first external (non-google.com) apply URL
within the card's share_el.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim a49a6539a1 googlejobs: use clean udm=8 deep-link format for job URLs
Previous shareUrl-based URLs included session-tied tokens (shmd, shmds,
shem) that can expire over time, causing the job detail panel to not
open when the link is clicked later from Telegram.

Switch to a minimal Google Jobs URL using only the htidocid and the new
SPA fragment format:
  https://www.google.com/search?q=<q>&udm=8#vhid=vt%3D20/docid%3D<id>&vssid=jobs-detail-viewer

Verified via Playwright: this format reliably opens the specific job
detail panel without any session-tied parameters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 0835522d06 aaoinfo: fix pagination click on detached DOM element
Use page.click(selector) instead of ElementHandle.click() so Playwright
re-queries the element at click time after AJAX page refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5fa28b3948 Add Google Jobs & HospitalRecruiting adapters; LinkedIn Volunteer filter; aaoinfo fixes
- Add googlejobs adapter (scroll-based extraction) and manifest
- Add hospitalrecruiting adapter (JS extraction) and manifest
- Fix googlejobs job URL: use shareUrl directly to preserve #fpstate=tldetail fragment
- Add exclude_title_keywords post-filter support (manifest + orchestrator)
- LinkedIn: exclude "volunteer" titles via exclude_title_keywords
- aaoinfo: exclude Preferred listings via :not(:has(.label-preferred)) selector
- aaoinfo: sort=start_ (descending = newest first)
- Telegram: multi-chat support, HTML escaping fixes, chunk splitting
- Various adapter, notifier, and CLI improvements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 317f929059 LinkedIn: fix relative URLs and deduplicate accessibility text in titles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim c4f9d5638b Fix Telegram HTML: URL & escaping, card-level chunk splitting, session scope fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 8ff94ad113 Configure loguru log level from LOG_LEVEL env var (default INFO)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim b28e204d80 LinkedIn: headless mode, scroll all 25 cards one-by-one
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 89ca7a4006 Fix warnings, LinkedIn selector update, Telegram notify listing, usajobs title filter
- LinkedIn: new URL with geoId, updated selectors to li[data-occludable-job-id], scroll adapter, pagination disabled
- usajobs: add include_title_keywords post-filter (dentist/orthodontist/orthodontic)
- aroragroup: fix AJAX load via doloadJBSearchList(), fix container selector
- govtjobs/srpmic: clear result_list_wait_selector to avoid 15s timeout on 0-result pages
- orchestrator: wait selector timeout WARNING → DEBUG
- extractor: no-container WARNING → DEBUG
- notifier: provider-based dispatch (telegram/kakaotalk), build_listing_messages() for Telegram
- cli notify: send job listing with links instead of Ollama summary
- global.yaml: provider set to telegram

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 22a0c6dbdc Extract employment_type for gilariver, usajobs; fix hrsa company parsing
- gilariver: parse employment type from subtitle "Location | Category | Active - Full Time"
- usajobs: add salary_text and employment_type selectors to YAML
- hrsa: filter <br> elements from td children to fix company/employment_type index offset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 72bcffad7f Add HRSA Health Workforce Connector adapter
Form-based keyword search (PrimeNG Angular SPA) with client-side pagination.
Uses JS click to bypass headless visibility issue on the Search button.
Extracts 112 dentist opportunity cards per run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 9d5251cead Add 6 new dental job site adapters
- ihs: Indian Health Service Dentistry (table, keyword filter)
- gilariver: Gila River Health Care via Infor CloudSuite (Angular, click pagination)
- nativehealth: NATIVE HEALTH via SmartRecruiters (AJAX Show More, keyword filter)
- srpmic: Salt River Pima-Maricopa via GovernmentJobs company page (URL keyword search)
- bfrench: Consulting BFrench via JazzHR (simple table, keyword filter)
- govtjobs: GovernmentJobs.com main search (URL keyword search, URL pagination)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 83fb8542a9 Add 8 site adapters, early stop, Telegram notifications, and list command
New site adapters:
- usajobs: multi-keyword sweep (dentist/orthodontics/orthodontist), URL pagination
- docshealth, southernortho: Paylocity platform, keyword in URL
- pdshealth, saltdental: iCIMS Angular platform, URL pagination
- hospitaljobsonline: Load More button, relative URL fix
- aaoinfo: AAO Career Center, click-based AJAX pagination
- aroragroup: Load More + post-scrape keyword filter

Core improvements:
- Early stop pagination: stops when all fingerprints on a page are already in DB
- multi_keyword_mode: separate — one scrape per keyword, cross-sweep dedup
- gimme-job list command to view collected postings
- Stored column in status command
- Telegram notification (replacing KakaoTalk)
- DB repo: count_by_site(), get_existing_fingerprints()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 8028d5e566 Initial implementation of gimme-job CLI
Complete Python package implementing all phases from the spec:
- Phase 0-1: Project scaffold, config, Pydantic/SQLAlchemy models, Typer CLI
- Phase 2: Runtime engine (BrowserManager, BaseAdapter/ManifestDrivenAdapter, orchestrator)
- Phase 3: Claude Code CLI integration (learn/repair modes with Jinja2 prompt templates)
- Phase 4: Ollama summarizer, KakaoTalk client, notification dispatcher
- Phase 5: Indeed adapter with manifest (sites/indeed.yaml)
- Phase 6: 36 unit tests (dates, hashing, dedupe, manifests, kakao, adapter)

gimme-job init/run/learn/repair/test/notify/status commands all wired up.
36/36 unit tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago