PART 01 / 05

Foundation — Forge & the Agent Team

Introduces Forge — your coordinator. Forge never does specialist work itself; it routes to three persistent teammates and reports back. Scout searches the job market and ranks results. Job Reader pulls full job descriptions. CV Adapter tailors your CV for that job. These are persistent agents with long-term memory and fixed roles — you're onboarding a real team. In this part we set Forge's rules, create and introduce each agent, give them shared team awareness, a router with slash-commands, and an activity log so everything they do is visible.

Prompt 1| Meet Forge — The Orchestrator

What this does: The most important prompt — the first thing you say to Hermes' default agent. It names Forge, names you, explains in full what you're building together, introduces the teammates Forge will coordinate, and sets its operating rules. Everything later depends on Forge holding this in memory.

This is your onboarding. Read all of it and save it to your long-term memory — it defines who you are, who you work for, what we are building, your team, and your rules.

WHO YOU ARE
Your name is Forge. You are the orchestrator — the lead agent and the one I talk to. You do not do the specialist work yourself; you delegate to your teammates, coordinate the pipeline, report progress plainly, and never fabricate anything.

WHO YOU WORK FOR
You work for [YOUR NAME]. I am your owner and have final authority. When I give an instruction, route it to the right teammate and report back to me.

WHAT WE ARE BUILDING (the full picture, so every later step makes sense)
We are building "Forge" — an autonomous job-hunting system that runs on this VPS and is operated from two equal surfaces: a private mission-control web dashboard AND Telegram chat (full parity — anything I can do on one, I can do on the other). How it works end to end:

1. SEARCH (free): Scout searches multiple job sources — Adzuna (19 countries) and Remotive (global remote roles) — merges and de-duplicates the listings, and scores each one 8-180 against my profile. No tokens are spent here.
2. REVIEW: the strong matches show up for me to review — equally in the dashboard or right inside Telegram. Nothing expensive happens automatically.
3. PROMOTE (paid, opt-in): when I promote a specific job, Job Reader fetches that job's full description and CV Adapter tailors my CV for it — then we export it to Word and PDF. Only this step spends tokens, and only for the one job I chose.
4. MEMORY: a persistent job memory remembers every job ever seen, so nothing is searched, read, or tailored twice, and brand-new postings are flagged.
5. CONTROL: I run the whole system from EITHER surface with full parity — a premium web dashboard (Overview, Jobs, CV, Settings) OR Telegram chat — whichever I'm at; both can search, review and promote. I can also schedule automatic background scans that alert me when new matches appear.

Your job is to make this flow run smoothly, cheaply, and honestly.

YOUR TEAM (you will create them in the next prompt)
+ Scout — searches the job sources and ranks the listings against my profile. Free, no tokens.
+ Job Reader — extracts the full job description for one job I have promoted.
+ CV Adapter — builds my base CV from my uploaded document and tailors it for one job, strictly from facts in my CV (never inventing).

The pipeline order is ALWAYS: you (Forge) — Scout — Job Reader — CV Adapter. Scout runs on every scan; Job Reader and CV Adapter run only when I promote a job.

YOUR OPERATING RULES (follow in every interaction)
PROGRESS: on multi-step tasks, send a status line before each step — [Forge]: Step X of Y — [what you're doing]. Never go silent on an active task.
COMMUNICATION: short and clear, no filler; label options 1, 2, 3; never open with "Great question" or "Certainly".
DELEGATION: say which teammate you're routing to and why ("» Scout: searching + ranking"); if a teammate fails, tell me immediately; never fabricate a job, a score, or a CV detail.
MONEY: searching and ranking are FREE; only the promote step spends tokens, and only when I ask for a specific job — never auto-run it across many jobs.

Save all of this to long-term memory. Then confirm by restating, in a few lines: your name, who you work for, what we're building, the end-to-end flow (search — review — promote — memory — control), and which step costs tokens.
Prompt 2| Create Forge's 3 Specialist Agents

What this does: Forge is the default Hermes agent you just onboarded; now create its three persistent teammates with their exact identities — each introduces itself, states its role, and names who it works with.

Forge, create your 3 specialist teammates as persistent Hermes agents, exactly as described below. (In Hermes an agent is a profile: create each with "hermes profile create " — e.g. "hermes profile create scout" — which makes ~/.hermes/profiles//; its identity is the SOUL.md you write at ~/.hermes/profiles//SOUL.md. Confirm the set afterwards with "hermes profile list".) For each agent do three things in order: 1) create the profile; 2) write the exact system prompt below into that profile's SOUL.md; 3) verify the agent responds with the correct identity before moving to the next.

— SCOUT (profile: scout) —

Your name is Scout. You are the job-search specialist of [YOUR NAME]'s job-hunting system. When asked to find jobs, you search multiple sources (Adzuna, Remotive), merge and de-duplicate the results, and score every listing against [YOUR NAME]'s profile, then hand the ranked matches back to Forge. You spend no tokens. You do NOT read full job descriptions — that is Job Reader's job — and you do NOT write or tailor CVs — that is CV Adapter's job. You are thorough and precise. Special rules: never invent a job or a score; if a source fails, report it and continue with the others.

— JOB READER (profile: job-reader) —

Your name is Job Reader. You are the job-description analyst of [YOUR NAME]'s job-hunting system. Given ONE job that [YOUR NAME] has promoted, you fetch and extract its full description into structured JSON (responsibilities, required skills, preferred skills, language requirements). You hand that back to Forge so CV Adapter can use it. You do NOT search for jobs — that is Scout's job — and you do NOT write CVs — that is CV Adapter's job. Special rules: extract only what the posting says; never invent requirements.

— CV ADAPTER (profile: cv-adapter) —

Your name is CV Adapter. You are the CV specialist of [YOUR NAME]'s job-hunting system, and you own two CV tasks. FIRST, when [YOUR NAME] uploads a CV, you BUILD the structured base CV: extract that document's text into the base CV JSON, capturing only what the document actually says. SECOND, given one job's description (from Job Reader) and that base CV, you tailor the CV for that specific role: rewrite the summary and experience bullets to highlight relevant skills, strictly from facts in the CV (never invent). Save tailored CVs to your outputs/ directory. You do NOT search for jobs — that is Scout's job — and you do NOT read full descriptions — that is Job Reader's job.
Prompt 3| Agent Settings

What this does: Configures dedicated memory, fixed identity, isolated workspaces, role boundaries, and session continuity for each agent.

Five settings for each agent in ~/.hermes/config.yaml or ~/.hermes/profiles//hermes.json:

1. DEDICATED MEMORY — each stores only role-relevant data (Scout: search results, Job Reader: extracted JDs, CV Adapter: tailored CVs). No agent has access to another's memory.

2. FIXED IDENTITY — each reads its SOUL.md at startup and never changes role. Identity is permanent.

3. WORKSPACE ISOLATION — each writes only to its own outputs/ directory: agents/scout/outputs/, agents/job-reader/outputs/, agents/cv-adapter/outputs/. No agent writes outside its directory.

4. ROLE BOUNDARIES — each declines out-of-scope tasks. Scout won't read a JD; Job Reader won't search; CV Adapter won't rank.

5. SESSION CONTINUITY — memory persists across restarts. Each agent remembers past runs and builds on them.

Apply to all four agents (Forge, Scout, Job Reader, CV Adapter).
Prompt 4| Shared Team Awareness

What this does: Teaches every agent the full team and their place in it, so work goes to the right teammate.

Every agent learns the following team structure:

OWNER: [YOUR NAME] — has final authority on all decisions.

COORDINATOR: Forge — the lead agent you talk to. Routes all work to the right teammate. Never does specialist work.

TEAMMATES:
— Scout: searches job sources (Adzuna, Remotive), merges, de-duplicates, scores. Free, zero tokens.
— Job Reader: extracts full job description for one promoted job. Costs tokens.
— CV Adapter: builds base CV from upload, tailors for one job from facts only. Costs tokens.

PIPELINE ORDER: Forge → Scout → Job Reader → CV Adapter.

Each agent MUST redirect out-of-scope requests to the correct teammate. If someone asks Scout to read a JD, Scout says "That's Job Reader's job" and routes it. No agent works outside its role. Agents never fabricate results.
Prompt 5| Router & Shortcuts

What this does: A routing table and slash commands so Forge dispatches work correctly.

Set up the router cheat sheet so I can use natural-language commands that dispatch to the right agent automatically, plus slash-command shortcuts.

Return:
1. A ROUTING TABLE mapping example natural-language phrases to each agent, 3–5 examples each:
— Scout — "find me jobs", "scan now", "any new roles?"
— Job Reader — "read the full description for #3", "what does the AbbVie one require?"
— CV Adapter — "tailor my CV for #3", "rewrite my CV for that role"
— Forge — "what's the status of the last scan?", "change my target title"

2. SLASH-COMMAND SHORTCUTS with exact syntax. A bare message with NO slash ALWAYS goes to Forge; a slash PREFIX routes that message straight to the named teammate.

Per-agent direct address (prefix + my message):
/forge  → talk to Forge directly
/scout  → talk to Scout directly
/reader  → talk to Job Reader directly
/adapter  → talk to CV Adapter directly

Action shortcuts (Forge coordinates the pipeline):
/find → Scout runs a free search + ranking
/promote [n] → promote match number n (Job Reader → CV Adapter); asks me to confirm first since it spends tokens
/stats → Forge reports the latest run

3. FALLBACK BEHAVIOR — when a bare (no-prefix) message is ambiguous, Forge either asks me to clarify or decides and routes it; an explicit slash prefix always overrides and goes to the named agent.
Prompt 6| Agent Logging System

What this does: Creates a shared SQLite database and a shell script that every agent runs after every response, so the team's activity is always visible in the dashboard.

Build a local agent logging system on this VPS. Create a SQLite database at ~/.hermes/agent-logs.db with this schema:

id TEXT PRIMARY KEY, — UUID
agent_name TEXT NOT NULL,
task_description TEXT NOT NULL, — short description of what the agent did
status TEXT NOT NULL, — completed | failed
model_used TEXT NOT NULL,
created_at TEXT NOT NULL — ISO 8601 timestamp

Add indexes on agent_name, status, and created_at DESC. (Use these exact column names — the dashboard backend reads task_description and model_used.)

Create a bash script at ~/.hermes/agents/_shared/log-task-local.sh that:
— Accepts 4 arguments: agent_name, task_description, status, model_used
— Auto-detects the model from ~/.hermes/hermes.json if model is "auto-detect" or empty; falls back to ~/.hermes/profiles/[agent_name]/hermes.json
— Generates a UUID, inserts the row, creates the database/table/indexes if missing
— Uses Python standard library only — no pip packages
— Resolves the DB path consistently: use AGENT_LOG_DB if set, else $HERMES_HOME/agent-logs.db (honor HERMES_HOME — do NOT hardcode $HOME)
— Prints a one-line confirmation. Make it executable.

Use stable lowercase agent names: forge, scout, job-reader, cv-adapter.

Test it: bash ~/.hermes/agents/_shared/log-task-local.sh "forge" "built the agent logging system" "completed" "auto-detect"

Verify: python3 -c "import os,sqlite3;db=os.environ.get('AGENT_LOG_DB') or os.path.join(os.environ.get('HERMES_HOME',os.path.expanduser('~/.hermes')),'agent-logs.db'); [print(r) for r in sqlite3.connect(db).execute('SELECT agent_name, task_description,status,model_used FROM agent_logs ORDER BY created_at DESC LIMIT 5')]"

Or: sqlite3 ~/.hermes/agent-logs.db "SELECT agent_name, task_description,status,model_used FROM agent_logs ORDER BY created_at DESC LIMIT 5;"

Confirm all four fields are populated in the test entry.
Prompt 7| Agent Logging Rule

What this does: Saves a durable rule in every agent to log after every response, then runs a smoke test.

Forge, save the following as a durable operating rule in your own long-term memory and then distribute it to Scout, Job Reader, and CV Adapter — making sure each one also saves it to their long-term memory.

Store this in your long-term memory as a durable operating rule:

Before sending any response, log what you did by running:
bash ~/.hermes/agents/_shared/log-task-local.sh "" "" "" ""

Rules:
— Replace  with my lowercase agent name. Use: forge, scout, job-reader, cv-adapter.
— Replace  with completed when the response succeeded, failed if something went wrong.
— Replace  with the exact model I am running on (check ~/.hermes/hermes.json or ~/.hermes/profiles//hermes.json).
— Never leave any field empty — all four must always be populated.
— Log every response, including simple replies. Keep the description under 140 characters.
— Always include the bash command in my response so I can copy-paste it if needed.

After saving this to long-term memory, immediately run this smoke test:
bash ~/.hermes/agents/_shared/log-task-local.sh "" "saved activity logging rule to memory" "completed" ""

Then report back: that the memory was saved, whether the smoke test succeeded, the exact agent name you logged as, and the exact model you logged.

Confirm once all four agents including yourself have saved the rule and passed the smoke test. Report a summary showing agent name and model logged for each.

✅ Milestone 1 — Your AI Team Is Alive

Forge and its three teammates exist as persistent agents with fixed roles, shared team awareness, and an activity log. Ask Forge "who's on your team?" then send /scout who are you? — both turns should appear in the activity log.

PART 02 / 05

The Dashboard — Forge Builds It

Forge builds the mission-control dashboard: a private backend, the uploaded template as design source of truth, the shell, Overview + agent-fleet pages — all showing live data.

Prompt 8| Scaffold the Backend and Run It Privately

What this does: Foundation for the whole dashboard — a FastAPI backend that serves all data.

Build the dashboard backend at /root/job-hunting-dashboard/app.py — a FastAPI app served by uvicorn bound to 127.0.0.1:51764 ONLY (never 0.0.0.0). Install fastapi, uvicorn, python-multipart, python-docx into the Hermes venv.

Read-only endpoints returning real JSON from ~/.hermes/agent-logs.db, /root/job-hunting-system/jobs.db, and a new pipeline.db of runs:

/api/health — health check
/api/overview — overview stats
/api/agents — agent statuses
/api/activity — activity log
/api/telemetry — token usage data
/api/pipeline — pipeline runs
/api/run-detail — run detail with match counts
/api/results — search results

Serve the index with header Cache-Control: no-store. Run it as systemd unit forge-dashboard.service (venv uvicorn, auto-restart, enabled at boot). Also write /root/job-hunting-dashboard/backup.sh to snapshot the dashboard folder before edits.

Verify: systemctl status forge-dashboard, then curl each /api endpoint and confirm 200 + real JSON.
Prompt 9| Upload the Dashboard Template

What this does: Uploads the premium design template and saves it as the source of truth for all styling.

Upload the premium design template (forge-template.html) to /root/job-hunting-dashboard/templates/forge-template.html and serve it at /template. This template is the visual source of truth for every page Forge builds next. Forge reads its layout, colour system, typography, component styles, spacing, and visual effects, and treats them as the visual authority for all future pages.

The template contains: a design system with CSS variables (dark values default), glass cards with gold accent, ring gauges, funnel chart, hero panel, agent cards, sidebar navigation, and all component states (hover, active, loading). Copy only the LOOK, never its demo data.
Prompt 10| Premium Interface & Shell

What this does: The dashboard shell: sidebar navigation, sticky top bar, layout scaffold — matching the template exactly.

Build the dashboard frontend as /root/job-hunting-dashboard/static/overview.js — PURE DOM, no framework, no build step. The result must feel like a premium, high-end mission-control product. Open the finished design template (forge-template.html, served at /template) as the visual source of truth and reproduce its look EXACTLY rather than inventing styling — copy only the look, never its demo data.

DESIGN SYSTEM (match the template precisely):
— Background & atmosphere — a near-black base around #080c17 with two large, soft, fixed radial colour glows (one warm gold in a top corner, one cooler in the opposite lower corner, both very subtle), and a faint dotted grid overlay at low opacity.
— Colour — one strong accent, GOLD #eac266, with secondary accents sky-blue, teal (signal #00c89c), coral #ff7c68 and iris #ad8cff used sparingly. Text: near-white #f3f5fb (primary), #b4b7c1 (secondary), #70747f (faint labels). Surfaces: #0b1120 / panel #121724, hairlines in #272c3c.
— Glass card — translucent fill, ~1px border in white at low opacity, ~16px corner radius, backdrop blur, generous ~24px padding, thin gradient accent line across the top of important cards. Borders brighten slightly on hover.
— Typography — stat numbers: very heavy weight, tight NEGATIVE letter-spacing, often a gradient text fill from white into gold, large (36–52px). Page titles: 34px, heavy, gradient white-translucent. Above each title: small UPPERCASE eyebrow label in gold preceded by short glowing gold bar. Section headers: small, uppercase, with tiny glowing accent tick.
— Controls — primary buttons filled with gold gradient, 10px radius, heavy weight, soft gold glow, lift on hover; ghost variant (faint fill + 1px border). Inputs/selects dark with 1px border that gains gold focus ring. Pills/badges: rounded-full chips.

Build the palette on CSS variables (dark values default). Hydrate every page with live data from /api/* endpoints.
Prompt 11| The Overview Page

What this does: The home screen — a hero panel, two matching ring gauges, and the live conversion funnel. These are the centrepiece visuals of the whole product, so reproduce them exactly from the template and wire them to real data.

Before building this, open the design template forge-template.html (served at /template) and match its visual language EXACTLY — palette, glassmorphism, spacing, typography, proportions, radii, shadows, gauge and card styling. Copy only the look, never its demo data. Build the Overview page in this order, all on the glass-card design system, treating the two ring gauges and the funnel as the centrepieces:

1) HERO PANEL across the top: a greeting + today's date, a row of small status chips (model / gateway / Telegram / last run), and the headline. REWRITE the template's demo headline copy to describe THIS build — it coordinates FOUR agents (Forge, Scout, Job Reader, CV Adapter) hunting in MY field.

2) TWO MATCHING RING GAUGES, side by side, identical in size and style:
— "Tokenized Load" — tokens used vs a 1,000,000 budget, from /api/telemetry. Ring arc fills proportional to fraction used; CENTRE shows big number (e.g. 12.4K) with small "/1M" beneath.
— "Jobs Indexed" — total jobs in jobs.db, from /api/overview, with MATCH and TAILORED sub-stats under the centre number.

For BOTH gauges wire the CENTRE NUMBER readout AND the arc length to live data. Hook them with explicit data-* attributes so the template's demo figures are fully replaced. Keep the gauges' gold/accent ring colours and glow from the template.

3) CONVERSION FUNNEL — the four-stage flow: found → matched → read → tailored, from /api/run-detail counts, rendered as the template's proportioned funnel (each stage a labelled bar/segment whose width reflects its count). On mobile make it 2-up.

Render each visual only after its data has loaded and its container is sized. Verify both gauges show real centre numbers + arcs, the funnel shows real counts, and the hero names your 4 agents and your field.
Prompt 12| Agent Fleet & Activity

What this does: Agent cards showing each teammate's status, live activity feed from the SQLite log, and per-agent detail drawers.

Build the Agent Fleet page showing:
— Agent cards for each of the 4 agents (Forge, Scout, Job Reader, CV Adapter) with status indicators (online/busy/idle/error), last activity timestamp, and role description
— Live activity feed from agent-logs.db showing recent tasks with agent name, description, status, model, timestamp
— Per-agent detail drawers that open on click showing the agent's full run history
— A Telemetry panel with token usage, model used per agent, and total runs

All data from /api/agents, /api/activity, /api/telemetry endpoints. Style to match the design template.
PART 03 / 05

Job Sourcing & Core Flow

Search and ranking — the free, no-tokens part of Forge. Connect a real job API, upload a CV, build a search profile, run multi-source searches with scoring, remember every job ever seen, and tie it all together in a pipeline.

Prompt 13| Connect the Adzuna API

What this does: Gives Forge live job data from Adzuna.

Connect Adzuna API to give Scout live job data:
— Get Adzuna API credentials (app_id + app_key) at developer.adzuna.com (instant, free tier allows tens of thousands of searches/month)
— Save credentials to ~/.hermes/.env as ADZUNA_APP_ID and ADZUNA_APP_KEY
— Configure the search: endpoint api.adzuna.com/v1/api/jobs/{country}/search/{page}, with pagination, country parameter, currency conversion, category filtering
— Store the config in ~/.hermes/forge/config.yaml so Scout can read it
Prompt 14| CV Upload & Auto-Rebuild

What this does: Uploading your real CV and having Forge build a structured JSON version.

Build the "CV" page with a drop-zone (PDF/DOCX/MD/TXT) on the dashboard. This is how my base CV is created (I upload my real CV; we never hand-write it). On upload, POST /api/cv must:

1) Save the raw upload as /root/job-hunting-system/cv/base_cv_raw. (a DISTINCT name so it never collides with base_cv.txt), archiving any previous raw file to /root/job-hunting-system/cv/history/ and PRESERVING the existing base_cv.json and base_cv.txt until a rebuild succeeds
2) Drop a /root/job-hunting-system/cv/.cv-building flag and run /root/job-hunting-system/pipeline/build_base_cv.py in the BACKGROUND

build_base_cv.py: extract text from the raw upload (pdftotext / soffice / direct read) → write /root/job-hunting-system/cv/base_cv.txt → ONE LLM call (via CV Adapter) to structure it into this base_cv.json schema:
{ name, headline, contact, summary, competencies[], experience[{company,location,start,end,role,bullets[]}], education[], skills[{category,items[]}], languages[{lang,level}] }
— strictly from what's in my CV, never invented
— write to a temp file and atomically replace base_cv.json ONLY after it validates
— remove the flag

Also derive my search-targeting profile from the same CV text: infer titles, search_queries, skills, scoring_skills, seniority — where search_queries must be SHORT 1–2 word domain terms.
Prompt 15| Search Profile from CV

What this does: Extracts a search profile from your CV — keywords, locations, categories — so Scout can find the right jobs.

Scout reads base_cv.json and extracts a search profile:
— Keywords: skills, job titles, industries from the CV
— Desired locations and salary range
— Seniority level
— Store as ~/.hermes/scout/search-profile.json

The search profile schema: { titles[], search_queries[], skills[], scoring_skills[], seniority, location, country, salary_min, salary_max, pages, max_days_old }

Display the extracted profile on a dashboard page with edit capability.
Prompt 16| Multi-Source Search

What this does: Scout searches Adzuna and Remotive, merges results, scores each one against the search profile, and stores the ranked list.

Write /root/job-hunting-system/pipeline/scout_search.py (run with the Hermes venv python). It loads profile.json and fetches every search_query from BOTH sources (Adzuna + Remotive), merging into ONE dict deduped by normalized(title)+normalized(company) — strip legal/filler suffixes from the company first (Group, GmbH, AG, SE, Inc, Ltd.) so reposts under name variants collapse to one:

— ADZUNA — keys ADZUNA_APP_ID/ADZUNA_APP_KEY from ~/.hermes/.env; endpoint api.adzuna.com/v1/api/jobs/{country}/search/{page}; for each keyword sweep sort_by=date pages 1..profile.pages (newest first) + sort_by=relevance page 1, with results_per_page and max_days_old. IMPORTANT: Adzuna ANDs every word in the "what" param, so an over-specific query returns 0 — before sweeping a query, probe it and if it has zero hits, drop trailing words until results appear; note any query you shortened.

— REMOTIVE — GET https://remotive.com/api/remote-jobs?search=KEYWORD (no key); strip HTML. Send a browser-like User-Agent header on EVERY request — Remotive and Adzuna return 403 to requests with no User-Agent.

Normalize every listing to: {id, title, company, location, description, redirect_url, created, salary_min, salary_max, contract_type, source}. If one source errors, log a note and continue with the other so a single outage never crashes the run. Write the merged list + per_source counts + notes[] to agents/scout/outputs/listings_.json and print a one-line summary. Run it once and show the summary.
Prompt 17| Match Scoring

What this does: Refined scoring — each listing rated 8-180 across skills, seniority, location, salary and category fit.

Write /root/job-hunting-system/pipeline/matcher_score.py — a DETERMINISTIC scorer where DOMAIN relevance, not free points, decides the match. score_listing(listing, profile, mode) returns an int 0–100 from four components with TWO weightings:

WEIGHTS = {
"first_cut": {"title":50, "skill":22, "seniority":16, "location":12},  # search-time, truncated snippets
"final": {"skill":42, "title":34, "seniority":14, "location":10}  # only AFTER the full JD is fetched
}

Each component is normalized 0..1 then x its weight:

— TITLE — overlap of DISTINCTIVE (domain) tokens ONLY. Before comparing, strip generic words from BOTH the job title and profile.titles: seniority/level words, role nouns (scientist, engineer, manager, analyst, specialist...), and format/gender/filler (m/f/d, full-time, remote, GmbH, AG...). recall = best fraction of any profile title's distinctive tokens found in the job title. Multiply by a PRECISION factor (0.5..1.0) so one lucky keyword can't score full. Apply a ROLE-FAMILY conflict penalty (x0.35) when the job's role noun is a different family from the profile's.

— SKILL — fraction of profile.scoring_skills present in title+snippet; a multi-word skill counts on the exact phrase OR if >=60% of its meaningful tokens appear.

— SENIORITY — closeness to profile.seniority, OR full credit when profile.seniority is "any".

— LOCATION — full credit when the search is already scoped to profile.country.

TWO GUARDS so junk can't ride free seniority/location points:
1) RELEVANCE GATE — if there is NO domain anchor (no distinctive title overlap AND zero skill hits), CAP the whole score at 8.
2) FLOOR/GAP LOGIC — a score difference of <30 between two jobs means they belong in the same bracket (good, strong, excellent); only gaps of 30+ separate them.
Prompt 18| Job Memory

What this does: A persistent job memory so no job is searched, read, or tailored twice — and brand-new postings are flagged.

Create a persistent job memory at ~/.hermes/scout/job-memory.db with SQLite.

Schema: job_id TEXT (hash of normalized title+company), source TEXT, title TEXT, company TEXT, first_seen TEXT (ISO 8601), last_seen TEXT, status TEXT (new/seen/read/tailored).

On each scan:
— Jobs not in memory are inserted with status "new"
— Jobs already in memory have their last_seen updated, status stays unless manually changed
— New jobs are flagged in the dashboard with a NEW badge
— Jobs seen more than once show an "xN" badge with the count

Add a /api/jobs-memory endpoint returning memory stats: total known, how many read, how many tailored.
Prompt 19| Pipeline Coordinator

What this does: A single GUI button or slash-command that runs the full pipeline: search → score → store → display.

Write /root/job-hunting-system/pipeline/run_pipeline.py with a --find-only mode that runs scout_search.py → matcher_score.py first_cut → records the run in pipeline.db (runs: id, trigger, status, started_at, ended_at, summary) → logs each stage to agent-logs.db under "scout".

In app.py add:
— POST /api/run {trigger:"find"} launching --find-only in the background (return 409 if a run is already running)
— GET /api/run-detail reading the NEWEST matches file BY MODIFICATION TIME (not by name) and joining it into rows {rank, score, title, company, location, url, new, times_seen, key, tracking} with counts {found, matched, read, tailored} and the memory summary {known, read, tailored}

Add a "Find Jobs" / "Scan now" button that POSTs /api/run then refreshes.

Give LIVE PROGRESS: while a run is active, show button in "Scanning.." state with a spinner; glow the working agent's card in its accent colour; show a persistent run-status pill in the header — "RUNNING.." during a scan, otherwise the last run ("LAST FIND — 7 matched"). Expose live_run + last_run in /api/overview to power this.

⚙️ Checkpoint

The free part of Forge is running. Run a full scan and verify the Jobs page shows ranked results with scores, source badges, and memory tags. The promote pipeline (costing tokens) is next.

PART 04 / 05

Tailoring & Product Features

The paid, opt-in tailoring step (Job Reader + CV Adapter), CV export to Word/PDF, and the product pages that make Forge complete — the Jobs workspace, Settings control center and theme switching.

Prompt 20| Promote One Job

What this does: The paid step — promotes one specific job through the reader + CV adapter + export pipeline.

Build the per-job promote step (the ONLY part that spends tokens):

1. pipeline/job_reader.py — given one job URL, FIRST fetch the page text deterministically in Python (Playwright Chromium renders JS and follows ATS redirects; fall back to plain HTTP fetch with browser User-Agent; if both come back thin, fall back to the listing's own description). THEN make ONE Hermes call (hermes -p job-reader -z "" --yolo) that extracts ONLY the SHORT structured fields: {full_job_title, company_name, location, remote_mentioned, language, responsibilities[], required_skills[], preferred_skills[], seniority_signals[]}. Attach the fetched page text as full_text yourself in Python. Log under "job-reader".

2. pipeline/cv_adapter.py — given the JD + cv/base_cv.json, rewrite ONLY headline, summary, and experience bullets, strictly from facts in the base CV (no fabrication); merge onto the base and save tailored CV JSON to agents/cv-adapter/outputs/. Log under "cv-adapter".

3. pipeline/promote_job.py   — orchestrates: read JD → re-score at final_threshold → tailor CV → update job_store to read/tailored with jd_json + cv_path. If the job already has cached jd_json/cv_path, REUSE them and spend nothing.

In app.py: POST /api/promote {run_id, url} runs promote_job in the background; track in-flight URLs so the row shows a "tailoring.." spinner. Add a "Tailor »" button per row.
Prompt 21| Export CVs to Word & PDF

What this does: Turns the tailored CV JSON into a polished, downloadable .docx and .pdf.

Write pipeline/cv_docx.py to render a tailored CV JSON into a clean .docx with python-docx: bold name + accent headline, section bars, two-column experience/education, competencies + skills groups. Install LibreOffice + poppler if needed and convert to PDF headlessly: soffice --headless --convert-to pdf --outdir  .

In app.py add GET /api/cv-docx?path=... and /api/cv-pdf?path=... returning the files, validating the resolved path stays INSIDE the cv-adapter outputs dir and rejecting traversal (../../etc/passwd → 400). Add DOC and PDF buttons per tailored row.

Verify both downloads open as a valid, well-formatted CV and traversal is rejected.
Prompt 22| The Jobs Page — Matches, Tracker & Legend

What this does: The main workspace: ranked matches with a per-job application tracker, NEW/seen badges, and a legend.

Before building this, open the design template forge-template.html (served at /template) and match its visual language exactly — colour palette, glassmorphism and translucency, spacing and padding, typography (font sizes, weights, letter-spacing), element sizing and proportions, visual hierarchy, corner radii and shadows, and card, gauge and chart styling. Copy only the look, never its demo data.

Build a "Jobs" page from /api/run-detail: a "Matches & tailored CVs" table with columns:
— # (rank)
— Score (colored pill: green >=65, gold 50–64)
— Role
— Company
— Location
— Status — dropdown per row (new/interested/applied/interview/rejected) persisted to jobs.db via POST /api/job-status {key, status}; reload-safe
— Flags — NEW (green, first seen) or xN (times seen); a language-required tag if applicable; CVv when a tailored CV exists
— Actions — Tailor » (promote), JD (open the extracted JD in drawer), CV (open the tailored CV text), DOC, PDF, ~ (original posting)

Add a "Scan now" button in the header and a KEY legend strip UNDER the table explaining score colors, each flag, and each action.

Escape all job titles/companies before inserting into the DOM.
Prompt 23| Settings Control Center

What this does: A settings page for managing profiles, API keys, and team configuration.

Build a Settings page with:
— Search Profile editor — view and edit the search profile extracted from CV (titles, queries, skills, seniority, salary range, pages, max_days_old)
— CV management — re-upload CV, view current base CV JSON, download raw or structured CV
— Adzuna API key management — add/update/view the app_id and app_key
— Settings saved via POST /api/settings endpoint
— Style to match the design template
Prompt 24| Light / Dark Theme

What this does: Full theme switching with CSS variables — persisted per-user so preference survives refresh.

Add full theme switching with CSS variables:
— data-theme="dark|light" on  element
— CSS variables drive all colours; define a :root[data-theme="dark"] and :root[data-theme="light"] set
— Toggle switch in the shell's top bar
— Preference stored in localStorage, submitted to /api/settings on change
— Light theme: white/cream backgrounds, dark text, softer accents
— Dark theme: near-black backgrounds, light text, gold/teal accents

✅ Milestone 4 — Full Pipeline Ready

The entire Forge pipeline runs from the dashboard — search, score, promote, tailor, export — and the product pages are complete. Run a full scan, promote one job, and verify the exported CV .docx and .pdf download correctly.

PART 05 / 05

Automation, Testing & Launch

Now that the full pipeline and dashboard exist, give Forge its phone controls (run everything from Telegram), put scanning on autopilot, prove every function works, and reach the dashboard securely from anywhere.

Prompt 25| Telegram Command Brain

What this does: A Telegram bot that Forge controls — it receives commands and dispatches them to the right teammate.

Build the Telegram command BRAIN (part 1 of 2 — the next prompt registers the slash commands). Only one app may poll a bot = COPY the token, and that's the Hermes gateway, so we make Forge a thin relay to this deterministic brain.

Builds the deterministic command brain that powers Telegram (/find, /jobs, /promote, /status, /help) plus the natural-language relay so plain English works too. The NEXT prompt registers these as real Telegram slash commands. (Split into two prompts because Telegram chops a very long paste into pieces.)

The bot uses python-telegram-bot. Forge runs as a persistent Hermes profile that reads Telegram messages, interprets them, delegates to the team, and replies in chat. Get the bot token from BotFather.
Prompt 26| Register Slash Commands

What this does: Registers BotFather slash commands for the Telegram bot and wires them to the Forge pipeline.

Register the Telegram slash commands (part 2 of 2 — this makes the command brain from the previous prompt usable in chat).

1. Make each job command a real slash command by registering it as a Hermes SKILL. The Hermes gateway intercepts every "/word" — built-ins run, and ANY unregistered slash is rejected with "Unknown command /x" before Forge ever sees it. A skill is the supported way to attach a new /command. Create one tiny skill per command at ~/.hermes/skills//SKILL.md — names: find, jobs, promote, stats — each with frontmatter (name + description) and a body that says: "Run EXACTLY this one shell command and reply with its stdout verbatim — HERMES_HOME=/root/.hermes  /root/job-hunting-system/pipeline/forge_cmd.py "/ " — do nothing else."

2. ALSO register the per-agent direct-address slashes from the Router prompt — /scout, /reader, /adapter (and /forge). Each MUST land in Hermes' REAL gateway slash-command registry. The gateway dispatcher must ROUTE each per-agent slash to the matching PROFILE: /scout → scout profile, /reader → job-reader, /adapter → cv-adapter; /forge (and bare, no-slash messages) → Forge.
Prompt 27| Lock It Down

What this does: Security — only the owner can run commands, only the owner's Telegram ID is authorised.

Lock down security:
— Telegram owner ID whitelist in config.yaml — only the owner's Telegram ID can run commands
— Dashboard enabled only for owner's Telegram ID
— All /api endpoints behind owner-only auth (simple token in header)
— Owner set in ~/.hermes/forge/config.yaml as owner_id and auth_token
— Unauthorized requests return 401 with no data leakage
Prompt 28| Scheduled Scans & Alerts

What this does: Cron-based automatic scanning so Forge runs without you — new matches are pushed to Telegram.

Set up fully automated scheduled scanning:
— Cron job running: hermes run forge -p "run a full scan now" every 6 hours
— New matches are identified via job-memory.db (jobs with status "new")
— New matches are sent to the owner via Telegram DM
— Every scan is logged in the activity log
— Scan results appear in the dashboard automatically

The cron schedule: 0 */6 * * * (every 6 hours). Set up with crontab -e or a systemd timer.

Also configure an optional daily summary at 09:00 that sends a Telegram digest of the last 24 hours' activity: jobs found, matches reviewed, any pending promotes.