In Persian and Urdu folklore, the huma is a bird that never lands — and whoever it flies over is said to be blessed with fortune. This one's a little more grounded: it fetches remote job listings and scores each one against your own profile using an Ollama model. By default it uses your local server, but you can point it at any server you control.
╭─────────────────────────── ✦ huma ───────────────────────────╮
│ query AI engineer │
│ model gemma3:4b │
│ profile profile.md │
╰──────────────────────── fortune, in flight ──────────────────╯
scoring listings against your profile ━━━━━━━━━━━━━━━━━━━━━━ 30/30
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Score ┃ Title ┃ Company ┃ Location ┃ Salary ┃ Why ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 9 │ Senior AI Engineer │ A.Team │ Americas │ $90-150│ Strong match on agentic │
│ │ / Architect │ │ │ /hr │ systems and RL post-... │
└───────┴────────────────────────┴────────────┴───────────┴────────┴──────────────────────────┘
Job boards give you keyword search; they don't know what you actually want. huma fetches
listings, hands each one plus your own profile to a small local model, and asks it to score
fit and say why — so you skim ranked results instead of reading fifty descriptions.
With the default local Ollama server, your profile and scoring stay on your machine. If you select a remote server, it receives the profile and job descriptions needed to score each listing.
Requires Ollama (local or remote) and Python 3.10+.
git clone <this-repo-url>
cd huma
# 1. pull a model if you don't have one yet — any size works, 4B is plenty and fast
ollama pull gemma3:4b
# 2. install with uv
uv sync
# 3. save your usual Ollama endpoint and model once
uv run huma config setup
# 4. optional: describe what you're looking for — stays local, gitignored, never committed
cp profile.example.md profile.md
# edit profile.md with your own background, skills, and what you want
# 5. create a saved profile from a text PDF or LaTex resume
uv run huma profile setup --resume resume.pdf
# 6. search using the saved profile and saved Ollama settings
uv run huma search "AI engineer"huma search "machine learning engineer" -p profile.md
huma config setup # save endpoint + model in config.yaml
huma config show # inspect active settings
huma profile setup --resume resume.pdf # text PDFs and .tex only
huma profile update --resume updated-resume.tex # refresh resume and preferences
huma profile show # inspect saved profile
huma search "backend engineer" -p profile.md --category software-dev --limit 50 --top 15
huma search "AI engineer" --resume resume.tex # use a resume just for this search
huma search "data scientist" --no-llm # just list raw results, skip scoring
huma search "AI engineer" -p profile.md --model qwen3:4b # use any model you have pulled
huma search "AI engineer" -p profile.md --ollama-url 192.168.1.20:11434
OLLAMA_HOST=https://ollama.example.com huma search "AI engineer" -p profile.md
huma clear-cache # wipe cached scores| Flag | Default | What it does |
|---|---|---|
--profile / -p |
none | Path to a text/markdown file describing what you want; sent to the model with every listing |
--resume |
none | A text-extractable PDF or .tex resume; combines with saved preferences and --profile |
--model / -m |
saved config or gemma3:4b |
Any Ollama model available on the selected server; overrides config once |
--ollama-url |
saved config, OLLAMA_HOST, or http://localhost:11434 |
Ollama server to use; accepts host:port or an HTTP(S) URL; overrides config once |
--limit / -l |
30 | Raw listings fetched from the job board |
--top / -n |
10 | How many top-scored jobs to display |
--min-score |
0 | Hide anything scored below this |
--category / -c |
none | Remotive category filter (e.g. software-dev) |
--no-cache |
off | Re-score everything instead of reusing cached scores |
--no-llm |
off | Skip scoring entirely, just list raw results |
Huma stores profiles, deterministic agent runs, fetched jobs, and scored results in
~/.local/share/huma/huma.db. Scores are keyed by job + model + profile + Ollama server, so
re-running the same search doesn't re-score jobs you've already seen — and switching profiles,
models, or servers doesn't return stale scores from a different search.
If the selected Ollama server isn't reachable or the model isn't pulled, huma tells you
up front instead of failing partway through scoring.
Run huma config setup once to save the model and endpoint in
~/.config/huma/config.yaml:
ollama:
url: http://localhost:11434
model: gemma3:4bCommand-line --model and --ollama-url options always override the YAML file for a single run.
If no YAML file exists, Huma continues to respect OLLAMA_HOST, then defaults to
http://localhost:11434. Use HUMA_CONFIG_FILE to choose a different config path.
huma profile setup --resume <file> follows a deterministic, persisted state machine: it validates
the file, extracts text, asks for search preferences, shows a preview, and only saves after your
confirmation. Huma accepts text-extractable PDFs and UTF-8 .tex resumes. It intentionally rejects
scanned/image-only, encrypted, empty, unreadable, and unsupported files rather than guessing.
The original resume is not stored in Huma's database. Huma saves file metadata and a bounded
resume-derived profile alongside your explicitly confirmed preferences. The optional --profile
file remains useful for keeping richer free-form instructions under your own version control.
Each search is also a deterministic run: fetching_jobs → deduplicating_jobs → filtering_jobs → scoring_jobs → results_ready. Use huma status <run-id> to inspect its recorded transitions, and
huma doctor to check the local database and active Ollama endpoint.
huma/
├── sources.py fetches remote listings (Remotive API — free, no key)
├── scorer.py sends each listing + your profile to Ollama, parses back {"score", "reason"}
├── ollama_check.py preflight: is Ollama running, is the model pulled
├── config.py saved YAML settings for the Ollama endpoint and model
├── db.py SQLite profiles, jobs, scores, and agent-run history
├── fsm.py deterministic states and allowed transitions
├── resume.py strict local PDF/LaTex resume parser
├── cache.py SQLite score cache, keyed by job + model + profile
├── cli.py typer entry point wiring it all together
└── display.py rich-rendered colored table output
Wired up today: Remotive, RemoteOK, Arbeitnow, and Jobicy — all free, no API key. To add
another board, write a fetch_*_jobs() in sources.py returning the same normalized dict
shape (id, source, title, company, location, salary, category, tags,
job_type, published, url, description) and add it to SOURCE_FETCHERS in cli.py.
Remotive's free API asks for max ~4 requests/day and attribution back to remotive.com — see their API documentation. This tool is for personal job searching, not redistribution.