{"id":47908510,"url":"https://github.com/sir-ad/knight","last_synced_at":"2026-04-15T05:02:00.819Z","repository":{"id":343060667,"uuid":"1175934129","full_name":"sir-ad/knight","owner":"sir-ad","description":"Privacy-first Chrome extension for local resume parsing, ATS autofill, Gmail tracking, and Ollama follow-ups","archived":false,"fork":false,"pushed_at":"2026-03-22T18:06:49.000Z","size":748,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T05:30:58.593Z","etag":null,"topics":["ats-autofill","chrome-extension","gmail","job-search","ollama","plasmo","typescript"],"latest_commit_sha":null,"homepage":"https://sir-ad.github.io/knight/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sir-ad.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-08T11:40:48.000Z","updated_at":"2026-03-22T18:06:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sir-ad/knight","commit_stats":null,"previous_names":["sir-ad/knight"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/sir-ad/knight","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sir-ad%2Fknight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sir-ad%2Fknight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sir-ad%2Fknight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sir-ad%2Fknight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sir-ad","download_url":"https://codeload.github.com/sir-ad/knight/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sir-ad%2Fknight/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31826902,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T18:05:02.291Z","status":"online","status_checked_at":"2026-04-15T02:00:06.175Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ats-autofill","chrome-extension","gmail","job-search","ollama","plasmo","typescript"],"created_at":"2026-04-04T05:01:42.903Z","updated_at":"2026-04-15T05:02:00.812Z","avatar_url":"https://github.com/sir-ad.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Knight\n\n[![CI](https://github.com/sir-ad/knight/actions/workflows/ci.yml/badge.svg)](https://github.com/sir-ad/knight/actions/workflows/ci.yml)\n[![Release](https://github.com/sir-ad/knight/actions/workflows/release.yml/badge.svg)](https://github.com/sir-ad/knight/actions/workflows/release.yml)\n[![GitHub release](https://img.shields.io/github/v/release/sir-ad/knight?color=0f172a)](https://github.com/sir-ad/knight/releases/latest)\n[![License: MIT](https://img.shields.io/badge/license-MIT-0f172a.svg)](./LICENSE)\n[![Chrome MV3](https://img.shields.io/badge/Chrome-MV3-4285F4?logo=google-chrome\u0026logoColor=white)](https://developer.chrome.com/docs/extensions/mv3/)\n[![Node 20+](https://img.shields.io/badge/Node-20+-339933?logo=node.js\u0026logoColor=white)](https://nodejs.org/)\n[![Python 3.11+](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python\u0026logoColor=white)](https://www.python.org/)\n\n\u003cbr/\u003e\n\n![Knight](./assets/banner.svg)\n\n\u003cbr/\u003e\n\n### The job application layer you've been missing.\n\nEvery application is the same form. Different URL, same twenty fields — name, email, phone, work history, education — pasted by hand into Workday, Greenhouse, Lever, and a dozen other portals that share nothing between them. Most autofill tools break on anything beyond a basic text input, or quietly ship your resume to a hosted AI pipeline to do the parsing.\n\n**Knight fixes this without the tradeoffs.** Your resume is parsed entirely on your own machine by a local Python service. Field mapping runs through whichever LLM you choose — local Ollama or a cloud provider you configure. Before a single character is written to any form, you see a diff of every proposed change and confirm it. Your data stays on your device.\n\n\u003cbr/\u003e\n\n🌐 **[sir-ad.github.io/knight](https://sir-ad.github.io/knight/)**\n\n\u003c/div\u003e\n\n---\n\n## Features\n\n| Capability | Detail |\n|---|---|\n| **Resume parsing** | PDF (digital + scanned OCR), DOCX, TXT, or pasted text |\n| **Local-first** | PyMuPDF + optional Tesseract OCR in a local Python sidecar |\n| **Autofill** | 14 named ATS adapters + generic DOM fallback |\n| **Review overlay** | Inline diff of what will be filled — confirm before anything is written |\n| **Application log** | Local status history with auto-detection from Gmail |\n| **Gmail sync** | Read-only OAuth — spots confirmations, interviews, rejections, offers |\n| **Follow-up drafts** | Generates follow-up emails from your application context |\n| **LLM providers** | Ollama · OpenAI · Anthropic · Gemini · OpenRouter |\n\n---\n\n## Architecture\n\n```\n┌──────────────────────────────────────────┐\n│           Chrome Extension (MV3)         │\n│                                          │\n│  ┌──────────┐  ┌─────────┐  ┌────────┐  │\n│  │ Service  │  │ Content │  │ Popup  │  │\n│  │ Worker   │◄─│ Script  │  │  UI   │  │\n│  │ (bus +   │  │(inject/ │  │(React) │  │\n│  │ LLM hub) │  │ fill)   │  │        │  │\n│  └────┬─────┘  └─────────┘  └────────┘  │\n└───────┼──────────────────────────────────┘\n        │ HTTP (localhost only)\n        ▼\n┌───────────────────────┐\n│  Resume Parser Sidecar│  http://127.0.0.1:43118\n│  FastAPI · Python     │\n│  PyMuPDF · Tesseract  │\n│  LangExtract          │\n└───────────────────────┘\n```\n\nAll LLM calls from the extension route through the **service worker** — content scripts never call APIs directly. The **sidecar** handles heavy file parsing outside the browser sandbox.\n\n---\n\n## Supported ATS Portals\n\n| Portal | Match pattern |\n|---|---|\n| Workday | `*.myworkdayjobs.com`, `*.workday.com` |\n| Greenhouse | `boards.greenhouse.io`, `*.greenhouse.io` |\n| Lever | `jobs.lever.co`, `*.lever.co` |\n| Naukri | `*.naukri.com` |\n| iCIMS | `*.icims.com` |\n| SmartRecruiters | `*.smartrecruiters.com` |\n| Oracle Taleo | `*.taleo.net` |\n| SAP SuccessFactors | `*.successfactors.com`, `*.successfactors.eu` |\n| LinkedIn Easy Apply | `*.linkedin.com/jobs/*` |\n| Indeed | `*.indeed.com/apply/*` |\n| Wellfound / AngelList | `*.wellfound.com/jobs/*`, `*.angel.co/jobs/*` |\n| Ashby | `*.ashbyhq.com` |\n| Rippling | `*.rippling.com/jobs/*` |\n| Generic fallback | Any form with standard labels, placeholders, or `aria-label` metadata |\n\n---\n\n## Quick Start\n\n### One-line install\n\n```bash\nbash \u003c(curl -fsSL https://raw.githubusercontent.com/sir-ad/knight/main/install.sh)\n```\n\nClones the repo, builds the extension, and sets up the Python sidecar in one shot. Then follow the printed instructions to load the unpacked extension in Chrome.\n\nSet `KNIGHT_DIR` to change the install location (default: `~/knight`).\n\n---\n\n### Manual setup\n\n### 1. Start the resume parser sidecar\n\n```bash\ngit clone https://github.com/sir-ad/knight.git\ncd knight/resume-parser-sidecar\n./scripts/setup-venv.sh   # one-time: creates .venv and installs deps\n./scripts/run.sh          # starts on http://127.0.0.1:43118\n```\n\nFor scanned PDF support, install Tesseract:\n\n```bash\n# macOS\nbrew install tesseract\n\n# Ubuntu / Debian\nsudo apt-get install -y tesseract-ocr\n```\n\n### 2. Build the extension\n\n```bash\ncd ../careerflow\nnpm install\nnpm run build\n```\n\n### 3. Load in Chrome\n\n1. Open `chrome://extensions`\n2. Enable **Developer Mode**\n3. Click **Load unpacked**\n4. Select `careerflow/build/chrome-mv3-prod`\n\n### 4. (Optional) Start Ollama for local LLM\n\n```bash\nOLLAMA_ORIGINS=chrome-extension://* ollama serve\nollama pull llama3.2:3b\n```\n\n---\n\n## Requirements\n\n| Component | Requirement |\n|---|---|\n| Node.js | 20+ |\n| Python | 3.11+ |\n| Chrome | 115+ (MV3) |\n| Ollama | Any recent version — for the local-first path |\n| Tesseract | Optional — only needed for scanned PDF resumes |\n\n---\n\n## LLM Provider Setup\n\nKnight uses the provider you configure in **Settings**. Ollama is the default and requires no API key.\n\n| Provider | Key required | Notes |\n|---|---|---|\n| Ollama | No | Default. Runs 100% locally. |\n| OpenAI | Yes | Supports GPT-4o, GPT-4.1 series |\n| Anthropic | Yes | Supports claude-haiku-3-5, claude-sonnet-4-5 |\n| Google Gemini | Yes | Supports gemini-2.0-flash |\n| OpenRouter | Yes | Routes to any supported model |\n\nStore keys in Settings → the extension keeps them in local Chrome storage and never includes them in exports.\n\n\u003e **Ollama endpoint:** enter the host root only — `http://localhost:11434`. Do **not** append `/api`.\n\n---\n\n## Gmail Sync\n\nKnight requests a **read-only** Gmail OAuth scope (`gmail.readonly`). It scans for emails from known ATS domains and classifies them as confirmation, interview invite, rejection, or offer — updating your local application log automatically.\n\nTo enable Gmail sync, set your OAuth client ID before building:\n\n```bash\n# careerflow/.env.local\nPLASMO_PUBLIC_GOOGLE_CLIENT_ID=your-google-oauth-client-id\n```\n\n---\n\n## Development\n\n### Extension\n\n```bash\ncd careerflow\nnpm run dev          # Plasmo dev server with hot reload\nnpm run typecheck    # TypeScript check\nnpm test             # Jest tests\nnpm test -- --testPathPattern=\u003cfile\u003e  # Single file\nnpm test -- --coverage\n```\n\n### Sidecar\n\n```bash\ncd resume-parser-sidecar\nsource .venv/bin/activate\npython -m uvicorn resume_parser_sidecar.app:app --host 127.0.0.1 --port 43118 --reload\n\npytest tests -q\npytest tests/test_service.py  # Single file\n```\n\n### Release gate (full validation)\n\n```bash\n# Extension\ncd careerflow\nnpx tsc --noEmit \u0026\u0026 npm test -- --runInBand \u0026\u0026 npm run build \u0026\u0026 npm run package\n\n# Sidecar\ncd ../resume-parser-sidecar\nsource .venv/bin/activate \u0026\u0026 pytest tests -q\n```\n\n---\n\n## Repo Layout\n\n```\nKnight/\n├── careerflow/                  # Chrome extension (TypeScript · React · Plasmo)\n│   ├── src/background/          # Service worker — message bus, LLM calls, Gmail sync\n│   ├── src/content/             # Content scripts — ATS detection, autofill, overlay\n│   ├── src/lib/                 # Shared utilities — types, LLM providers, storage\n│   └── src/popup/               # React popup — Profile / Dashboard / Settings tabs\n├── resume-parser-sidecar/       # Local FastAPI service (Python 3.11+)\n│   └── resume_parser_sidecar/   # app, extraction, langextract runner, providers\n├── docs/                        # GitHub Pages site\n└── .github/workflows/           # CI, release packaging, Pages deploy\n```\n\n---\n\n## CI / CD\n\n| Workflow | Trigger | What it does |\n|---|---|---|\n| `ci.yml` | Push / PR to `main` | typecheck → tests → build → package (extension); pytest (sidecar) |\n| `release.yml` | Version tags (`v*`) | Builds and attaches the extension zip to GitHub Releases |\n| `pages.yml` | Push to `main` | Deploys `docs/` to GitHub Pages |\n\n---\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsir-ad%2Fknight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsir-ad%2Fknight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsir-ad%2Fknight/lists"}