Javis OS
Javis OS is an Agentic AI that assists entrepreneurs in business tasks, helping them grow their work.
AI translation
Jobs scored against your profile before you apply, and applicants ranked for employers.
BusinessTypeScript · CSSGitHub @grantc-archOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A two-sided job board that scores every candidate against every job, and shows both sides exactly where the score comes from.
Each job is scored on five criteria. The employer splits 100% between them, with a 1%-step slider or by typing a number; changing one shares the difference between the others, so the total always stays at 100%:
| Criterion | How it's scored |
|---|---|
| Qualification | 60% for meeting the minimum level (partial credit below it), 40% for a field of study matching the job's keywords |
| Experience | Years of relevant experience against the minimum, capped at full marks |
| Skills | Share of the job's listed skills the candidate has. Skills are matched by meaning, not wording (see below) |
| Location | Straight-line distance: within 25 km 100%, within 60 km (commutable) 75%, further away 50% if willing to relocate, otherwise 0%. Remote roles always 100%. Places without coordinates fall back to comparing names |
| Availability | Can start by the start date 100%, within 2 weeks 70%, within a month 40%, later 10% |
Must-haves are yes/no questions the candidate answers when applying. A "no" is flagged and sorts the applicant below everyone who meets all must-haves, but they stay visible.
The score is drawn as one bar split into five coloured lanes. Each lane's width is the employer's weight; how much of it is filled is how well the candidate meets that criterion, so the gaps show exactly where points were lost.
Skills are picked as tags. As you type, the picker suggests standard names from a list of about 80 common skills (src/lib/skills.ts), and anything typed is mapped to its standard name, so "MS Excel", "excel" and "spreadsheets" all become Microsoft Excel, and "calendar management" becomes Diary management. Suites count for their programs: a candidate with "MS Office" has Excel, Word, Outlook, PowerPoint and Teams. Skills that aren't on the list still work: they're kept as typed and matched loosely. CV auto-fill uses the same names.
The engine is a pure function in src/lib/matching.ts with unit tests in src/lib/matching.test.ts.
| Job page with match breakdown | Posting a job and setting weights |
|---|---|
Each open job page carries JobPosting structured data, and open jobs are listed in /sitemap.xml, so Google can show them in its job search panel. The markup is built in src/lib/job-posting.ts.
LIST_JOBS_ON_GOOGLE=true, because Google doesn't allow fake or test postings, and anyone can sign up and post a job on a public demo.noindex.noindex once it's closed or its closing date passes (in South African time). Visitors following an old link see a "This job has closed" page with similar open jobs, rather than an error. Closing dates set validThrough.TELECOMMUTE with applicants limited to South Africa, and the description says so, as Google requires. Hybrid jobs aren't marked remote.Employers see whether each job is listed, and get a copy-link button and LinkedIn, WhatsApp and email share links. Shared links show a preview image with the job title, company, location and salary:
On the profile page, a candidate can upload their CV and press Fill in from my CV. The PDF is sent to Claude (claude-haiku-5-5 by default) using structured outputs, so the reply always matches a fixed schema: name, phone, city, highest completed qualification, field of study, years of experience, skills, a draft summary, and whether they mention their own transport.
ANTHROPIC_API_KEY is set. Get a key from the Claude Console. Reading a one or two page CV with Haiku costs a fraction of a cent; see Anthropic's pricing for current rates.The schema and clean-up live in src/lib/cv-extraction.ts, the API call in src/lib/cv-autofill.ts, and the merge rules in src/lib/autofill-merge.ts.
Location fields (employer sign-up, company details, candidate profile, job form) suggest South African suburbs and towns as you type, using Photon, komoot's free geocoder for OpenStreetMap data. No API key or billing is needed.
/api/places route, so visitors' typing isn't sent straight to a third party, and answers are cached for a day.PLACES_API_URL.Forgot your password? on the log-in page emails a reset link.
npm run dev prints the link in the terminal, and the live site says password reset isn't available..env.local, and in Vercel):
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
[email protected]
SMTP_PASS=the app password, without spaces
Gmail allows about 500 emails a day and sends from your own address. Once the site has its own domain, a provider such as Resend or Brevo is better: use its SMTP settings and set EMAIL_FROM, for example Shortlist <[email protected]>.
These were picked by looking at what LinkedIn, Indeed, PNet, Welcome to the Jungle (Otta), Workable and Greenhouse offer, and keeping what fits a small South African job board.
For candidates
| Feature | What it does |
|---|---|
| Saved jobs | A Save button on every job. Saved jobs are listed on the dashboard with their match and closing date. Applying removes a job from the list. |
| Filters and sorting | Workplace, job type, minimum salary and date posted, sorted by best match, newest, highest salary or closing soonest. Jobs closing within a week say so. |
| Similar jobs | Each job page suggests up to three open jobs sharing skills, title words or field (src/lib/similar.ts). |
| Company pages | /companies/<id> shows the company's details and every job it has open. |
| Company logos | Employers upload a logo under Company details. The browser shrinks it to 256px (WebP) before upload; the server only accepts real PNG, JPEG or WebP files. It shows on job lists, job pages, the company page and in Google job data. If a company has a website and no logo, saving its details fetches the site's icon through Google's favicon service (only Google is contacted; icons under 64px are skipped), and there's a "Use my website's icon" button. Companies without a logo get a tile with their initials. Served from /logos/<id>. |
| Benefits | Employers tick benefits (medical aid, provident fund, 13th cheque…). They show on the job and in the Google markup. |
| Job alerts | A daily or weekly email of new jobs scoring at least the match the candidate chooses. Nothing is sent when there's nothing new. |
| Application receipts | An email as soon as someone applies, saying what happens next |
| Application updates | One email a day listing any status changes (shortlisted, interview, hired, not progressing). A status changed by mistake and changed back that day is never emailed. |
| Talent pool | Candidates can tick "open to opportunities". Employers then see an anonymous version of their profile (qualification, experience, skills, city, availability, but no name, contact details or CV) scored against each job, and can invite them to apply. Invitations are emailed straight away, up to 25 a day per employer. |
Messages
Everyone has a username (made from their name at sign-up, changeable in Settings). People message each other by username at /messages, and a job can be sent inside a message from its Share button, where "Send in a message" is the first option, followed by email, copy link, WhatsApp, Facebook, LinkedIn, X and Telegram. Messages stay inside Shortlist (no emails); the menu shows an unread count and conversations refresh every few seconds while open. Email addresses are never shown in messages. Anyone can block someone from their conversation, and sending is capped (60 messages an hour, 30 new conversations a day).
For employers
| Feature | What it does |
|---|---|
| Pipeline board | Applicants in one column per stage. Drag a card, or use its menu, which also works by keyboard and on phones. |
| Ratings | A private 1 to 5 star rating on each applicant, shown in the list and on the board, with a sort by rating. |
| Listing stats | Views, applications, the share of viewers who applied, average match and how many meet every must-have. A view is counted once per browser tab; the employer's own visits and search engine crawlers don't count. |
| CSV export | Every applicant in ranked order with contact details, scores, must-have answers, status, rating and notes. Text that could run as a spreadsheet formula is neutralised. |
| New applicant email | One email a day per employer, with new applicants per job and the best match. |
Trust and safety
ADMIN_EMAILS) review reports at /admin: restore the job, or remove it for good. Removed jobs can be restored later.The once-a-day emails (application updates, new applicants, job alerts) are sent by /api/cron/daily, which Vercel Cron calls every morning, as set in vercel.json. On the free Hobby plan it runs some time between 08:00 and 09:00 South African time.
CRON_SECRET is set. Vercel sends that value with each call, so nobody else can trigger it./settings.example.com addresses, which can't receive mail, so nothing is sent to them.CRON_SECRET, visit http://localhost:3000/api/cron/daily in npm run dev to run it by hand. The emails are printed in the terminal if SMTP isn't set up.To post several jobs at once, for example on behalf of an employer who has sent their ads, put them in a JSON file (see imports/old-school.json for the format) and run:
npm run jobs:import -- imports/old-school.json --employer [email protected] --dry-run # check only
npm run jobs:import -- imports/old-school.json --employer [email protected]
DATABASE_URL first, as for db:migrate.jose)@anthropic-ai/sdk) for CV auto-fillYou need Node.js 20 or newer.
npm install
npm run setup # creates the local database in .data/ and adds demo data
npm run dev # http://localhost:3000
Demo logins (password demo-password for both):
[email protected]: Lion's Head Marketing, hiring the Administrator role[email protected]: a marketing graduate who hasn't applied for the Administrator role yetTo try CV auto-fill locally, create a file called .env.local in the project folder containing ANTHROPIC_API_KEY=your-key, then restart npm run dev. All the optional settings are listed in .env.example.
Other commands:
npm test # unit tests
npm run db:seed -- --reset # wipe all data and reload the demo data
npm run db:reset # delete the local database entirely and set it up again
Put the code on GitHub. Create an empty repository, then:
git remote add origin https://github.com/<your-username>/shortlist.git
git push -u origin main
Create a Postgres database with Neon or Supabase and copy its connection string.
Create the tables and demo data from your computer, pointing at that database:
DATABASE_URL="postgres://..." npm run setup
(On Windows PowerShell: $env:DATABASE_URL="postgres://..."; npm run setup)
Import the repository on Vercel and add two environment variables:
DATABASE_URL: the connection string from step 2AUTH_SECRET: a long random string. Generate one with
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"Deploy. Every push to main redeploys.
Optional: CV auto-fill. Add ANTHROPIC_API_KEY in Vercel and redeploy.
Optional: email. Add the four SMTP_ variables (see Forgot password) for password resets and invitations. For the daily emails, also add CRON_SECRET (any long random string, generated the same way as AUTH_SECRET). Redeploy.
To review reported jobs, add ADMIN_EMAILS with your own email address (comma-separated for more than one), sign up on the site with that address, and a Reports link appears in the menu.
The privacy policy (/privacy) and terms (/terms) give CONTACT_EMAIL as the address to write to, or, if that isn't set, the first ADMIN_EMAILS address, then SMTP_USER.
Optional: Google for Jobs, once real employers are posting:
When you change src/db/schema.ts, run npm run db:generate to create a migration, then npm run db:migrate locally and against production. Whenever you pull changes that add a file to drizzle/, run DATABASE_URL="postgres://..." npm run db:migrate once against your hosted database before (or straight after) the new code deploys, otherwise the live site will error.
src/
app/
actions/ server actions: auth, candidate, employer, settings, reports, admin
jobs/ job board, job page (with Google markup and share image), apply and report flows
companies/ public company pages
candidate/ candidate dashboard and profile (with CV auto-fill)
employer/ employer dashboard, post/edit job, ranked applicants, board, talent pool, CSV export
settings/ email and talent pool settings
admin/ reported job review
api/ place search, view counting, daily emails, one-click unsubscribe
cv/[candidateId]/ access-controlled CV download
sitemap.ts open jobs and companies for search engines
robots.ts keeps dashboards and CVs out of search
components/ fit bar, job list, pipeline board, ratings, form helpers
db/ schema and connection (PGlite locally, Postgres in production)
lib/ matching engine, auth, queries, ranking, Google markup, CV auto-fill,
similar jobs, emails and the daily email job
assets/fonts/ Schibsted Grotesk (SIL Open Font License) for share images
scripts/ migrate and seed
drizzle/ SQL migrations
LIST_JOBS_ON_GOOGLEtruecontent value into a GOOGLE_SITE_VERIFICATION variable, redeploy, then press Verify.sitemap.xml.